Cegah onmouseout ketika mengarahkan elemen anak dari div absolut induk TANPA jQuery

169

Saya mengalami masalah dengan onmouseoutfungsi dalam div posisi absolut. Ketika mouse menyentuh elemen anak di div, acara mouseout menyala, tapi saya tidak ingin itu menyala sampai mouse keluar dari induknya, absolut div.

Bagaimana saya bisa mencegah mouseoutacara dari menembak ketika menyentuh elemen anak TANPA jquery.

Saya tahu ini ada hubungannya dengan peristiwa yang menggelegak, tetapi saya tidak beruntung menemukan cara mengatasi ini.

Saya menemukan posting serupa di sini: Bagaimana cara menonaktifkan peristiwa mouseout yang dipicu oleh elemen anak?

Namun solusi itu menggunakan jQuery.

John
sumber
Saya akhirnya memecahkan masalah dengan menggunakan batas waktu dan membersihkannya di atas elemen anak
John
Hai, saya telah melihat demo Anda tetapi ketika saya melihat elemen-elemen di panel kanan bawah tidak ada yang terjadi?
John
1
Ditemukan jawaban ini jika Anda ingin mencegah mouseout pada acara induk ketika Anda mouseover elemen anak: javascript mouseover / mouseout .
user823527
1
Lihat jawaban
craigmichaelmartin

Jawaban:

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

Saya membuat demo JsFiddle cepat, dengan semua CSS dan HTML yang dibutuhkan, lihat ...

Tautan EDIT TETAP untuk dukungan lintas-browser http://jsfiddle.net/RH3tA/9/

Perhatikan bahwa ini hanya memeriksa orang tua langsung, jika div orang tua memiliki anak-anak bersarang maka Anda harus entah bagaimana melintasi elemen orang tua mencari "elemen asli"

Contoh EDIT untuk anak-anak yang bersarang

EDIT Fixed untuk semoga 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;
}

Dan JSFiddle baru , tautan yang diperbarui EDIT

Amjad Masad
sumber
1
Anda harus menambahkan acara mouseout ke biola agar dapat diuji. Peringatan atau sesuatu harus dilakukan.
John
Maaf sepertinya Anda sudah waspada ('MouseOut'), namun itu tidak berfungsi untuk saya? Saya telah menjalankannya tanpa opsi perpustakaan JS
John
Diuji pada safari dan chrome, tetapi harus bekerja pada semuanya! apa browser kamu?
Amjad Masad
2
Bagaimana jika Anda memiliki anak cucu? atau cucu yang hebat? yaitu, bisakah Anda memiliki solusi rekursif alih-alih hanya anak-anak yang ingin meniru?
Roozbeh15
20
Ini tidak berhasil. mouseleaveadalah jawaban yang benar
Code Whisperer
126

Gunakan onmouseleave.

Atau, di jQuery, gunakan mouseleave()

Ini adalah hal yang tepat yang Anda cari. Contoh:

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

atau, di jQuery:

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

contohnya ada di sini .

PHPWannaBePro
sumber
3
onMouseLeaveadalah apa yang akhirnya saya gunakan juga karena tidak muncul.
Alecz
Menyelamatkan saya banyak waktu. thnx
npo
mouseleavetidak bisa dibatalkan
grecdev
@grecdev, katakan lebih banyak!
Ben Wheeler
97

Untuk solusi CSS murni sederhana yang berfungsi dalam beberapa kasus ( IE11 atau yang lebih baru ), orang dapat menghapus anak-anak pointer-eventsdengan mengaturnyanone

.parent * {
     pointer-events: none;
}
Zach Saucier
sumber
4
Cemerlang! Benar-benar baru saja menyelesaikan masalah saya!
Anna_MediaGirl
4
Ini harus menjadi jawaban pertama atau paling tidak kedua, saya siap untuk pergi dengan kerumitan pendengar acara dan ini benar-benar memperbaiki masalah saya juga. Sangat sederhana !
Mickael V.
9
Ini mencegah mouseout dari penembakan, tetapi bagi saya itu juga mencegah anak yang sebenarnya dari diklik sebagai pilihan dalam menu, yang merupakan titik menu.
johnbakers
@hellofunk Itulah sebabnya Anda akan menerapkan acara klik ke induknya. Kode persis yang diperlukan bervariasi tergantung pada implementasi, jawaban ini hanya menyarankan menggunakan pointer-eventsproperti
Zach Saucier
1
ini hanya berfungsi jika Anda tidak memiliki elemen pengontrol lain (edit, hapus) di dalam area tersebut, karena solusi ini juga memblokirnya ..
Zoltán Süle
28

Inilah solusi yang lebih elegan berdasarkan apa yang ada di bawah ini. itu menyumbang peristiwa menggelegak dari lebih dari satu tingkat anak-anak. Ini juga menyumbang masalah lintas-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
sumber
Sam Elie, ini sepertinya tidak dapat bekerja pada IE, Safari atau Opera. Apakah Anda memiliki skrip lintas versi browser? Ini berfungsi seperti pesona di Firefox dan Chrome. Terima kasih.
1
Ketika saya menempelkan fungsi di atas ke dalam skrip saya, Dreamweaver dan FF melempar kesalahan pada baris pertama, "function onMouseOut (this, event) {". Sepertinya Anda tidak diizinkan untuk menempatkan "ini" sebagai parameter. Saya kira itu semacam bekerja ketika saya meninggalkan "ini" dari params input, tetapi saya tidak tahu pasti, karena "Saya tidak tahu apa yang saya tidak tahu".
TARKUS
1
Saya juga memiliki masalah yang sama muncul di Chrome pada baris yang sama, tidak termasuk thisdari garis definisi fungsi @GregoryLewis disebutkan memperbaiki masalah. Juga untuk dukungan lintas browser lainnya, coba ini: if (window.addEventListener) {document.getElementById ('parent'). AddEventListener ('mouseout', onMouseOut, true); } lain jika (window.attachEvent) {document.getElementById ('parent'). attachEvent ("onmouseout", onMouseOut); }
DWIL
Sangat tampan! Jawaban terbaik jika dukungan untuk browser lama diperlukan.
BurninLeo
13

Terima kasih kepada Amjad Masad yang menginspirasi saya.

Saya sudah solusi berikut yang tampaknya berfungsi di IE9, FF dan Chrome dan kodenya cukup singkat (tanpa penutupan kompleks dan melintangkan hal-hal anak):

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

bukannya onmouseout menggunakan onmouseleave.

Anda belum menunjukkan kepada kami kode spesifik Anda sehingga saya tidak dapat menunjukkan kepada Anda pada contoh spesifik Anda bagaimana melakukannya.

Tapi itu sangat sederhana: ganti onmouseout dengan onmouseleave.

Itu saja :) Jadi, sederhana :)

Jika tidak yakin bagaimana melakukannya, lihat penjelasan tentang:

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

Ketenangan kue :) Nikmati saja :)

kavi
sumber
Ini adalah teriakan yang hebat, layak untuk dicoba jika Anda memiliki situasi yang sama
Chris
12

Jika Anda menggunakan jQuery, Anda juga dapat menggunakan fungsi "mouseleave", yang menangani semua ini untuk Anda.

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

do_something akan menyala ketika mouse memasuki targetdiv atau salah satu dari anak-anaknya, do_something_else hanya akan menyala ketika mouse meninggalkan targetdiv dan salah satu dari anak-anaknya.

Jamie Brown
sumber
8

Saya pikir Quirksmode memiliki semua jawaban yang Anda butuhkan (perilaku browser yang berbeda dan aktivitas mouseenter / mouseleave ), tapi saya pikir kesimpulan paling umum untuk kekacauan acara itu adalah penggunaan kerangka kerja seperti JQuery atau Mootools (yang memiliki mouseenter dan acara mouseleave , yang persis seperti apa yang Anda intuisi akan terjadi).

Lihatlah bagaimana mereka melakukannya, jika Anda mau, lakukan sendiri
atau Anda dapat membuat versi Mootools "lean mean" khusus Anda hanya dengan bagian acara (dan ketergantungannya).

Ruben
sumber
7

Mencoba mouseleave()

Contoh:

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

   </div>
</div>

;)

Jean-Philippe
sumber
5

Saya telah menemukan solusi yang sangat sederhana,

cukup gunakan event onmouseleave = "myfunc ()" daripada event onmousout = "myfunc ()"

Dalam kode saya itu berhasil !!

Contoh:

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

Contoh yang sama dengan fungsi 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>

Semoga bermanfaat :)

Arminius
sumber
2

Ada dua cara untuk menangani ini.

1) Periksa event.target hasil dalam panggilan balik Anda untuk melihat apakah itu cocok dengan div orangtua Anda

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) Atau gunakan menangkap acara dan memanggil event.stopPropagation dalam fungsi 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>
selbie
sumber
Bagaimana saya bisa membuat ini berhasil untuk onmouseout bukan onmouseover?
John
Ups. Salahku. Saya hanya mengubah semua referensi "mouseover" menjadi "mouseout" pada kode di atas. Itu harus dilakukan untuk Anda.
selbie
Untuk metode pertama, event.target selalu cocok dengan elemen induk ketika mouse memeriksa elemen anak, jadi itu tidak berfungsi
John
Hal yang sama terjadi pada metode kedua. Ketika mouse memasuki elemen anak itu masih memicu acara
John
1

Saya membuatnya bekerja seperti pesona dengan ini:

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

Ah, dan MyDivtag seperti ini:

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

Dengan cara ini, ketika onmouseout pergi ke anak, cucu, dll ... itu style.display='none' itu tidak dieksekusi; tetapi ketika onmouseout keluar dari MyDiv itu berjalan.

Jadi tidak perlu menghentikan propagasi, gunakan timer, dll ...

Terima kasih atas contohnya, saya dapat membuat kode ini dari mereka.

Semoga ini bisa membantu seseorang.

Juga dapat ditingkatkan seperti ini:

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

Dan kemudian tag DIVs seperti ini:

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

Jadi lebih umum, tidak hanya untuk satu div dan tidak perlu menambahkan id="..."pada setiap layer.

z666zz666z
sumber
1

Jika Anda memiliki akses ke elemen yang dilampirkan acara di dalam mouseoutmetode, Anda dapat menggunakan contains()untuk melihat apakah event.relatedTargetelemen anak atau tidak.

Seperti event.relatedTargetelemen yang dilewati mouse, jika bukan elemen anak, Anda telah keluar dari elemen tersebut.

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

Pada Sudut 5, 6 dan 7

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

Lalu selanjutnya

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

Saya memeriksa offset elemen asli untuk mendapatkan koordinat halaman batas elemen, lalu pastikan tindakan mouseout hanya dipicu saat mouseout keluar dari batas tersebut. Kotor tapi berhasil.

$(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
sumber
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
sumber
Tolong beri penjelasan dengan server Anda.
Sebass van Boxel
0

Jika Anda menambahkan (atau memiliki) kelas CSS atau id ke elemen induk, maka Anda dapat melakukan sesuatu seperti ini:

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

Jadi hal-hal hanya akan dieksekusi ketika acara tersebut pada div induk.

Karthik Palaniappan
sumber
0

Saya hanya ingin berbagi sesuatu dengan Anda.
Saya mengalami kesulitan dengan ng-mouseenterdan ng-mouseleaveacara.

Studi kasus:

Saya membuat menu navigasi mengambang yang beralih ketika kursor berada di atas ikon.
Menu ini ada di atas setiap halaman.

  • Untuk menangani show / hide pada menu, saya beralih kelas.
    ng-class="{down: vm.isHover}"
  • Untuk beralih vm.isHover , saya menggunakan acara ng mouse.
    ng-mouseenter="vm.isHover = true"
    ng-mouseleave="vm.isHover = false"

Untuk saat ini, semuanya baik-baik saja dan berfungsi seperti yang diharapkan.
Solusinya bersih dan sederhana.

Masalah yang masuk:

Dalam tampilan tertentu, saya memiliki daftar elemen.
Saya menambahkan panel tindakan ketika kursor berada di atas elemen daftar.
Saya menggunakan kode yang sama seperti di atas untuk menangani perilaku.

Masalah:

Saya tahu ketika kursor saya ada di menu navigasi mengambang dan juga di atas elemen, ada konflik antara satu sama lain.
Panel aksi muncul dan navigasi mengambang disembunyikan.

Masalahnya adalah bahwa bahkan jika kursor berada di atas menu navigasi mengambang, elemen daftar ng-mouseenter dipicu.
Tidak masuk akal bagi saya, karena saya akan mengharapkan jeda otomatis dari acara propagasi mouse.
Saya harus mengatakan bahwa saya kecewa dan saya meluangkan waktu untuk mencari tahu masalah itu.

Pikiran pertama:

Saya mencoba menggunakan ini:

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

Saya menggabungkan banyak peristiwa pointer pointer (mousemove, mouveover, ...) tetapi tidak ada yang membantu saya.

Solusi CSS:

Saya menemukan solusinya dengan properti css sederhana yang saya gunakan semakin banyak:

pointer-events: none;

Pada dasarnya, saya menggunakannya seperti itu (pada elemen daftar saya):

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

Dengan yang rumit ini, peristiwa ng-mouse tidak akan lagi dipicu dan menu navigasi mengambang saya tidak akan lagi menutup dirinya ketika kursor berada di atasnya dan lebih dari satu elemen dari daftar.

Untuk melangkah lebih jauh:

Seperti yang Anda harapkan, solusi ini berfungsi tetapi saya tidak menyukainya.
Kami tidak mengontrol acara kami dan itu buruk.
Plus, Anda harus memiliki akses ke vm.isHoverruang lingkup untuk mencapai itu dan mungkin tidak mungkin atau tidak mungkin kotor dalam beberapa cara.
Saya bisa membuat biola jika seseorang ingin melihat.

Meskipun demikian, saya tidak punya solusi lain ...
Ceritanya panjang dan saya tidak bisa memberi Anda kentang, jadi tolong maafkan saya teman saya.
Bagaimanapun, pointer-events: noneini adalah kehidupan, jadi ingatlah itu.

C0ZEN
sumber