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

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

Как я могу предотвратить запуск события mouseout при попадании в дочерний элемент WITHOUT jQuery.

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

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

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

Ответ 1

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 ФИКСИРОВАННАЯ ссылка для поддержки кросс-браузера http://jsfiddle.net/RH3tA/9/

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

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

Ответ 2

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

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

Это точная вещь, которую вы ищете. Пример:

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

или, в jQuery:

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

пример здесь.

Ответ 3

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

.parent * {
     pointer-events: none;
}

Ответ 4

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

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

Ответ 5

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

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

Ответ 6

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

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

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

Ответ 7

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

Пример:

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

   </div>
</div>

;)

Ответ 8

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

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

Ответ 9

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

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

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

Ответ 10

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

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

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

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

Если вы не уверены, как это сделать, см. объяснение на:

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

Мир торта :) Наслаждайтесь этим :)

Ответ 11

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

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>

Ответ 12

Я делаю это как шарм с этим:

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

Ah и тег MyDiv:

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

Таким образом, когда onmouseout переходит к дочернему, grand-child и т.д., 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="..." на каждый уровень.

Ответ 13

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

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

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

Ответ 14

Хотя решение вы упомянули использует Jquery, MouseEnter и MouseLeave являются носителями событий DOM, так что вы можете использовать без JQuery.

Ответ 15

На угловых 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');
 }
}

Ответ 16

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

Ответ 17

var elem = $('#some-id');
elem.mouseover(function () {
   // Some code here
}).mouseout(function (event) {
   var e = event.toElement || event.relatedTarget;
   if (elem.has(e).length > 0) return;

   // Some code here
});

Ответ 18

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

Ответ 19

Я просто хотел поделиться чем-то с вами.
Мне было трудно с событиями 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 - это жизнь, поэтому запомните ее.