図に示すように、ページに2つのブロックがあり、一方が他方にネストされていると想像してください。 ページレイアウトでは、次のようになります。
<div id="block_outer"> <div id="block_inner"></div> </div>
ここで、onClickOuterイベントが#block_outerブロックに、onClickInnerイベントが#block_innerブロックにそれぞれ関連付けられているとします。 そして、#block_innerブロックをクリックしたときにonClickOuterイベントが発生しないことを確認する方法についての質問に答えてください。 そして、それはまったく呼び出されますか? もしそうなら、イベントはどの順序でトリガーされますか? また、jQuery.liveメソッドなどが他のライブラリでどのように機能するかを知っていますか(たとえば、ExtJSでのイベント委任)。
ちょっとした歴史
文明の夜明け、恐竜が地球を駆け巡り、アンティークのITワーカーが石彫りのスマートフォンを使用したとき、ブラウザ戦争が本格的に進行し、Webページ上のイベントの動作に関するMSとNetscapeの意見が分かれました(幸いなことに、年齢のために直面する必要はありませんでした)その当時に戻って)。 ページ上の要素をネストする場合(上記の例のように)、MSはイベントバブリングモデルを提案しました。つまり、イベントの順序はDOMツリーの構造を上回っています(「グル」)。 Netscapeは、イベントキャプチャと呼ばれる反対のモデルを提案しました。このモデルでは、イベント処理がDOMツリーを下って要素を「キャプチャ」します。
W3Cは両方のオプションを組み合わせようとしました- 標準では、プログラマーはメソッドの3番目のパラメーターを使用してページ上のイベントの動作を設定できます
addEventListener(type, listener, useCapture)
つまり、クリックすると、「下降」フェーズが最初に発生し、useCapture = trueフラグに関連付けられたイベントがトリガーされ、次に「上昇」フェーズがトリガーされ、DOMツリーに沿って昇順で他のイベントがトリガーされます。 デフォルトでは、イベントは常にバブリングフェーズにサブスクライブします。つまり、useCapture = falseイベントにサブスクライブする次のメソッドを使用します。
elementNode.onclick = someMethod;
今日のブラウザはどのように機能しますか?
addEventListenerメソッドは、バージョン9より前のIEには存在しません。 このために、3番目の引数を持たないattachEventが使用されます。つまり、イベントは常にIEで「グル」と鳴ります。このブラウザについて以下で説明するものの多くは意味がありません。 他のすべてのブラウザーは、2000年からの仕様に従って逸脱することなくaddEventListenerを実装します。
上記を要約するために、イベントの優先度を制御する方法を示す簡単なテストを作成しましょう。
- HTML構造:
<div id="level1"> <div id="level2"> <div id="level3"> </div> </div> </div>
- スクリプト
// using jQuery; jQuery.fn.addEvent = function(type, listener, useCapture) { var self = this.get(0); if (self.addEventListener) { self.addEventListener(type, listener, useCapture); } else if (self.attachEvent) { self.attachEvent('on' + type, listener); } }
var EventsFactory = function(logBox){ this.createEvent = function(text){ return function(e){ logBox.append(text + ' '); } } } var factory = new EventsFactory( $('#test') ); $('#level1').addEvent('click', factory.createEvent(1), true); $('#level1').addEvent('click', factory.createEvent(1), false); $('#level2').addEvent('click', factory.createEvent(2), true); $('#level3').addEvent('click', factory.createEvent(3), false);
- デモ
ブロック#level3をクリックすると、番号が次の順序で表示されます。
1 2 3 1
つまり、ブロック#level1および#level2はキャプチャ段階にサブスクライブされ、#level3および#level1(一度署名される)はバブリング段階にサブスクライブされます。 キャプチャフェーズが最初に呼び出され、ツリーを下る、最初のフェーズは#level1、#level2、#level3要素の行自体が適切です。次に、DOMに沿ってレイズするとき、#level1要素の行が再び適切です。 Internet Explorerには次が表示されます。
3 2 1 1
次のイベントを停止する方法は?
バインドされたイベントはいずれも、次の要素の走査を停止する場合があります。
function someMethod(e) { if (!e) { window.event.cancelBubble = true; } else if (e.stopPropagation) { e.stopPropagation(); } }
W3Cモデルでは、イベントオブジェクトのstopPropagationメソッドについて説明していますが、Microsoftもここで異なっているため、IEの場合はevent.cancelBubbleフィールドを参照する必要があります。
イベントターゲット
ご存じのように、イベントをトリガーしたページ要素を特定することができます。 イベントオブジェクトには、開始要素を参照するターゲットフィールドがあります。 これは例を使用して簡単に表示できます。
- HTML構造:
<div id="level1"> <div id="level2"> <div id="level3"> </div> </div> </div>
- スクリプト
$('#level1').addEvent('click', function(e) { // MSIE "features" var target = e.target ? e.target : e.srcElement; if ( $(target).is('#level3') ) { $('#test').append('#level3 clicked'); } }, false);
- デモ
ここで何が起こっているのかを説明しましょう-#level1内でクリックすることでイベントターゲットをチェックし、イニシエーターが内部ブロック#level3である場合、コードを実行します。 この実装は何かを思い出させますか? これがjQuery.liveの仕組みです。要素がページに存在しないが、将来表示される場合でも、イベントをそれにバインドできます。 バブリング段階では、イベントはドキュメントレベルに到達します。ドキュメントレベルはページ上のすべての要素の共通の親であり、event.targetに応じて特定の機能をトリガーする場合とトリガーしない場合があるイベントをアタッチできます。
そして、ここで疑問が生じます:jQuery.liveがドキュメントレベルでイベントをバブリングフェーズにバインドする場合、前のイベントは呼び出しチェーンを停止し、最後のイベントの呼び出しを中断することができますか? はい、これはそうです。これの前に実行されるイベントの1つがevent.stopPropagation()を呼び出すと、呼び出しチェーンが中断されます。 以下に例を示します。
- HTML構造:
<div id="level1"> <div id="level2"> <div id="level3"> </div> </div> </div>
- スクリプト
$('#level1').live('click', function(e){ $('#test').append('#level1 live triggered'); }); $('#level2').bind('click', function(e){ $('#test').append('this will break live event'); if (e.stopPropagation) { e.stopPropagation(); } });
- デモ
エリア#レベル3をクリックすると、「これはライブイベントを中断します」が表示されます。つまり、ライブイベントは実行されません。 このようなハッキングは可能であり、何かの美しい実装である可能性があり、時にはそれが難しい(地獄のように難しい)知覚可能な間違いである可能性があることに注意してください。
上記の例では、変数「e」がjQuery.Eventインスタンスであることに注意することも重要です。 IEの場合、イベントにはstopPropagationメソッドがありません。IEでバブリングを停止するには、event.cancelBubble = trueフラグを設定する必要があります。 しかし、jQueryはこの問題をエレガントに解決し、このメソッドを独自のものに置き換えます。
さまざまなJSライブラリはイベントとどのように連携しますか?
この時点で、イベントを処理できるライブラリがたくさんあることを予約しますが、残念なことに、記事と著者の知識はゴムではないため、jQuery、MooTools、Dojo、ExtJSのみを検討します。 したがって、ファンは言語とフレームワークについて議論するために、あなたに合格するようお願いします。
- jQuery
bindを介してイベントを操作する方法を知っています。これは、イベントを常にバブルフェーズにバインドしますが、このハンドラーのイベントチェーンを停止する3番目のパラメーター「preventBubble」を持っています。 click 、 changeなどのラッパーや、イベントを委任する方法: delegate 、 liveもあります。 - MooTools
カスタムイベントを処理できるaddEventを通してイベントを扱う方法を知っています 。 AddEventでは、処理段階を指定することもできません。 pseude:relayを使用して、イベントの委任を操作できます。 - 道場
connect (「dontFix」パラメーターが指定されている場合はイベントのチェーンを停止することもできます)またはbehaviorを使用します。 委任には、 デリゲートメソッドを使用できます。 - Extjs
私の意見では、イベントを操作するための最も簡単なインターフェイスを提供します。 onメソッドは、たとえば、delay、stopPropagation、デリゲート、または独自の引数など、オブジェクトの形式でパラメータを渡すことができます。
ご覧のとおり、これらのライブラリはすべてブラウザ間の互換性を損ない、どこでもイベントバブリングを使用し、イベントを処理するための同様の機能を提供します。 しかし、それが内部からどのように機能するかを理解することは決して害になりません:)