ワテの場合、WEBプログラミングは独学で習得した。
でもまだ知らない手法や技術も多いが。
今のところJavaScript、jQuery、CSSに関してはこんな風にやりたいなあと思った事(WEBデザイン)は自力で出来る(つもりだ)。
さて、そんなワテであるが、マウスのイベントには以下のものが有るのは知っている。
jQueryの公式サイトから引用した。
マウスイベント | 説明 |
.click() | Bind an event handler to the “click” JavaScript event, or trigger that event on an element. |
.contextmenu() | Bind an event handler to the “contextmenu” JavaScript event, or trigger that event on an element. |
.dblclick() | Bind an event handler to the “dblclick” JavaScript event, or trigger that event on an element. |
.hover() | Bind one or two handlers to the matched elements, to be executed when the mouse pointer enters and leaves the elements. |
.mousedown() | Bind an event handler to the “mousedown” JavaScript event, or trigger that event on an element. |
.mouseenter() | Bind an event handler to be fired when the mouse enters an element, or trigger that handler on an element. |
.mouseleave() | Bind an event handler to be fired when the mouse leaves an element, or trigger that handler on an element. |
.mousemove() | Bind an event handler to the “mousemove” JavaScript event, or trigger that event on an element. |
.mouseout() | Bind an event handler to the “mouseout” JavaScript event, or trigger that event on an element. |
.mouseover() | Bind an event handler to the “mouseover” JavaScript event, or trigger that event on an element. |
.mouseup() | Bind an event handler to the “mouseup” JavaScript event, or trigger that event on an element. |
.toggle() |
Bind two or more handlers to the matched elements, to be executed on alternate clicks. Also in: Deprecated > Deprecated 1.8 | Removed |
引用元 https://api.jquery.com/category/events/mouse-events/
沢山あるけれど、名前を見れば大体どんなイベントなのかは見当は付くと思う。
しかしながら、.mouseleave() と .mouseout() と言う似たような奴があるのだ。
昔からその存在は知っていたが、今まで使い分ける必要性がある場面に出くわした事が無かった。
ところか先日、.mouseleave() と .mouseout() を使い分ける必要がある状況になったのだ。
この記事では、.mouseleave() と .mouseout() の違いやそれらを使うべき場面などをワテの経験に基づいて解説したい。
まあ、ワテの備忘録みたいなもんだけれど。
では本題に入ろう。
ワテが遭遇した状況 – マウス右クリックの検出
例えばChromeブラウザーでどこかのサイトを表示している時に、画面上でマウス右クリックした場合にはブラウザーのデフォルトの右クリックメニューが表示される。
でも、ワテの場合は、自前の右クリックメニューを作成したいと思ったので、まずはマウス右クリックメニューを検出するコードを書いた。
<!DOCTYPE html> <html> <head> <meta charset="utf-8" /> <title></title> <script src="Scripts/jquery-3.2.1.js"></script> </head> <body> <style> body { box-sizing: border-box; padding: 0; margin: 0; border: 5px solid red; height: 100vh; overflow: hidden; } </style> <script> //JavaScriptで右クリックメニューを検出 document.body.addEventListener('contextmenu', function (ev) { ev.preventDefault(); alert('1'); //・・・ 必要な処理を書く return false; }, false); //jQueryで右クリックメニューを検出 $(document).on('contextmenu','body', function (e) { e.preventDefault(); //・・・ 必要な処理を書く alert('2'); }); </script> </body> </html>
上のコードでは、JavaScriptとjQueryのそれぞれの例を紹介しているので、実際に使う場合はどちらかのみで良い。
兎に角こんな風にすればJavaScriptやjQueryでbody上でマウスの右クリックが行われたイベントを検出可能だ。
要点は、e.preventDefault();を実行する事。
e.preventDefault();
これでブラウザーのデフォルト動作を抑制できる。つまりデフォルトの右クリックメニューを表示させないように出来る。
それで、あとは自分で表示したいメニューを作って表示すれば良い。
例えばこんなdiv要素を作っておいて、最初はdisplay:noneで非表示にしておく。
<div class="contextMenu" id="contextMenuID"> <ul> <li id="メニュー1" > <a href="#メニュー1">メニュー1</a> </li> <li id="メニュー2" > <a href="#メニュー2">メニュー2</a> </li> <li id="メニュー3" > <a href="#メニュー3">メニュー3</a> </li> </ul> </div>
それで右クリック地点にposition:absoluteで移動してdisplay:blockで表示するなどで自前の右クリックメニューを表示出来る。
div領域からマウスポインターが出たら再びdisplay:noneで非表示にするなどの処理を入れれば、より完成度の高い自前の右クリメニューを作れる。
例えば、こんな感じか。
document.getElementById('contextMenuID').addEventListener('mouseout', function (e) { e.preventDefault(); var elem = document.getElementById('contextMenuID'); elem.style.display = 'none'; return false; }, false);
ワテもそんな風に自前の右クリックメニュー作ってみたのだが、困った問題が起こった。
それは、div領域からマウスが出るイベントの検出にはmouseoutを使っていたのだがこれだと確かにdivからマウスが出ると反応するのだが、div内にある<li>や<a>などの子要素の上にマウスポインターが移動してもmouseoutが反応してしまう。
それだと困る。
あくまで四角いdivの外側にマウスポインターが移動した場合にのみイベントを検出して、その時にdisplay:noneでdivを非表示にしたい。
何か良い方法は無いかなあと思ってググってみた。
そんな時にはStackOverflowを探してみる
そうするとこんな記事を見付けた。
Prevent onmouseout when hovering child element of the parent absolute div WITHOUT jQuery
Attention Required! | Cloudflare
要するにワテが遭遇したのと同じく、子要素の上にマウスが行った場合にonmouseoutが反応するのを回避する方法を探しているという質問だ。それもjQueryを使わない手法で。
その記事を見ると、子要素だけがあり孫要素などは無いケースと、孫要素やそれより下層の要素がある場合の二種類の手法が掲載されていた。
どちらの場合もonmouseoutイベントが発生したら、それが目的のparentNodeなのかchild要素なのかを判定している感じ。
実際に試してみたら期待通りに動くけれどもちょっとややこしい。
でも、その記事をよく読んでいたら、幾つかのコメントが投稿されていて、その中に、
This doesn’t work. mouseleave is the correct answer
って書いてある。
ああそうか、今まではmouseoutしか使っていなかったのだが、こう言う場合にはmouseleaveを使うといいのか!
早速試してみた。
.mouseleave() なら簡単に解決
先ほどと殆ど同じだが、イベントの名前がmouseleaveに変わった。
document.getElementById('contextMenuID').addEventListener('mouseleave', function (e) { e.preventDefault(); var elem = document.getElementById('contextMenuID'); elem.style.display = 'none'; return false; }, false);
これで、期待通りに動く。
つまり、divのcontextMenuIDの外側にマウスポインターが移動した場合にのみ反応する。
contextMenuID内にある子要素、孫要素、、、などの上にマウスポインターが移動しても反応させないように出来る。
これで一件落着なのだが、分かり易くするためにサンプルプログラムを書いてみた。
.mouseleave() と .mouseout()の違いを確認する
下図でchildやparentのdiv領域にマウスポインターを移動してみると良い。
多数の数字が表示されているので分かりにくいと思うが、child長方形とparen長方形の間をマウスポインターを移動してみると分かり易い。
その結果、mouseenter/mouseleaveはカウンターが増えない。つまりイベントが発生していない。
一方、mouseover/mouseoutではカウンターが増える。つまりイベントが発生している。
このデモプログラムで分るように、マウスポインターがparent領域にある限りは、仮にchild領域に入ってもparentのmouseleaveは発生しない。
これや!これがやりたかったんや!
その全ソースコードは以下の通り。
上のサンプルはjQuery版。
ついでにJavaScript版も作ってみた。
なお、JavaScript版では、イベントハンドラ関数の割り当ては以下の様にしている。
document.getElementById('parentID').addEventListener('mouseout', onMouseOutP, false); document.getElementById('parentID').addEventListener('mouseleave', onMouseLeaveP, false);
このaddEventListenerの第三引数に注意が必要だ。
true: Captureフェーズ
false: Targetフェーズとか Bubblingフェーズと呼ばれる
なのだが、trueにすると期待通りに動かなかった。
その辺りの解説は別の記事で紹介したい。
まとめ
JavaScriptやjQueryのマウスイベントに於いて、
- mouseout
- mouseleave
と言う似たようなのがある。
その要素の中に子要素、孫要素などがある場合には両者の使い分けが重要になる。
mouseleaveの場合には、子要素、孫要素が有ってもそれらの上にマウスポインターが移動しても、目的の要素の中にマウスポインターがある限りはmouseleaveイベントは発生しない。
mouseoutの場合には、目的の要素の中にマウスポインターが有っても、その子要素、孫要素などの上にマウスポインターが入ると、mouseoutが発生する。
この二点を覚えておくと良いだろう。
WEBプログラミング関連本を読む
有名な山田さんの本だ。
ワテの場合、JavaScriptよりもTypeScriptを良く使う。理由は、Visual Studioの開発環境でTypeScriptを使うと強力な文法チェック機能や、使い易いインテリセンス機能などがあるので、文法を知らなくてもスイスイと開発が出来るからだ。
コメント