tl;drまとめ
階層内の複数の要素で特定のイベントを処理するように登録されている場合、バブリング中に到達した最初の要素で実行されますが、階層をさらにバブリングすると実行されない (または早期に戻る) 関数を記述します。(イベントの伝播を実際に停止することなく。)
簡単な例
この HTMLを考えると…
<!DOCTYPE html>
<body>
<div><button>click</button></div>
<script>
var d = document.querySelector('div'),
b = document.querySelector('button');
d.addEventListener('mousedown',clickPick,false);
d.addEventListener('mousedown',startDrag,false);
b.addEventListener('mousedown',startDrag,false);
function clickPick(){ console.log('click',this.tagName); }
function startDrag(){ console.log('drag',this.tagName); }
</script>
…ボタンをクリックすると、次の出力が得られます。
ドラッグ BUTTON
クリック DIV
ドラッグ DIV
ただし、divをドラッグしたくありません。具体的には、特定の伝播チェーンに登録されている最もリーフの要素である で、startDrag
1 回だけ処理されるようにしたいと考えています。<button>
「働く」ソリューション
次の JavaScript コードは、IE9、Chrome18、FF11、Safari5、および Opera11 で動作することがテストされています。
function startDrag(evt){
if (seenHandler(evt,startDrag)) return;
console.log('drag',this.tagName);
}
function seenHandler(evt,f){
if (!evt.handlers) evt.handlers=[];
for (var i=evt.handlers.length;i--;) if (evt.handlers[i]==f) return true;
evt.handlers.push(f);
return false;
}
グローバルオブジェクトを使用しても、上記はIE8 では機能しません。バブリング中にwindow.event
同じオブジェクトが再利用されることはありません。event
質問)
ただし、上記が動作することが保証されているかどうかはわかりません...また、可能な限りエレガントであるかどうかもわかりません.
event
伝播中に同じオブジェクトがチェーンを通過することが保証されていますか?- オブジェクトは、
event
別のイベント ディスパッチで再利用されないことが保証されていますか? - オブジェクトは
event
、expando プロパティの追加をサポートすることが保証されていますか? - 現在のイベントのディスパッチで同じイベント ハンドラー関数が既に見られているかどうかを検出する、より洗練された方法を考えられますか?
実際のアプリケーション
この SVG 階層を想像してみてください…</p>
<g>
<rect … />
<rect … />
<circle … />
</g>
…グループ内の任意の四角形をドラッグしてグループ全体をドラッグできるようにしたいユーザーと、その上をドラッグして円だけをドラッグできるようにしたいユーザー。
ここで、ほとんどの詳細を処理する一般的なドラッグ ライブラリをミックスします。このライブラリを変更して、ユーザーが<g>
と の両方にドラッグ ハンドラーを追加した場合<circle>
、円をドラッグするとグループでドラッグが開始されないようにしますが、すべてのイベントの伝播を停止することはmousedown
ありません (ユーザーが持っている可能性があるため)。望ましい他の目的のための高レベルのハンドラー)。