Запобігайте onmouseout при наведенні дочірнього елемента батьківського абсолютного діла БЕЗ jQuery


169

У мене виникають проблеми з onmouseoutфункцією в абсолютно розміщеному діві. Коли миша потрапляє на дочірній елемент у діві, подія mouseout запускається, але я не хочу, щоб воно спрацьовувало, поки миша не вийде з батьківського, абсолютного діва.

Як я можу запобігти появі mouseoutподії, коли вона потрапляє на дочірній елемент БЕЗ jquery.

Я знаю, що це має щось спільне з бурчанням подій, але мені не пощастило дізнатися, як це зробити.

Тут я знайшов подібний пост: Як відключити події миші, викликані дочірніми елементами?

Однак це рішення використовує jQuery.


Я вирішив проблему, використовуючи тайм-аут і очистивши її після наведення дочірніх елементів
Іван

Привіт, я переглянув вашу демонстрацію, але коли я перебираю елементи в нижній правій панелі, нічого не відбувається?
Іван

1
Знайшов цю відповідь, якщо ви хочете запобігти виходу з миші на батьківську подію, коли ви переходите мишею на дочірній елемент: javascript mouseover / mouseout .
користувач823527

1
Перевірте відповідь @Zach Saucier похований нижче
craigmichaelmartin

Відповіді:


94
function onMouseOut(event) {
        //this is the original element the event handler was assigned to
        var e = event.toElement || event.relatedTarget;
        if (e.parentNode == this || e == this) {
           return;
        }
    alert('MouseOut');
    // handle mouse event here!
}



document.getElementById('parent').addEventListener('mouseout',onMouseOut,true);

Я зробив швидку демонстрацію JsFiddle, з усіма необхідними CSS та HTML, перевірте це ...

EDIT FIXED посилання для крос-браузерна http://jsfiddle.net/RH3tA/9/

Зверніть увагу, що це перевіряє лише безпосереднього батька, якщо батьківський дів вклав дітей, то вам доведеться якось пройти елементи батьків, які шукають "Оригінальний елемент"

Приклад EDIT для вкладених дітей

EDIT Виправлено для перехресного веб-переглядача

function makeMouseOutFn(elem){
    var list = traverseChildren(elem);
    return function onMouseOut(event) {
        var e = event.toElement || event.relatedTarget;
        if (!!~list.indexOf(e)) {
            return;
        }
        alert('MouseOut');
        // handle mouse event here!
    };
}

//using closure to cache all child elements
var parent = document.getElementById("parent");
parent.addEventListener('mouseout',makeMouseOutFn(parent),true);

//quick and dirty DFS children traversal, 
function traverseChildren(elem){
    var children = [];
    var q = [];
    q.push(elem);
    while (q.length > 0) {
      var elem = q.pop();
      children.push(elem);
      pushAll(elem.children);
    }
    function pushAll(elemArray){
      for(var i=0; i < elemArray.length; i++) {
        q.push(elemArray[i]);
      }
    }
    return children;
}

І нове оновлене посилання JSFiddle , EDIT


1
Ви повинні додати подію до виходу миші до скрипки, щоб вона могла бути перевірена. Попередження чи щось має робити.
Іван

Вибачте, схоже, що у вас є попередження ("MouseOut"), однак це не працює для мене? Я потрапив у біг без жодної можливості бібліотеки JS
Джон

Тестували на сафарі та хромі, але слід працювати на все! який ваш браузер?
Амджад Масад

2
Що робити, якщо у вас є великі діти? чи великих діток? тобто ви можете мати рекурсивне рішення замість того, щоб просто не змусити дітей?
Roozbeh15

20
Це не працює. mouseleaveправильна відповідь
Шепіт коду

126

Використовуйте onmouseleave.

Або в jQuery використовуйте mouseleave()

Це саме те, що ви шукаєте. Приклад:

<div class="outer" onmouseleave="yourFunction()">
    <div class="inner">
    </div>
</div>

або, у jQuery:

$(".outer").mouseleave(function(){
    //your code here
});

приклад тут .


3
onMouseLeaveце те, що я в кінцевому підсумку використовував, оскільки він не міхур.
Алеч

Економив мені багато часу. thnx
npo

mouseleaveне можна скасувати.
grecdev

@grecdev, скажи більше!
Бен Вілер

98

Для більш простого рішення CSS, яке працює в деяких випадках ( IE11 або новіших версій ), можна видалити дітей, pointer-eventsвстановивши їх наnone

.parent * {
     pointer-events: none;
}

4
Блискуче! Повністю виправлена ​​моя проблема!
Anna_MediaGirl

4
Це має бути перша чи принаймні друга відповідь, я був готовий піти з клопотаннями слухачів подій, і це повністю виправило мою проблему теж. Так просто !
Мікаель В.

9
Це запобігає запуску миші, але для мене це також запобігає натисканню фактичної дитини як виділення в меню, яке є пунктом меню.
johnbakers

@hellofunk Ось чому ви застосуєте подія клацання до батьків. Точний необхідний код змінюється залежно від реалізації, ця відповідь просто пропонує використовувати pointer-eventsмайно
Zach Saucier

1
він працює лише в тому випадку, якщо у вас немає інших елементів контролера (редагування, видалення) всередині області, оскільки це рішення також блокує їх ..
Zoltán Süle

28

Ось більш елегантне рішення на основі того, що було подано нижче. він пояснює події, що киплять від більш ніж одного рівня дітей. Він також враховує проблеми між браузером.

function onMouseOut(this, event) {
//this is the original element the event handler was assigned to
   var e = event.toElement || event.relatedTarget;

//check for all children levels (checking from bottom up)
while(e && e.parentNode && e.parentNode != window) {
    if (e.parentNode == this||  e == this) {
        if(e.preventDefault) e.preventDefault();
        return false;
    }
    e = e.parentNode;
}

//Do something u need here
}

document.getElementById('parent').addEventListener('mouseout',onMouseOut,true);

Сем Елі, схоже, це не працює над IE, Safari або Opera. Чи є у вас крос-браузерна версія цього сценарію? Це працює як шарм у Firefox та Chrome. Дякую.

1
Коли я вставляю вищезгадану функцію у свій сценарій, Dreamweaver та FF видають помилку в першому рядку, "функція onMouseOut (це, подія) {". Схоже, вам не дозволяється ставити "це" як параметр. Я думаю, що це працює, коли я залишаю "це" з вхідних парам, але я не знаю точно, тому що "я не знаю, що не знаю".
TARKUS

1
У мене також з’явився той самий випуск у Chrome у тому ж рядку, за винятком thisрядка визначення функції @GregoryLewis, який вирішив проблему. Також для отримання додаткової підтримки між браузерами спробуйте це: if (window.addEventListener) {document.getElementById ('parent'). AddEventListener ('mouseout', onMouseOut, true); } else if (window.attachEvent) {document.getElementById ('parent'). attachEvent ("onmouseout", onMouseOut); }
DWils

Дуже вродливий! Найкраща відповідь, якщо потрібна підтримка старих браузерів.
BurninLeo

13

Завдяки Амджаду Масаду, який мене надихнув.

У мене є таке рішення, яке, здається, працює в IE9, FF та Chrome, і код досить короткий (без складного закриття та поперечних дочірніх речей):

    DIV.onmouseout=function(e){
        // check and loop relatedTarget.parentNode
        // ignore event triggered mouse overing any child element or leaving itself
        var obj=e.relatedTarget;
        while(obj!=null){
            if(obj==this){
                return;
            }
            obj=obj.parentNode;
        }
        // now perform the actual action you want to do only when mouse is leaving the DIV
    }

13

замість onmouseout використовуйте onmouseleave.

Ви не показали нам свій конкретний код, тому я не можу показати вам на вашому конкретному прикладі, як це зробити.

Але це дуже просто: просто замініть onmouseout на onmouseleave.

Це все :) Отже, просто :)

Якщо ви не знаєте, як це зробити, дивіться пояснення на:

https://www.w3schools.com/jsref/tryit.asp?filename=tryjsref_onmousemove_leave_out

Спокій торта :) Насолоджуйтесь :)


Це чудовий крик, і варто перевірити, чи є у вас подібна ситуація
Кріс,

12

Якщо ви використовуєте jQuery, ви також можете скористатися функцією "mouseleave", яка стосується всього цього для вас.

$('#thetargetdiv').mouseenter(do_something);
$('#thetargetdiv').mouseleave(do_something_else);

do_something запуститься, коли миша потрапить у thetargetdiv або будь-який з його дітей, do_something_else запуститься лише тоді, коли миша покине thetargetdiv та когось із своїх дітей.


8

Я думаю, що у Quirksmode є всі відповіді, які вам потрібні (різні поведінки у броузерах браузера та події mouseenter / mouseleave ), але я вважаю, що найпоширенішим висновком цього безладу подій є використання такої структури, як JQuery або Mootools (у якій є мишецентр і події миші , які саме те, що ви інтуїтивно мали б статися).

Погляньте, як вони це роблять, якщо хочете, зробіть це самостійно,
або ви можете створити власну "Moantools" версію Mootools з лише частиною події (та її залежностями).



5

Я знайшов дуже просте рішення,

просто використовуйте подію onmouseleave = "myfunc ()", ніж подія onmousout = "myfunc ()"

У моєму коді це працювало !!

Приклад:

<html>
<head>
<script type="text/javascript">
   function myFunc(){
      document.getElementById('hide_div').style.display = 'none';
   }
   function ShowFunc(){
      document.getElementById('hide_div').style.display = 'block';
   }
</script>
</head>
<body>
<div onmouseleave="myFunc()" style='border:double;width:50%;height:50%;position:absolute;top:25%;left:25%;'>
   Hover mouse here
   <div id='child_div' style='border:solid;width:25%;height:25%;position:absolute;top:10%;left:10%;'>
      CHILD <br/> It doesn't fires if you hover mouse over this child_div
   </div>
</div>
<div id="hide_div" >TEXT</div>
<a href='#' onclick="ShowFunc()">Show "TEXT"</a>
</body>
</html>

Той же приклад з функцією миші:

<html>
<head>
<script type="text/javascript">
   function myFunc(){
      document.getElementById('hide_div').style.display = 'none';
   }
   function ShowFunc(){
      document.getElementById('hide_div').style.display = 'block';
   }
</script>
</head>
<body>
<div onmouseout="myFunc()" style='border:double;width:50%;height:50%;position:absolute;top:25%;left:25%;'>
   Hover mouse here
   <div id='child_div' style='border:solid;width:25%;height:25%;position:absolute;top:10%;left:10%;'>
      CHILD <br/> It fires if you hover mouse over this child_div
   </div>
</div>
<div id="hide_div">TEXT</div>
<a href='#' onclick="ShowFunc()">Show "TEXT"</a>
</body>
</html>

Сподіваюся, це допомагає :)



2

Є два способи впоратися з цим.

1) Перевірте результат event.target у зворотному дзвінку, щоб побачити, чи відповідає він вашому батьківському div

var g_ParentDiv;

function OnMouseOut(event) {
    if (event.target != g_ParentDiv) {
        return;
    }
    // handle mouse event here!
};


window.onload = function() {
    g_ParentDiv = document.getElementById("parentdiv");
    g_ParentDiv.onmouseout = OnMouseOut;
};

<div id="parentdiv">
    <img src="childimage.jpg" id="childimg" />
</div>

2) Або використовуйте захоплення подій та виклик event.stopPropagation у функції зворотного дзвінка

var g_ParentDiv;

function OnMouseOut(event) {

    event.stopPropagation(); // don't let the event recurse into children

    // handle mouse event here!
};


window.onload = function() {
    g_ParentDiv = document.getElementById("parentdiv");
    g_ParentDiv.addEventListener("mouseout", OnMouseOut, true); // pass true to enable event capturing so parent gets event callback before children
};

<div id="parentdiv">
    <img src="childimage.jpg" id="childimg" />
</div>

Як я можу зробити цю роботу для onmouseout замість onmouseover?
Іван

На жаль Моє ліжко. Я просто змінюю всі посилання на "mouseover" на "mouseout" у наведеному вище коді. Це має зробити це за вас.
selbie

Для першого методу event.target завжди відповідає батьківському елементу, коли миша переходить над дочірнім елементом, тому він не працює
Іван

Те ж саме відбувається і для другого методу. Коли миша вводить дочірній елемент, вона все ще запускає подію
Іоанн

1

Я змушую це працювати як шарм із цим:

function HideLayer(theEvent){
 var MyDiv=document.getElementById('MyDiv');
 if(MyDiv==(!theEvent?window.event:theEvent.target)){
  MyDiv.style.display='none';
 }
}

А, і MyDivтег такий:

<div id="MyDiv" onmouseout="JavaScript: HideLayer(event);">
 <!-- Here whatever divs, inputs, links, images, anything you want... -->
<div>

Таким чином, коли onmouseout переходить до дитини, онука тощо, style.display='none'це не виконується; але коли onmouseout виходить з MyDiv, він працює.

Тому не потрібно зупиняти розповсюдження, використовувати таймери тощо ...

Дякую за приклади, я міг би зробити з них цей код.

Сподіваюся, що це комусь допоможе.

Також можна покращити так:

function HideLayer(theLayer,theEvent){
 if(theLayer==(!theEvent?window.event:theEvent.target)){
  theLayer.style.display='none';
 }
}

І тоді теги DIVs, як це:

<div onmouseout="JavaScript: HideLayer(this,event);">
 <!-- Here whatever divs, inputs, links, images, anything you want... -->
<div>

Отже, більш загальне, не тільки для одного діва та не потрібно додавати id="..."на кожен шар.


1

Якщо у вас є доступ до елемента, до якого подія додається всередині mouseoutметоду, ви можете використовувати, contains()щоб побачити, event.relatedTargetє дочірнім елементом чи ні.

Як event.relatedTargetі елемент, до якого перейшла миша, якщо це не дочірній елемент, ви вийшли з елемента.

div.onmouseout = function (event) {
    if (!div.contains(event.relatedTarget)) {
        // moused out of div
    }
}

1

На кутових 5, 6 і 7

<div (mouseout)="onMouseOut($event)"
     (mouseenter)="onMouseEnter($event)"></div>

Потім далі

import {Component,Renderer2} from '@angular/core';
...
@Component({
 selector: 'app-test',
 templateUrl: './test.component.html',
 styleUrls: ['./test.component.scss']
})
export class TestComponent implements OnInit, OnDestroy {
...
 public targetElement: HTMLElement;

 constructor(private _renderer: Renderer2) {
 }

 ngOnInit(): void {
 }

 ngOnDestroy(): void {
  //Maybe reset the targetElement
 }

 public onMouseEnter(event): void {
  this.targetElement = event.target || event.srcElement;
  console.log('Mouse Enter', this.targetElement);
 }

 public onMouseOut(event): void {
  const elementRelated = event.toElement || event.relatedTarget;
  if (this.targetElement.contains(elementRelated)) {
    return;
  }
  console.log('Mouse Out');
 }
}

0

Я перевіряю зсув оригінального елемента, щоб отримати координати сторінки меж елемента, а потім переконайтеся, що дія миші виконується лише тоді, коли миші виходять із цих меж. Брудно, але це працює.

$(el).live('mouseout', function(event){
    while(checkPosition(this, event)){
        console.log("mouseovering including children")
    }
    console.log("moused out of the whole")
})

var checkPosition = function(el, event){
    var position = $(el).offset()
    var height = $(el).height()
    var width = $(el).width()
    if (event.pageY > position.top 
|| event.pageY < (position.top + height) 
|| event.pageX > position.left 
|| event.pageX < (position.left + width)){
    return true
}
}


0

Якщо ви додали (або маєте) клас CSS або ідентифікатор до батьківського елемента, ви можете зробити щось подібне:

<div id="parent">
  <div>
  </div>
</div>

JavaScript:
document.getElementById("parent").onmouseout = function(e) {
  e = e ? e : window.event //For IE
  if(e.target.id == "parent") {
    //Do your stuff
  }
}

Таким чином, речі виконуються лише тоді, коли подія знаходиться на батьківському ділі.


0

Я просто хотів чимсь поділитися з вами.
У мене складно було ng-mouseenterі ng-mouseleaveподії.

Тематичне дослідження:

Я створив плаваюче меню навігації, яке перемикається, коли курсор знаходиться над значком.
Це меню було вгорі кожної сторінки.

  • Для обробки шоу / приховування в меню я перемикаю клас.
    ng-class="{down: vm.isHover}"
  • Щоб переключити vm.isHover , я використовую ng події миші.
    ng-mouseenter="vm.isHover = true"
    ng-mouseleave="vm.isHover = false"

Наразі все було добре і працювали так, як очікувалося.
Розчин чистий і простий.

Вхідна проблема:

У конкретному поданні я маю список елементів.
Я додав панель дій, коли курсор знаходиться над елементом списку.
Я використовував той самий код, що і вище, для обробки поведінки.

Проблема:

Я зрозумів, коли мій курсор знаходиться у плаваючому навігаційному меню, а також у верхній частині елемента, між ними виникає конфлікт.
На панелі дій з'явилася плаваюча навігація.

Вся справа в тому, що навіть якщо курсор знаходиться над плаваючим навігаційним меню, запускається елемент списку ng-mouseenter.
Для мене це не має сенсу, тому що я б очікував автоматичного перерви подій розповсюдження миші.
Треба сказати, що я розчарувався і витрачаю деякий час, щоб з’ясувати цю проблему.

Перші думки:

Я намагався використати такі:

  • $event.stopPropagation()
  • $event.stopImmediatePropagation()

Я поєднав багато потік подій (миші, миші, ...), але жодна мені не допомогла.

CSS-рішення:

Я знайшов рішення з простим властивістю css, яким я все більше і більше користуюся:

pointer-events: none;

В основному, я використовую його так (у елементах мого списку):

ng-style="{'pointer-events': vm.isHover ? 'none' : ''}"

У цьому хитрому випадку події ng-миші більше не будуть спрацьовувати, і моє плаваюче навігаційне меню більше не закриватиметься, коли курсор над ним та над елементом зі списку.

Щоб піти далі:

Як ви можете очікувати, це рішення працює, але мені це не подобається.
Ми не контролюємо наші події, і це погано.
Крім того, ви повинні мати доступ до vm.isHoverсфери, щоб досягти цього, і це може бути не можливо чи можливо, але певним чином чи іншим чином забруднене.
Я міг би зробити загадку, якщо хтось захоче подивитися.

Тим не менш, у мене немає іншого рішення ...
Це довга історія, і я не можу дати тобі картоплю, тому, будь ласка, прости мене.
У будь-якому випадку, pointer-events: noneце життя, тому пам’ятайте про це.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.