Запретить 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.