Verhindern Sie onmouseout, wenn Sie das untergeordnete Element des übergeordneten absoluten div OHNE jQuery bewegen

169

Ich habe Probleme mit der onmouseoutFunktion in einem absolut positionierten Div. Wenn die Maus auf ein untergeordnetes Element im div trifft, wird das mouseout-Ereignis ausgelöst, aber ich möchte nicht, dass es ausgelöst wird, bis sich die Maus außerhalb des übergeordneten absoluten div befindet.

Wie kann ich verhindern, dass das mouseoutEreignis ausgelöst wird, wenn es auf ein untergeordnetes Element OHNE jquery trifft?

Ich weiß, dass dies etwas mit dem Sprudeln von Ereignissen zu tun hat, aber ich habe kein Glück, herauszufinden, wie ich das herausfinden kann.

Ich habe hier einen ähnlichen Beitrag gefunden: Wie deaktiviere ich Mouseout-Ereignisse, die von untergeordneten Elementen ausgelöst werden?

Diese Lösung verwendet jedoch jQuery.

John
quelle
Am Ende löste ich das Problem mit einer Zeitüberschreitung und löschte es beim Schweben der untergeordneten Elemente
John
Hallo, ich habe mir Ihre Demo angesehen, aber wenn ich über die Elemente im unteren rechten Bereich gehe, passiert nichts?
John
1
Diese Antwort wurde gefunden, wenn Sie ein Mouseout für das übergeordnete Ereignis verhindern möchten, wenn Sie mit der Maus über ein untergeordnetes Element fahren: Javascript Mouseover / Mouseout .
user823527
1
Check out @Zach Saucier Antwort unten begraben
craigmichaelmartin

Antworten:

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

Ich habe eine schnelle JsFiddle-Demo mit all dem benötigten CSS und HTML erstellt.

EDIT feste Querung für Cross-Browser - Unterstützung http://jsfiddle.net/RH3tA/9/

HINWEIS : Dies überprüft nur das unmittelbare übergeordnete Element. Wenn das übergeordnete div verschachtelte Kinder hatte, müssen Sie die Elemente, die Eltern suchen, auf irgendeine Weise durchlaufen, um nach dem "ursprünglichen Element" zu suchen.

BEARBEITEN Sie ein Beispiel für verschachtelte Kinder

BEARBEITEN Für hoffentlich browserübergreifend behoben

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

Und ein neuer JSFiddle , EDIT aktualisierter Link

Amjad Masad
quelle
1
Sie sollten der Geige ein Mouseout-Ereignis hinzufügen, damit sie getestet werden kann. Ein Alarm oder etwas sollte tun.
John
Entschuldigung, es sieht so aus, als hätten Sie einen Alarm ('MouseOut'), aber er funktioniert bei mir nicht? Ich habe Run ohne JS-Bibliotheksoption getroffen
John
Getestet auf Safari und Chrom, sollte aber an allem funktionieren! Was ist dein Browser?
Amjad Masad
2
Was ist, wenn Sie Enkel haben? oder Urenkel? dh können Sie eine rekursive Lösung haben, anstatt nur Kinder zu immidiieren?
Roozbeh15
19
Das funktioniert nicht. mouseleaveist die richtige Antwort
Code Whisperer
125

Verwenden Sie onmouseleave.

Oder verwenden Sie in jQuery mouseleave()

Es ist genau das, wonach Sie suchen. Beispiel:

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

oder in jQuery:

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

Ein Beispiel ist hier .

PHPWannaBePro
quelle
3
onMouseLeaveist das, was ich letztendlich auch benutzt habe, da es nicht sprudelt.
Alecz
Hat mir viel Zeit gespart. thnx
npo
mouseleavekann nicht stornierbar sein.
grecdev
@grecdev, sag mehr!
Ben Wheeler
96

Für eine einfachere reine CSS-Lösung , die in einigen Fällen funktioniert ( IE11 oder neuer ), kann man Kinder entfernen, pointer-eventsindem man sie auf setztnone

.parent * {
     pointer-events: none;
}
Zach Saucier
quelle
4
Brillant! Total nur mein Problem behoben!
Anna_MediaGirl
4
Dies sollte die erste oder zumindest zweite Antwort sein. Ich war bereit, mich dem Ärger der Event-Listener zu stellen, und dies hat auch mein Problem vollständig behoben. So einfach !
Mickael V.
9
Dies verhindert, dass das Mouseout ausgelöst wird, aber für mich verhindert es auch, dass das tatsächliche Kind als Auswahl im Menü angeklickt wird, das der Punkt des Menüs ist.
Johnbakers
@hellofunk Deshalb würden Sie das Klickereignis auf das übergeordnete Ereignis anwenden. Der genaue benötigte Code variiert je nach Implementierung. Diese Antwort schlägt lediglich die Verwendung der pointer-eventsEigenschaft vor
Zach Saucier
1
Es funktioniert nur, wenn Sie keine anderen Controller-Elemente (Bearbeiten, Löschen) im Bereich haben, da diese Lösung sie ebenfalls blockiert.
Zoltán Süle
28

Hier ist eine elegantere Lösung, die auf den folgenden Informationen basiert. Es ist für Ereignisse verantwortlich, die von mehr als einer Ebene von Kindern ausgehen. Es berücksichtigt auch browserübergreifende Probleme.

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
quelle
Sam Elie, dies scheint nicht mit IE, Safari oder Opera zu funktionieren. Haben Sie eine browserübergreifende Version dieses Skripts? Es funktioniert wie ein Zauber in Firefox und Chrome. Vielen Dank.
1
Wenn ich die obige Funktion in mein Skript einfüge, werfen Dreamweaver und FF in der ersten Zeile den Fehler "function onMouseOut (this, event) {" aus. Es sieht so aus, als ob Sie "dies" nicht als Parameter setzen dürfen. Ich denke, es funktioniert irgendwie, wenn ich "dies" aus den Eingabeparametern weglasse, aber ich weiß es nicht genau, weil "Ich weiß nicht, was ich nicht weiß".
TARKUS
1
Ich hatte auch das gleiche Problem in Chrome in der gleichen Zeile, ausgenommen thisvon der Funktionsdefinitionszeile @GregoryLewis erwähnt das Problem behoben. Versuchen Sie dies auch, um mehr browserübergreifende Unterstützung zu erhalten: if (window.addEventListener) {document.getElementById ('parent'). AddEventListener ('mouseout', onMouseOut, true); } else if (window.attachEvent) {document.getElementById ('parent'). attachEvent ("onmouseout", onMouseOut); }
DWils
Sehr hübsch! Beste Antwort, wenn Unterstützung für ältere Browser erforderlich ist.
BurninLeo
13

Vielen Dank an Amjad Masad, der mich inspiriert hat.

Ich habe die folgende Lösung, die in IE9, FF und Chrome zu funktionieren scheint, und der Code ist ziemlich kurz (ohne den komplexen Abschluss und die transversalen untergeordneten Dinge):

    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
    }
Lawrence Mok
quelle
13

Verwenden Sie anstelle von onmouseout onmouseleave.

Sie haben uns Ihren spezifischen Code nicht gezeigt, daher kann ich Ihnen in Ihrem spezifischen Beispiel nicht zeigen, wie es geht.

Aber es ist sehr einfach: Ersetzen Sie einfach onmouseout durch onmouseleave.

Das ist alles :) Also einfach :)

Wenn Sie sich nicht sicher sind, wie es geht, lesen Sie die Erklärung zu:

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

Ruhe des Kuchens :) Genießen Sie es :)

kavi
quelle
Dies ist ein großartiger Ruf, der es wert ist, überprüft zu werden, wenn Sie eine ähnliche Situation haben
Chris
12

Wenn Sie jQuery verwenden, können Sie auch die Funktion "mouseleave" verwenden, die all dies für Sie erledigt.

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

do_something wird ausgelöst, wenn die Maus das Zieldiv oder eines ihrer untergeordneten Elemente betritt. do_something_else wird nur ausgelöst, wenn die Maus das Zieldiv und eines ihrer untergeordneten Elemente verlässt.

Jamie Brown
quelle
8

Ich denke, Quirksmode hat alle Antworten, die Sie benötigen (unterschiedliches Browsing- Verhalten des Browsers und Mouseenter- / Mouseleave- Ereignisse), aber ich denke, die häufigste Schlussfolgerung für dieses Ereignis ist die Verwendung eines Frameworks wie JQuery oder Mootools (mit dem Mouseenter) und Mausblatt Ereignisse, die genau das sind, was Sie sich vorgestellt haben, würden passieren).

Schauen Sie sich an, wie sie es machen, wenn Sie möchten, machen Sie es selbst
oder Sie können Ihre benutzerdefinierte "Lean Mean" -Version von Mootools nur mit dem Ereignisteil (und seinen Abhängigkeiten) erstellen .

Ruben
quelle
7

Versuchen mouseleave()

Beispiel:

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

   </div>
</div>

;)

Jean-Philippe
quelle
5

Ich habe eine sehr einfache Lösung gefunden,

Verwenden Sie einfach das Ereignis onmouseleave = "myfunc ()" als das Ereignis onmousout = "myfunc ()"

In meinem Code hat es funktioniert !!

Beispiel:

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

Gleiches Beispiel mit Mouseout-Funktion:

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

Ich hoffe es hilft :)

Arminius
quelle
3

Obwohl die von Ihnen genannte Lösung jquery, mouseenter und mouseleave verwendet, handelt es sich um native dom-Ereignisse, sodass Sie sie möglicherweise ohne jquery verwenden.

Mohammed Essehemy
quelle
2

Es gibt zwei Möglichkeiten, damit umzugehen.

1) Überprüfen Sie das Ergebnis event.target in Ihrem Rückruf, um festzustellen, ob es mit Ihrem übergeordneten div übereinstimmt

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) Oder verwenden Sie die Ereigniserfassung und rufen Sie event.stopPropagation in der Rückruffunktion auf

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>
Selbie
quelle
Wie kann ich diese Funktion für onmouseout anstelle von onmouseover verwenden?
John
Hoppla. Mein Fehler. Ich ändere einfach alle "Mouseover" -Referenzen auf "Mouseout" im obigen Code. Das sollte es für dich tun.
Selbie
Bei der ersten Methode stimmt das event.target immer mit dem übergeordneten Element überein, wenn die Maus über das untergeordnete Element fährt, sodass es nicht funktioniert
John,
Gleiches gilt für die zweite Methode. Wenn die Maus ein untergeordnetes Element eingibt, wird das Ereignis immer noch ausgelöst
John,
1

Ich mache es wie ein Zauber damit:

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

Ah, und MyDivTag ist wie folgt:

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

Auf diese Weise, wenn ein Mausausgang zu einem Kind, Enkelkind usw. geht style.display='none' wird das nicht ausgeführt . aber wenn onmouseout MyDiv verlässt, läuft es.

Sie müssen also nicht die Ausbreitung stoppen, Timer verwenden usw.

Vielen Dank für Beispiele, ich könnte diesen Code daraus machen.

Hoffe das hilft jemandem.

Kann auch so verbessert werden:

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

Und dann die DIVs-Tags wie folgt:

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

Also allgemeiner, nicht nur für ein Div und keine Notwendigkeit, id="..."jede Ebene hinzuzufügen .

z666zz666z
quelle
1

Wenn Sie Zugriff auf das Element haben, an das das Ereignis innerhalb der mouseoutMethode angehängt ist , können Sie verwenden, contains()um festzustellen, ob event.relatedTargetes sich um ein untergeordnetes Element handelt oder nicht.

Da event.relatedTargetdas Element hat in , auf die die Maus übergeben, wenn es nicht ein untergeordnetes Element ist, haben Sie des Elements moused aus.

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

Auf Winkel 5, 6 und 7

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

Dann weiter

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');
 }
}
Alan Torrico
quelle
0

Ich überprüfe den Versatz des ursprünglichen Elements, um die Seitenkoordinaten der Elementgrenzen zu erhalten, und stelle dann sicher, dass die Mouseout-Aktion nur ausgelöst wird, wenn das Mouseout außerhalb dieser Grenzen liegt. Schmutzig, aber es funktioniert.

$(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
}
}
Saranicole
quelle
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
});
Michael Vaganov
quelle
Bitte geben Sie eine Erklärung mit Ihrer Antwort.
Sebass van Boxel
0

Wenn Sie dem übergeordneten Element eine CSS-Klasse oder -ID hinzugefügt haben (oder haben), können Sie Folgendes tun:

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

Sachen werden also nur ausgeführt, wenn sich das Ereignis auf dem übergeordneten Div befindet.

Karthik Palaniappan
quelle
0

Ich wollte nur etwas mit dir teilen.
Ich habe es schwer mit ng-mouseenterund ng-mouseleaveEreignissen.

Die Fallstudie:

Ich habe ein schwebendes Navigationsmenü erstellt, das umgeschaltet wird, wenn sich der Cursor über einem Symbol befindet.
Dieses Menü befand sich oben auf jeder Seite.

  • Um das Ein- / Ausblenden im Menü zu handhaben, schalte ich eine Klasse um.
    ng-class="{down: vm.isHover}"
  • Um vm.isHover umzuschalten , verwende ich die ng-Mausereignisse.
    ng-mouseenter="vm.isHover = true"
    ng-mouseleave="vm.isHover = false"

Im Moment war alles in Ordnung und funktionierte wie erwartet.
Die Lösung ist sauber und einfach.

Das eingehende Problem:

In einer bestimmten Ansicht habe ich eine Liste von Elementen.
Ich habe ein Aktionsfeld hinzugefügt, wenn sich der Cursor über einem Element der Liste befindet.
Ich habe den gleichen Code wie oben verwendet, um das Verhalten zu behandeln.

Das Problem:

Ich habe herausgefunden, dass sich ein Cursor im schwebenden Navigationsmenü und auch oben auf einem Element befindet und dass es einen Konflikt untereinander gibt.
Das Aktionsfeld wurde angezeigt und die schwebende Navigation wurde ausgeblendet.

Die Sache ist, dass selbst wenn sich der Cursor über dem schwebenden Navigationsmenü befindet, das Listenelement ng-mouseenter ausgelöst wird.
Es macht für mich keinen Sinn, weil ich eine automatische Unterbrechung der Mausausbreitungsereignisse erwarten würde.
Ich muss sagen, dass ich enttäuscht war und einige Zeit damit verbringe, dieses Problem herauszufinden.

Erste Gedanken:

Ich habe versucht, diese zu verwenden:

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

Ich habe viele ng-Zeigerereignisse (mousemove, mouveover, ...) kombiniert, aber keine hilft mir.

CSS-Lösung:

Ich fand die Lösung mit einer einfachen CSS-Eigenschaft, die ich immer häufiger verwende:

pointer-events: none;

Grundsätzlich benutze ich es so (auf meinen Listenelementen):

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

Mit diesem kniffligen Ereignis werden die ng-mouse-Ereignisse nicht mehr ausgelöst und mein schwebendes Navigationsmenü schließt sich nicht mehr, wenn sich der Cursor darüber und über einem Element aus der Liste befindet.

Um weiter zu gehen:

Wie zu erwarten, funktioniert diese Lösung, aber ich mag sie nicht.
Wir kontrollieren unsere Ereignisse nicht und es ist schlecht.
Außerdem müssen Sie Zugriff auf das vm.isHoverOszilloskop haben, um dies zu erreichen, und es ist möglicherweise nicht möglich oder möglich, aber auf die eine oder andere Weise schmutzig.
Ich könnte eine Geige machen, wenn jemand schauen will.

Trotzdem habe ich keine andere Lösung ...
Es ist eine lange Geschichte und ich kann dir keine Kartoffel geben, also vergib mir bitte, mein Freund.
Wie auch immer, pointer-events: noneist das Leben, also erinnere dich daran.

C0ZEN
quelle