jQueryなしで親の絶対divの子要素をホバーするときにonmouseoutを防ぐ


169

onmouseout絶対位置のdivで関数に問題があります。マウスがdivの子要素を押すと、mouseoutイベントが発生しますが、マウスが親の絶対divの外になるまで発生させたくありません。

mouseoutjqueryなしで子要素にヒットしたときにイベントが発生しないようにするにはどうすればよいですか。

これはイベントのバブリングと関係があることはわかっていますが、これを解決する方法を見つけるのに運がありません。

私はここで同様の投稿を見つけました:子要素によってトリガーされるマウスアウトイベントを無効にする方法?

ただし、そのソリューションはjQueryを使用します。


私はタイムアウトを使用して問題を解決し、子要素のホバー時にそれをクリアすることになりました
John

こんにちは、デモを確認しましたが、右下のパネルの要素を確認しても何も起こりませんか?
ジョン・

1
子要素にマウスオーバーしたときに親イベントでのマウスアウトを防止したい場合は、この答えが見つかりました:javascript mouseover / mouseout
user823527 2012

回答:


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

必要なすべてのCSSとHTMLを使用して、簡単なJsFiddleデモを作成しました。確認してください...

クロスブラウザーサポートの修正済みリンクを編集http://jsfiddle.net/RH3tA/9/

これは直接の親のみをチェックすることに注意してください。親divにネストされた子がある場合は、「元の要素」を探して要素の親をトラバースする必要があります。

ネストされた子の編集

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

そして新しいJSFiddleEDIT更新リンク


1
テストできるように、マウスアウトイベントをフィドルに追加する必要があります。警告または何かがすべきです。
John

申し訳ありませんが、alert( 'MouseOut')があるようですが、私にはうまくいきませんか?JSライブラリオプションなしで実行を実行しました
John

サファリとクロムでテストされていますが、すべてで動作するはずです!あなたのブラウザは何ですか?
Amjad Masad 2011年

2
孫がいるとどうなりますか?または偉大な孫?つまり、あなたはただの子供たちの代わりに、再帰的な解決策を持つことができますか?
Roozbeh15、2012年

20
これは機能しません。mouseleave正解です
コードウィスパー2014年

126

を使用しonmouseleaveます。

または、jQueryで次を使用します mouseleave()

それはあなたが探しているものそのものです。例:

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

または、jQueryで:

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

例はこちらです。


3
onMouseLeave泡にならないので結局使ってしまいました。
Alecz

時間を大幅に節約できました。thnx
npo

mouseleave キャンセルできません。
grecdev

@grecdev、もっと言って!
ベンウィーラー

97

よりシンプルな純粋なCSSソリューションいくつかのケース(IE11以降)で機能場合、子供を次のようpointer-eventsに設定することで削除できます。none

.parent * {
     pointer-events: none;
}

4
鮮やかさ!私の問題を完全に修正しました!
Anna_MediaGirl 14

4
これが最初または少なくとも2番目の答えになるはずです。イベントリスナーの面倒な問題に対応する準備ができていたので、これで問題も完全に修正されました。とても簡単 !
ミカエルV. 2014年5

9
これはマウスアウトの発火を防ぎますが、私にとっては、メニューのポイントであるメニューの選択として実際の子がクリックされるのを防ぎます。
johnbakers

@hellofunkそのため、親にクリックイベントを適用します。必要な正確なコードは実装によって異なりますが、この回答はpointer-eventsプロパティの使用を示唆しています
Zach Saucier

1
このソリューションもブロックするため、エリア内に他のコントローラー要素(編集、削除)がない場合にのみ機能します。
ZoltánSüleOct

28

以下は、以下に基づいたよりエレガントなソリューションです。それは、複数のレベルの子供たちから泡立つ出来事を説明します。また、ブラウザ間の問題についても説明します。

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、これはIE、Safari、Operaで動作していないようです。このスクリプトのクロスブラウザバージョンはありますか?FirefoxとChromeの魅力のように動作します。ありがとう。

1
上記の関数をスクリプトに貼り付けると、DreamweaverとFFは最初の行に "function onMouseOut(this、event){"というエラーをスローします。「this」をパラメータとして指定することは許可されていないようです。入力パラメータから「これ」を省くとうまくいくと思いますが、「わからないことがわからない」ので、よくわかりません。
TARKUS 2013

1
私はまた、同じ行のChromeで同じ問題のポップアップを表示しましたが、this@ GregoryLewisが関数定義の行から除外すると、問題が修正されました。また、クロスブラウザーのサポートを増やすには、次のことを試してください。} else if(window.attachEvent){document.getElementById( 'parent')。attachEvent( "onmouseout"、onMouseOut); }
DWils、2014年

とてもハンサム!古いブラウザのサポートが必要な場合の最良の回答。
BurninLeo

13

私にインスピレーションを与えてくれたAmjad Masadに感謝します。

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
    }

13

onmouseoutの代わりにonmouseleaveを使用してください。

あなたは私たちにあなたの特定のコードを示していないので、それを行う方法をあなたの特定の例で示すことはできません。

しかし、それは非常に簡単です。onmouseoutをonmouseleaveに置き換えるだけです。

それだけです:)だから、シンプルです:)

方法がわからない場合は、次の説明を参照してください。

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

ケーキの平和:)それをお楽しみください:)


これは素晴らしい一言です。同様の状況にある場合は、チェックする価値があります
Chris

12

jQueryを使用している場合は、「mouseleave」関数を使用することもできます。これは、これらすべてを処理します。

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

do_somethingは、マウスがtargetdivまたはその子のいずれかに入ると起動します。do_something_elseは、マウスがtargetdivおよびその子のいずれかから離れたときにのみ起動します。


8

Quirksmodeはあなたが必要とするすべての答えを持っていると思います(異なるブラウザーのバブリング動作とmouseenter / mouseleaveイベント)が、そのイベントバブリングの混乱に対する最も一般的な結論、JQueryまたはMootools(mouseenterねずみイベント。これは、まさにあなたがものです)。

あなたがしたい場合、彼らはそれを行う方法を見て、それを自分で行う
か、次のことができ、カスタム作成だけでイベント部分とMooToolsはの「リーン平均」バージョン(とその依存関係を)。



5

私は非常に簡単な解決策を見つけました、

onmousout = "myfunc()"イベントよりもonmouseleave = "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>

それが役に立てば幸い :)



2

これを処理するには2つの方法があります。

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>

これをonmouseoverではなくonmouseoutで機能させるにはどうすればよいですか?
ジョン

おっとっと。私の悪い。上記のコードですべての「mouseover」の参照を「mouseout」に変更するだけです。それでうまくいくはずです。
セルビー2011年

最初のメソッドの場合、マウスが子要素の上に移動すると、event.targetは常に親要素と一致するため、機能しません
John

2番目の方法でも同じことが起こります。マウスが子要素に入ると、イベントがトリガーされます
John

1

私はこれを魅力のように機能させます:

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

ああ、MyDivタグはこのようなものです:

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

このように、onmouseoutが子供や孫などに行くとき、... 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>

より一般的には、1つのdivだけでなくid="..."、各レイヤーに追加する必要もありません。


1

mouseoutメソッド内でイベントが関連付けられている要素にアクセスできる場合は、を使用contains()してevent.relatedTarget、が子要素であるかどうかを確認できます。

event.relatedTargetマウスが渡された要素と同様に、それが子要素でない場合は、要素からマウスアウトしました。

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

1

Angular 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');
 }
}

0

元の要素のオフセットをチェックして、要素の境界のページ座標を取得し、マウスアウトがこれらの境界の外にあるときにのみマウスアウトアクションがトリガーされることを確認します。汚れていますが動作します。

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

アンサーで説明してください。
Sebass van Boxel、

0

親要素にCSSクラスまたはIDを追加した(または持っている)場合、次のようなことができます。

<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にある場合にのみ、実行されます。


0

私はあなたと何かを共有したかっただけです。とイベントで
苦労しました。 ng-mouseenterng-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人生なので、覚えておいてください。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.