Impedire onmouseout quando si passa con il mouse sull'elemento figlio del div assoluto parent SENZA jQuery


169

Sto riscontrando problemi con la onmouseoutfunzione in un div assolutamente positivo. Quando il mouse colpisce un elemento figlio nel div, l'evento mouseout si attiva, ma non voglio che si attivi finché il mouse non è fuori dal genitore, div assoluto.

Come posso evitare che l' mouseoutevento venga generato quando colpisce un elemento figlio SENZA jquery.

So che questo ha a che fare con il gorgoglio degli eventi, ma non ho fortuna a scoprire come risolverlo.

Ho trovato un post simile qui: Come disabilitare gli eventi mouseout attivati ​​da elementi figlio?

Tuttavia, quella soluzione utilizza jQuery.


Ho finito per risolvere il problema usando un timeout e cancellandolo al passaggio del mouse sugli elementi figlio
John

Ciao, ho visto la tua demo ma quando ripasso gli elementi nel pannello in basso a destra non succede nulla?
John,

1
Hai trovato questa risposta se stai cercando di impedire un mouseout sull'evento padre quando passi il mouse su un elemento figlio: javascript mouseover / mouseout .
user823527

Risposte:


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

Ho fatto una rapida demo di JsFiddle, con tutti i CSS e HTML necessari, dai un'occhiata ...

EDIT FIXED link per supporto cross-browser http://jsfiddle.net/RH3tA/9/

NOTA: questo controlla solo il genitore immediato, se il div genitore aveva figli nidificati, allora devi in ​​qualche modo attraversare gli elementi che i genitori cercano "elemento originale"

Esempio di EDIT per i bambini nidificati

EDIT Risolto per spero 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;
}

E un nuovo link JSFiddle , EDIT aggiornato


1
È necessario aggiungere un evento mouseout al violino in modo che possa essere testato. Un avviso o qualcosa dovrebbe fare.
Giovanni,

Scusa, sembra che tu abbia un avviso ("MouseOut"), tuttavia non funziona per me? Ho funzionato senza opzione di libreria JS
John

Testato su Safari e Chrome, ma dovrebbe funzionare su tutto! qual è il tuo browser?
Amjad Masad,

2
E se avessi dei nipoti? o pronipoti? cioè puoi avere una soluzione ricorsiva invece di semplicemente immidiare i bambini?
Roozbeh15,

20
Questo non funziona mouseleaveè la risposta corretta
Code Whisperer,

126

Usa onmouseleave.

Oppure, in jQuery, utilizzare mouseleave()

È la cosa esatta che stai cercando. Esempio:

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

o, in jQuery:

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

un esempio è qui .


3
onMouseLeaveè quello che ho finito per usare anche perché non si gonfia.
Alecz,

Mi hai risparmiato un sacco di tempo. thnx
npo

mouseleavenon può essere cancellabile.
grecdev,

@grecdev, dì di più!
Ben Wheeler,

97

Per una soluzione CSS semplice più semplice che funziona in alcuni casi ( IE11 o più recente ), è possibile rimuovere i bambini pointer-eventsimpostandoli sunone

.parent * {
     pointer-events: none;
}

4
Brillante! Totalmente appena risolto il mio problema!
Anna_MediaGirl

4
Questa dovrebbe essere la prima o almeno la seconda risposta, ero pronto per affrontare la seccatura degli ascoltatori di eventi e anche questo ha risolto completamente il mio problema. Così semplice !
Mickael V.

9
Ciò impedisce al mouseout di attivarsi, ma per me impedisce anche al bambino reale di essere selezionato come una selezione nel menu, che è il punto del menu.
johnbakers,

@hellofunk Ecco perché dovresti applicare l'evento click al genitore. Il codice esatto necessario varia a seconda dell'implementazione, questa risposta suggerisce solo l'uso della pointer-eventsproprietà
Zach Saucier,

1
funziona solo se non hai altri elementi del controller (modifica, elimina) all'interno dell'area, perché anche questa soluzione li blocca.
Zoltán Süle

28

Ecco una soluzione più elegante basata su ciò che è venuto di seguito. rappresenta l'evento che bolle da più di un livello di bambini. Conta anche per problemi tra browser.

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

Sam Elie, questo sembra non funzionare su IE, Safari o Opera. Hai una versione cross-browser di questo script? Funziona come un fascino in Firefox e Chrome. Grazie.

1
Quando incollo la funzione sopra nel mio script, Dreamweaver e FF generano un errore sulla prima riga, "function onMouseOut (this, event) {". Sembra che non ti sia permesso di inserire "questo" come parametro. Immagino che funzioni quando tralascio "questo" dai parametri di input, ma non lo so per certo, perché "Non so cosa non conosco".
TARKUS,

1
Ho anche avuto lo stesso problema pop-up in Chrome sulla stessa riga, escluso thisdalla riga di definizione della funzione @GregoryLewis menzionata risolto il problema. Inoltre, per ulteriore supporto per più browser, prova questo: if (window.addEventListener) {document.getElementById ('parent'). AddEventListener ('mouseout', onMouseOut, true); } else if (window.attachEvent) {document.getElementById ('parent'). attachEvent ("onmouseout", onMouseOut); }
DWils,

Molto bello! La migliore risposta se è necessario il supporto per browser meno recenti.
BurninLeo,

13

Grazie ad Amjad Masad che mi ha ispirato.

Ho la seguente soluzione che sembra funzionare in IE9, FF e Chrome e il codice è piuttosto breve (senza la complessa chiusura e le cose trasversali del bambino):

    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

invece di onmouseout usa onmouseleave.

Non ci hai mostrato il tuo codice specifico, quindi non posso mostrarti nel tuo esempio specifico come farlo.

Ma è molto semplice: basta sostituire onmouseout con onmouseleave.

Questo è tutto :) Quindi, semplice :)

Se non sei sicuro di come farlo, vedi la spiegazione su:

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

Pace della torta :) Divertiti :)


Questo è un grande grido, vale la pena dare un'occhiata se hai una situazione simile
Chris

12

Se stai usando jQuery puoi anche usare la funzione "mouseleave", che si occupa di tutto questo per te.

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

do_something si attiva quando il mouse entra nel bersaglio o uno dei suoi figli, do_something_else si attiva solo quando il mouse lascia il bersaglio e tutti i suoi figli.


8

Penso che Quirksmode abbia tutte le risposte di cui hai bisogno (diversi comportamenti di bubbling dei browser e gli eventi mouseenter / mouseleave ), ma penso che la conclusione più comune a quel pasticcio di bubbling di eventi sia l'uso di un framework come JQuery o Mootools (che ha il mouseenter e eventi di mouseleave , che sono esattamente ciò che hai intuito sarebbe accaduto).

Dai un'occhiata a come lo fanno, se vuoi, fallo da solo
o puoi creare la tua versione "lean mean" personalizzata di Mootools con solo la parte dell'evento (e le sue dipendenze).


7

Provare mouseleave()

Esempio :

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

   </div>
</div>

;)


5

Ho trovato una soluzione molto semplice,

basta usare l'evento onmouseleave = "myfunc ()" rispetto all'evento onmousout = "myfunc ()"

Nel mio codice ha funzionato !!

Esempio:

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

Stesso esempio con la funzione 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>

Spero che sia d'aiuto :)



2

Esistono due modi per gestirlo.

1) Controlla il risultato di event.target nel tuo callback per vedere se corrisponde al div del tuo genitore

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) Oppure usa la funzione di cattura degli eventi e chiama event.stopPropagation nella funzione di 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>

Come posso farlo funzionare per onmouseout anziché onmouseover?
John,

Ops. Colpa mia. Ho appena cambiato tutti i riferimenti "mouseover" a "mouseout" nel codice sopra. Questo dovrebbe farlo per te.
selbie

Per il primo metodo, event.target corrisponde sempre all'elemento padre quando il mouse passa sull'elemento figlio, quindi non funziona
John,

La stessa cosa accade per il secondo metodo. Quando il mouse entra in un elemento figlio, attiva comunque l'evento
John,

1

Lo faccio funzionare come un fascino con questo:

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

Ah, e il MyDivtag è così:

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

In questo modo, quando onmouseout va a un bambino, un nipote, ecc ... style.display='none'non viene eseguito; ma quando onmouseout esce da MyDiv, viene eseguito.

Quindi non è necessario interrompere la propagazione, utilizzare i timer, ecc ...

Grazie per esempi, ho potuto ricavare questo codice da loro.

Spero che questo aiuti qualcuno.

Inoltre può essere migliorato in questo modo:

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

E poi i tag DIV in questo modo:

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

Quindi, più in generale, non solo per un div e non è necessario aggiungere id="..."su ogni livello.


1

Se hai accesso all'elemento a cui è collegato l'evento all'interno del mouseoutmetodo, puoi usare contains()per vedere se event.relatedTargetè un elemento figlio o meno.

Come event.relatedTargetè l'elemento in cui è passato il mouse, se non è un elemento figlio, ti sei allontanato dall'elemento.

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

1

Su Angolare 5, 6 e 7

<div (mouseout)="onMouseOut($event)"
     (mouseenter)="onMouseEnter($event)"></div>

E poi

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

Controllo l'offset dell'elemento originale per ottenere le coordinate della pagina dei limiti dell'elemento, quindi mi assicuro che l'azione del mouseout sia attivata solo quando il mouseout è fuori da tali limiti. Sporco ma funziona.

$(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
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
});

Per favore, dai qualche spiegazione con il tuo anwser.
Sebass van Boxel,

0

Se hai aggiunto (o hai) una classe CSS o un ID all'elemento genitore, puoi fare qualcosa del genere:

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

Quindi le cose vengono eseguite solo quando l'evento si trova sul div parent.


0

Volevo solo condividere qualcosa con te.
Ho avuto dei momenti difficili con ng-mouseentered ng-mouseleaveeventi.

Il caso di studio:

Ho creato un menu di navigazione mobile che si attiva quando il cursore si trova su un'icona.
Questo menu era in cima a ogni pagina.

  • Per gestire mostra / nascondi nel menu, seleziono una classe.
    ng-class="{down: vm.isHover}"
  • Per attivare vm.isHover , utilizzo gli eventi del mouse di ng.
    ng-mouseenter="vm.isHover = true"
    ng-mouseleave="vm.isHover = false"

Per ora, tutto è andato bene e ha funzionato come previsto.
La soluzione è pulita e semplice.

Il problema in arrivo:

In una vista specifica, ho un elenco di elementi.
Ho aggiunto un pannello azioni quando il cursore si trova su un elemento dell'elenco.
Ho usato lo stesso codice di cui sopra per gestire il comportamento.

Il problema:

Ho capito quando il mio cursore si trova nel menu di navigazione mobile e anche nella parte superiore di un elemento, c'è un conflitto tra loro.
Viene visualizzato il pannello azioni e la navigazione mobile è nascosta.

Il fatto è che anche se il cursore si trova sul menu di navigazione mobile, viene attivato l'elemento elenco ng-mouseenter.
Non ha senso per me, perché mi aspetterei un'interruzione automatica degli eventi di propagazione del mouse.
Devo dire che sono rimasto deluso e dedico un po 'di tempo a scoprire quel problema.

Primi pensieri:

Ho provato a usare questi:

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

Ho combinato molti eventi puntatore (mousemove, mouveover, ...) ma nessuno mi aiuta.

Soluzione CSS:

Ho trovato la soluzione con una semplice proprietà CSS che utilizzo sempre di più:

pointer-events: none;

Fondamentalmente, lo uso così (sugli elementi della mia lista):

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

Con questo difficile, gli eventi di ng-mouse non verranno più attivati ​​e il mio menu di navigazione mobile non si chiuderà più quando il cursore si trova sopra di esso e sopra un elemento dell'elenco.

Andare oltre:

Come ci si può aspettare, questa soluzione funziona ma non mi piace.
Non controlliamo i nostri eventi ed è un male.
Inoltre, è necessario avere un accesso vm.isHoverall'ambito per raggiungere questo obiettivo e potrebbe non essere possibile o possibile ma sporco in un modo o nell'altro.
Potrei fare un violino se qualcuno vuole guardare.

Tuttavia, non ho un'altra soluzione ...
È una lunga storia e non posso darti una patata, quindi per favore perdonami amico mio.
Comunque, pointer-events: noneè la vita, quindi ricordalo.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.