Evitar el onmouseout cuando se desplaza el elemento hijo del div absoluto padre SIN jQuery


Estoy teniendo problemas con la función onmouseout en un div positoned absoluto. Cuando el ratón golpea un elemento hijo en el div, el evento mouseout se dispara, pero no quiero que se dispare hasta que el ratón esté fuera del div padre, absoluto.

¿Cómo puedo evitar que el evento mouseout se dispare cuando golpea un elemento hijo SIN jquery?

Sé que esto tiene algo que ver con el burbujeo de eventos, pero no estoy teniendo suerte en descubrir cómo resolver esto.

Encontré un similar publicar aquí: ¿Cómo deshabilitar los eventos de mouseout activados por elementos secundarios?

Sin embargo, esa solución utiliza jQuery.

Author: Cœur, 2011-01-15

17 answers

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

Hice una demostración rápida de JsFiddle, con todo el CSS y HTML necesario, échale un vistazo...

EDITAR Enlace fijo para compatibilidad entre navegadores http://jsfiddle.net/RH3tA/9 /

TENGA EN CUENTA que esto solo comprueba el padre inmediato, si el div padre tenía hijos anidados, entonces tiene que atravesar de alguna manera los elementos padres buscando el "elemento original"

EDITAR ejemplo para hijos anidados

EDITAR Arreglado para con suerte cross-browser

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

Y un nuevo JSFiddle, EDITAR link actualizado

 86
Author: Amjad Masad,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-05-05 14:54:37

Para una solución CSS pura más simple que funcione en algunos casos ( IE11 o más reciente), se podría eliminar pointer-events poniéndolos en none

.parent * {
     pointer-events: none;
}
 72
Author: Zach Saucier,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-07-23 02:41:51

Use onmouseleave.

O, en jQuery, use mouseleave()

Es exactamente lo que estás buscando. Ejemplo:

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

O, en jQuery:

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

Un ejemplo es aquí.

 66
Author: PHPWannaBePro,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-05-26 14:35:36

Aquí hay una solución más elegante basada en lo que vino a continuación. cuenta el evento burbujeante de más de un nivel de niños. También tiene en cuenta los problemas entre navegadores.

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);
 26
Author: Sam-Elie,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2012-11-08 10:03:22

Si está utilizando jQuery, también puede usar la función "mouseleave", que se ocupa de todo esto por usted.

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

Do_something se disparará cuando el ratón entre en elargetdiv o en cualquiera de sus hijos, do_something_else solo se disparará cuando el ratón salga delargetdiv y de cualquiera de sus hijos.

 12
Author: Jamie Brown,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-05-06 23:33:50

Gracias a Amjad Masad que me inspiró.

Tengo la siguiente solución que parece funcionar en IE9, FF y Chrome y el código es bastante corto (sin el cierre complejo y las cosas secundarias transversales):

    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
    }
 11
Author: Lawrence Mok,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2012-06-02 04:40:48

Creo que Quirksmode tiene todas las respuestas que necesita (diferentes navegadores bubbling behaviour y los eventos mouseenter/mouseleave ), pero creo que la conclusión más común a ese evento bubbling mess es el uso de un framework como jQuery o Mootools (que tiene el mouseenter y mouseleave eventos, que son exactamente lo que intuías que sucedería).

Echa un vistazo a cómo lo hacen, si quieres, hazlo tú mismo
o puedes cree su versión personalizada "lean mean" de Mootools con solo la parte del evento (y sus dependencias).

 7
Author: Ruben,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2011-01-15 03:34:08

Intenta mouseleave()

Ejemplo:

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

   </div>
</div>

;)

 7
Author: Jean-Philippe Ménard,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2013-06-18 18:41:00

He encontrado una solución muy simple,

Simplemente use el evento onmouseleave="myfunc()" que el evento onmousout="myfunc ()"

En mi código funcionó!!

Ejemplo:

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

El mismo ejemplo con la función 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>

Espero que ayude:)

 5
Author: Arminius,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2012-12-26 13:03:59

Hay dos maneras de manejar esto.

1) Compruebe el evento.resultado objetivo en su devolución de llamada para ver si coincide con su div padre

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) O use captura de eventos y llame a eventos.stopPropagation en la función callback

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>
 1
Author: selbie,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2011-01-15 06:56:02

Hago que funcione como un encanto con esto:

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

Ah, y MyDiv tag es así:

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

De esta manera, cuando una despedida va a un niño, nieto, etc... el style.display='none' no se ejecuta; pero cuando onmouseout sale de myDiv se ejecuta.

Así que no hay necesidad de detener la propagación, usar temporizadores, etc...

Gracias por los ejemplos, podría hacer este código a partir de ellos.

Espero que esto ayude a alguien.

También se puede mejorar así:

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

Y luego las etiquetas DIVs así:

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

Así que más general, no solo para un div y no hay necesidad de añadir id="..." en cada capa.

 1
Author: z666zz666z,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2012-03-28 11:23:38

Compruebo el desplazamiento del elemento original para obtener las coordenadas de página de los límites del elemento, luego me aseguro de que la acción mouseout solo se active cuando el mouseout esté fuera de esos límites. Sucio, pero funciona.

$(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
Author: saranicole,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2012-01-25 15:08:46
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
});
 0
Author: Michael Vaganov,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2012-10-02 15:49:06

Si agregaste (o tienes) una clase CSS o id al elemento padre, entonces puedes hacer algo como esto:

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

Así que las cosas solo se ejecutan cuando el evento está en el div padre.

 0
Author: Karthik Palaniappan,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2013-09-13 19:19:09

Solo quería compartir algo contigo.
Me costó trabajo con los eventos ng-mouseenter y ng-mouseleave.

El estudio de caso:

He creado un menú de navegación flotante que es alternar cuando el cursor está sobre un icono.
Este menú estaba en la parte superior de cada página.

  • Para manejar mostrar/ocultar en el menú, alterno una clase.
    ng-class="{down: vm.isHover}"
  • Para alternar vm.isHover, utilizo los eventos ng mouse.
    ng-mouseenter="vm.isHover = true"
    ng-mouseleave="vm.isHover = false"

Para ahora, todo estaba bien y funcionó como se esperaba.
La solución es limpia y sencilla.

El problema entrante:

En una vista específica, tengo una lista de elementos.
He añadido un panel de acciones cuando el cursor está sobre un elemento de la lista.
Usé el mismo código que el anterior para manejar el comportamiento.

El problema:

Me di cuenta cuando mi cursor está en el menú de navegación flotante y también en la parte superior de un elemento, hay un conflicto entre unos y otros.
El panel de acción apareció y la navegación flotante estaba oculta.

La cosa es que incluso si el cursor está sobre el menú de navegación flotante, se activa el elemento de lista ng-mouseenter.
No tiene sentido para mí, porque esperaría una interrupción automática de los eventos de propagación del ratón.
Debo decir que me decepcionó y dediqué algún tiempo a descubrir ese problema.

Primeros pensamientos:

Traté de utilice estos:

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

Combiné una gran cantidad de eventos de puntero ng (mousemove, mouveover,...) pero nadie me ayuda.

Solución CSS:

Encontré la solución con una propiedad css simple que uso cada vez más:

pointer-events: none;

Básicamente, lo uso así (en los elementos de mi lista):

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

Con este complicado, los eventos ng-mouse ya no se activarán y mi menú de navegación flotante ya no se activará cierre él mismo cuando el cursor está sobre él y sobre un elemento de la lista.

Para ir más lejos:

Como es de esperar, esta solución funciona, pero no me gusta.
No controlamos nuestros eventos y es malo.
Además, debe tener un acceso al ámbito vm.isHover para lograr eso y puede que no sea posible o posible, pero sucio de alguna manera u otra.
Podría hacer un violín si alguien quiere mirar.

Sin embargo, no tengo otro solución...
Es una larga historia y no puedo darte una papa así que por favor perdóname amigo mío.
De todos modos, pointer-events: none es la vida, así que recuérdalo.

 0
Author: C0ZEN,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-05-01 21:48:54

Si tiene acceso al elemento al que se adjunta el evento dentro del método mouseout, puede usar contains() para ver si el event.relatedTarget es un elemento hijo o no.

Como event.relatedTarget es el elemento al que ha pasado el ratón, si no es un elemento hijo, se ha eliminado del elemento.

div.onmouseout = function (event) {
    if (!div.contains(event.relatedTarget)) {
        // moused out of div
    }
}
 0
Author: spaceman,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-05-14 16:10:41

Aunque la solución a la que se refirió utiliza jquery, mouseenter y mouseleave son eventos dom nativos, por lo que puede usar sin jquery.

 0
Author: Mohammed Essehemy,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-01-26 22:29:25