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

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

Как я могу предотвратить запуск события mouseout, когда он попадает в дочерний элемент БЕЗ jquery.

Я знаю, что это как-то связано с всплыванием событий, но мне не удалось выяснить, как это решить.

Я нашел похожее сообщение здесь: Как отключить события mouseout, вызванные дочерние элементы?

Однако это решение использует jQuery.


person John    schedule 15.01.2011    source источник
comment
В итоге я решил проблему, используя тайм-аут и очистив его при наведении курсора на дочерние элементы.   -  person John    schedule 16.01.2011
comment
Привет, я посмотрел вашу демонстрацию, но когда я просматриваю элементы в правой нижней панели, ничего не происходит?   -  person John    schedule 16.01.2011
comment
Нашел этот ответ, если вы хотите предотвратить наведение курсора мыши на родительское событие при наведении указателя мыши на дочерний элемент: javascript mouseover / mouseout.   -  person user823527    schedule 11.08.2012
comment
Посмотрите @Zach Saucier ответ закопан внизу   -  person craigmichaelmartin    schedule 28.10.2016


Ответы (22)


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, проверьте это ...

РЕДАКТИРОВАТЬ ФИКСИРОВАННАЯ ссылка для кросс-браузерной поддержки http://jsfiddle.net/RH3tA/9/

ПРИМЕЧАНИЕ, это проверяет только непосредственного родителя, если у родительского div есть вложенные дочерние элементы, вам нужно каким-то образом пройти через родительские элементы в поисках «оригинального элемента».

Пример 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 обновленная ссылка

person Amjad Masad    schedule 15.01.2011
comment
Вы должны добавить событие mouseout к скрипке, чтобы его можно было протестировать. Предупреждение или что-то должно сделать. - person John; 16.01.2011
comment
Извините, похоже, у вас есть предупреждение ('MouseOut'), но оно не работает для меня? Я нажал запустить без опции библиотеки JS - person John; 16.01.2011
comment
Проверено на сафари и хроме, но должно работать на всем! какой у вас браузер? - person Amjad Masad; 17.01.2011
comment
Что делать, если у вас есть внуки? или правнуки? то есть у вас может быть рекурсивное решение, а не просто иммидировать детей? - person Roozbeh15; 28.01.2012
comment
Работает отлично. Но не могли бы вы помочь мне его отсоединить :-)? Я пытался несколько часов, но он продолжает добавлять обработчики событий в мой div. - person Michel; 18.01.2013
comment
Вот отредактированная версия для дочерних детей. jsfiddle.net/RH3tA/622 вы можете добавить некоторые условия для добавления уровни дочернего ребенка (состояние неравенства на уровне дочернего ребенка) - person Acuao; 15.04.2013
comment
Небольшое изменение, чтобы избежать ошибки при наличии более одного каскадного уровня элемента. function OnMouseOut (event) {// это исходный элемент, которому обработчик события был назначен var e = event.toElement || event.relatedTarget; в то время как (е! = ноль) {если (е == это) {возврат; } e = e.parentNode; } alert ('MouseOut'); } - person Slavik; 13.09.2013
comment
Кстати, если бы вы создавали дочерние элементы в Javascript с помощью innerHTML, это не сработает. Они должны быть созданы с помощью метода createElement - и до того, как событие будет добавлено к родительскому элементу. - person Guesser; 25.09.2013
comment
Это не работает. mouseleave - правильный ответ - person Code Whisperer; 09.06.2014
comment
@itcouldevenbeaboat Оба, похоже, работают для меня, хотя mouseleave, похоже, работает только в слушателе событий (поэтому html DOM или просто element.mouseleave = onMouseOut(); не будут работать). - person paddotk; 11.10.2014
comment
Это довольно старый ответ - в настоящее время вы можете использовать contains, что делает его гораздо более элегантным решением: function makeMouseOutFn(event) { e = event.toElement || event.relatedTarget; if (this.contains(e)) return; //handle mouse event here } - person Ian; 30.09.2020

Используйте 1_.

Или в jQuery используйте mouseleave()

Это именно то, что вы ищете. Пример:

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

или в jQuery:

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

пример находится здесь.

person PHPWannaBePro    schedule 07.06.2015
comment
onMouseLeave - это то, что я тоже использовал, так как он не пузырится. - person Alecz; 28.06.2018
comment
Сэкономил мне много времени. спасибо - person npo; 02.04.2019
comment
mouseleave нельзя отменить. - person grecdev; 13.02.2020
comment
@grecdev, скажи еще! - person Ben Wheeler; 14.04.2020

Для более простого решения на чистом CSS, которое работает в большинстве случаев, можно удалить детский _ 1_, установив для них значение none

.parent * {
     pointer-events: none;
}

Поддержка браузера: IE11 +

person Zach Saucier    schedule 16.09.2013
comment
Блестяще! Полностью исправил мою проблему! - person Anna_MediaGirl; 03.04.2014
comment
Это должен быть первый или, по крайней мере, второй ответ, я был готов избавиться от хлопот, связанных с прослушивателями событий, и это также полностью устранило мою проблему. Так просто ! - person Mickael V.; 13.05.2014
comment
Это предотвращает срабатывание mouseout, но для меня это также предотвращает щелчок по фактическому дочернему элементу в качестве выбора в меню, который является точкой меню. - person johnbakers; 17.05.2015
comment
@hellofunk Вот почему вы должны применить событие click к родительскому элементу. Точный необходимый код зависит от реализации, этот ответ просто предлагает использовать свойство pointer-events - person Zach Saucier; 03.11.2015
comment
он работает, только если у вас нет других элементов контроллера (редактировать, удалять) внутри области, потому что это решение также блокирует их .. - person Zoltán Süle; 09.10.2018
comment
Работает как шарм :) - person vijayst; 02.01.2019
comment
У меня нет слов для этого, он отлично работает и такой чистый. Не знал, что CSS позволит управлять событиями - person Jonathan Muller; 15.05.2019
comment
Супер простое решение. Снимаю перед вами шляпу. - person Kzrbill; 18.12.2020

Вот более элегантное решение, основанное на приведенном ниже. это объясняет возникновение событий от более чем одного уровня дочерних элементов. Это также учитывает проблемы кроссбраузерности.

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);
person Sam-Elie    schedule 30.10.2012
comment
Сэм Эли, похоже, это не работает в IE, Safari или Opera. У вас есть кроссбраузерная версия этого скрипта? Это прекрасно работает в Firefox и Chrome. Спасибо. - person ; 14.06.2013
comment
Когда я вставляю указанную выше функцию в свой сценарий, Dreamweaver и FF выдают ошибку в первой строке function onMouseOut (this, event) {. Похоже, вам не разрешено указывать это как параметр. Я предполагаю, что это вроде работает, когда я пропускаю это во входных параметрах, но я не знаю наверняка, потому что я не знаю того, чего не знаю. - person TARKUS; 21.07.2013
comment
У меня также была такая же проблема, всплывающая в Chrome в той же строке, за исключением this из строки определения функции, упомянутой @GregoryLewis, исправила проблему. Также для дополнительной поддержки кроссбраузерности попробуйте следующее: if (window.addEventListener) {document.getElementById ('parent'). AddEventListener ('mouseout', onMouseOut, true); } else if (window.attachEvent) {document.getElementById ('родительский'). attachEvent (onmouseout, onMouseOut); } - person DWils; 30.01.2014
comment
Очень симпатичный! Лучший ответ, если требуется поддержка старых браузеров. - person BurninLeo; 14.05.2017

вместо onmouseout используйте onmouseleave.

Вы не показали нам свой конкретный код, поэтому я не могу показать вам на вашем конкретном примере, как это сделать.

Но это очень просто: просто замените onmouseout на onmouseleave.

Все :) Итак, все просто :)

Если не знаете, как это сделать, см. Пояснения по:

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

Торт :) Наслаждайтесь :)

person kavi    schedule 28.05.2019
comment
Это отличный крик, стоит проверить, есть ли у вас похожая ситуация. - person Chris; 31.07.2019
comment
Как раз то, что мне было нужно! благодарю вас! - person Dazag; 02.12.2020

Спасибо Амджаду Масаду, который меня вдохновил.

У меня есть следующее решение, которое, похоже, работает в 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
    }
person user1431972    schedule 02.06.2012

Если вы используете jQuery, вы также можете использовать функцию mouseleave, которая со всем этим занимается за вас.

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

do_something срабатывает, когда мышь входит в targetdiv или любой из его дочерних элементов, do_something_else срабатывает только тогда, когда мышь покидает targetdiv и любой из его дочерних элементов.

person Jamie Brown    schedule 06.05.2014

Я думаю, что в Quirksmode есть все ответы, которые вам нужны (поведение пузырей в разных браузерах и < em> mouseenter / mouseleave), но я думаю, что наиболее частым выводом из этого беспорядка всплытия событий является использование фреймворка, такого как JQuery или Mootools (который имеет mouseenter < / em> и mouseleave, которые, как вы и предполагали, произойдут).

Посмотрите, как они это делают, если хотите, сделайте это сами
или можете создайте свою собственную «экономичную среднюю» версию Mootools, используя только часть события (и ее зависимости).

person Ruben    schedule 15.01.2011

Попробуйте mouseleave()

Пример :

<div id="parent" mouseleave="function">
   <div id="child">

   </div>
</div>

;)

person Jean-Philippe    schedule 18.06.2013

Я нашел очень простое решение,

просто используйте событие 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>

Надеюсь, это поможет :)

person Arminius    schedule 26.12.2012

Хотя упомянутое вами решение использует jquery, mouseenter и mouseleave - это собственные события dom, поэтому вы можете использовать их без jquery.

person Mohammed Essehemy    schedule 26.01.2018

Есть два способа справиться с этим.

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>
person selbie    schedule 15.01.2011
comment
Как я могу заставить эту работу onmouseout вместо onmouseover? - person John; 15.01.2011
comment
Ой. Виноват. Я просто меняю все ссылки при наведении указателя мыши на указатель мыши в приведенном выше коде. Это должно сделать это за вас. - person selbie; 15.01.2011
comment
Для первого метода event.target всегда соответствует родительскому элементу, когда указатель мыши проходит над дочерним элементом, поэтому он не работает. - person John; 15.01.2011
comment
То же самое происходит и со вторым методом. Когда мышь входит в дочерний элемент, она все равно запускает событие. - person John; 15.01.2011

Я заставляю его работать как шарм с помощью этого:

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>

В общем, не только для одного div и не нужно добавлять id="..." на каждом слое.

person z666zz666z    schedule 28.03.2012

Если у вас есть доступ к элементу, к которому событие прикреплено внутри метода mouseout, вы можете использовать contains(), чтобы узнать, является ли event.relatedTarget дочерним элементом или нет.

Поскольку event.relatedTarget - это элемент, в который перешла мышь, если это не дочерний элемент, вы вышли из элемента.

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

На Angular 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');
 }
}
person Alan Torrico    schedule 12.12.2018

просто мы можем проверить, что e.relatedTarget имеет дочерний класс, и, если true, вернуть функцию.

    if ($(e.relatedTarget).hasClass("ctrl-btn")){
        return;
    }

это код, который работал у меня, я использовал для воспроизведения видео html5, кнопка паузы переключает элемент видео при наведении

element.on("mouseover mouseout", function(e) {

    if(e.type === "mouseout"){

        if ($(e.relatedTarget).hasClass("child-class")){
            return;
        }

    }

});
person yathavan    schedule 17.09.2020

Я проверяю смещение исходного элемента, чтобы получить координаты страницы границ элемента, затем убеждаюсь, что действие mouseout срабатывает только тогда, когда mouseout выходит за эти границы. Грязно, но работает.

$(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
}
}
person saranicole    schedule 25.01.2012

Если вы добавили (или имеете) класс или идентификатор 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
  }
}

Таким образом, все выполняется только тогда, когда событие находится в родительском div.

person Karthik Palaniappan    schedule 13.09.2013

Я просто хотел чем-то с вами поделиться.
У меня были проблемы с 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()

Я объединил множество событий указателя ng (mousemove, mouveover, ...), но ни одно мне не помогло.

Решение CSS:

Я нашел решение с простым свойством css, которое использую все чаще и чаще:

pointer-events: none;

В основном я использую это так (в элементах моего списка):

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

С этим хитрым вариантом события ng-mouse больше не будут запускаться, и мое плавающее меню навигации больше не закроется, когда курсор окажется над ним и над элементом из списка.

Чтобы продолжить:

Как и следовало ожидать, это решение работает, но мне оно не нравится.
Мы не контролируем наши события, и это плохо.
Кроме того, для этого у вас должен быть доступ к vm.isHover области, и это может невозможно или возможно, но так или иначе грязно.
Я могу сделать скрипку, если кто-то захочет посмотреть.

Тем не менее, у меня нет другого решения ...
Это долгая история, и я не могу дать вам картошку, поэтому, пожалуйста, простите меня, мой друг.
В любом случае, pointer-events: none это жизнь, так что запомните это.

person C0ZEN    schedule 01.05.2017

Есть простой способ заставить его работать. Элемент и все дочерние элементы вы задаете одно и то же имя класса, затем:

element.onmouseover = function(event){
 if (event.target.className == "name"){
 /*code*/
 }
}
person Arturo Viñas Salazar    schedule 09.02.2021

Также для vanillajs вы можете использовать этот способ

document.querySelector('.product_items') && document.querySelector('.product_items').addEventListener('mouseleave', () => updateCart())


const updateCart = () => {
let total = 0;
document.querySelectorAll('input') && document.querySelectorAll('input').forEach(item => total += +item.value)
  document.getElementById('total').innerHTML = total
}
<div class="product_items">
  <div class="product_item">
    <div class="product_name">
    </div>
    <div class="multiply__btn">
      <button type="button">-</button>
      <input name="test" type="text">
      <button type="button">+</button>
    </div>
  </div>
  <div class="product_item">
    <div class="product_name">
    </div>
    <div class="multiply__btn">
      <button type="button">-</button>
      <input name="test" type="text">
      <button type="button">+</button>
    </div>
  </div>
  <div class="product_item">
    <div class="product_name">
    </div>
    <div class="multiply__btn">
      <button type="button">-</button>
      <input name="test" type="text">
      <button type="button">+</button>
    </div>
  </div>
</div>

<div id="total"></div>

person Galaxy IT    schedule 16.07.2021

person    schedule
comment
Пожалуйста, дайте объяснение в своем ответе. - person Sebass van Boxel; 02.10.2012