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


169

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

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

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

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

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


Я закончил тем, что решил проблему, используя тайм-аут и очистив его от наведения дочерних элементов
Джон

Привет, я посмотрел ваше демо, но когда я перехожу по элементам в нижней правой панели, ничего не происходит?
Джон

1
Нашел этот ответ, если вы хотите предотвратить наведение мыши на родительское событие при наведении указателя мыши на дочерний элемент: javascript mouseover / mouseout .
user823527

1
Проверьте ответ
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, посмотрите ...

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

Обратите внимание, что это проверяет только непосредственного родителя, если родительский div имел вложенные дочерние элементы, то вы должны каким-то образом пройти через родительские элементы, ища «оригинальный элемент»

РЕДАКТИРОВАТЬ пример для вложенных детей

РЕДАКТИРОВАТЬ Исправлено для, надеюсь, кросс-браузер

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 к скрипке, чтобы его можно было проверить. Предупреждение или что-то должно сделать.
Джон

Извините, похоже, у вас есть предупреждение ('MouseOut'), но у меня оно не работает? Я нажал "Run" без опции библиотеки JS
Джон

Проверено на сафари и хроме, но должно работать на всем! какой у тебя браузер?
Амжад Масад

2
Что делать, если у вас есть внуки? или правнуки? то есть, вы можете иметь рекурсивное решение вместо того, чтобы просто подражать детям?
Roozbeh15

20
Это не работает mouseleaveправильный ответ
Code Whisperer

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
Brilliant! Полностью исправлена ​​моя проблема!
Anna_MediaGirl

4
Это должен быть первый или, по крайней мере, второй ответ, я был готов пойти с хлопотами слушателей событий, и это полностью решило и мою проблему. Так просто !
Микаэль В.

9
Это предотвращает срабатывание мыши, но для меня это также предотвращает щелчок по фактическому ребенку в качестве выбора в меню, которое является точкой меню.
Джонбейкерс

@hellofunk Вот почему вы бы применили событие click к родителю. Точный требуемый код варьируется в зависимости от реализации, этот ответ просто предлагает использовать 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 выдают ошибку в первой строке: «function onMouseOut (this, event) {». Похоже, вам не разрешено указывать "this" в качестве параметра. Я думаю, это работает, когда я пропускаю «this» из входных параметров, но я точно не знаю, потому что «я не знаю, чего я не знаю».
ТАРКУС

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 сработает, когда мышь войдет в целевой целевой элемент или любого из его дочерних элементов, do_something_else сработает только тогда, когда мышь покинет целевой целевой элемент и любой из его дочерних элементов.


8

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

Посмотрите, как они это делают, если вы хотите, сделайте это сами,
или вы можете создать свою собственную версию 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>

Тот же пример с функцией mouseout:

<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" в коде выше. Это должно сделать это для вас.
Селби

Для первого метода event.target всегда соответствует родительскому элементу, когда указатель мыши наводится на дочерний элемент, поэтому он не работает
John

То же самое происходит для второго метода. Когда мышь входит в дочерний элемент, она по-прежнему вызывает событие
Джон

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>

Так что более общий, не только для одного 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

Я проверяю смещение исходного элемента, чтобы получить координаты страницы границ элемента, а затем проверяю, что действие 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
}
}


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
  }
}

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


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()

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

CSS решение:

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

pointer-events: none;

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

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

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

Чтобы идти дальше:

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

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

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.