1

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をドラッグしたくありません。具体的には、特定の伝播チェーンに登録されている最もリーフの要素である で、startDrag1 回だけ処理されるようにしたいと考えています。<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

質問)

ただし、上記が動作することが保証されているかどうかはわかりません...また、可能な限りエレガントであるかどうかもわかりません.

  1. event伝播中に同じオブジェクトがチェーンを通過することが保証されていますか?
  2. オブジェクトは、event別のイベント ディスパッチで再利用されないことが保証されていますか?
  3. オブジェクトはevent、expando プロパティの追加をサポートすることが保証されていますか?
  4. 現在のイベントのディスパッチで同じイベント ハンドラー関数が既に見られているかどうかを検出する、より洗練された方法を考えられますか?

実際のアプリケーション

この SVG 階層を想像してみてください…</p>

<g>
  <rect … />
  <rect … />
  <circle … />
</g>

…グループ内の任意の四角形をドラッグしてグループ全体をドラッグできるようにしたいユーザーと、その上をドラッグして円だけをドラッグできるようにしたいユーザー。

ここで、ほとんどの詳細を処理する一般的なドラッグ ライブラリをミックスします。このライブラリを変更して、ユーザーが<g>と の両方にドラッグ ハンドラーを追加した場合<circle>、円をドラッグするとグループでドラッグが開始されないようにしますが、すべてのイベントの伝播を停止することはmousedownありません (ユーザーが持っている可能性があるため)。望ましい他の目的のための高レベルのハンドラー)。

4

2 に答える 2

0

これは、すべての主要なブラウザーの現在のバージョンで機能する一般的なソリューションです...ただし、IE8 では機能せず、将来のバージョンでの動作が保証されない可能性があります。

// Solution supporting arbitrary number of handlers
function seenFunction(evt,f){
  var s=evt.__seenFuncs;
  if (!s) s=evt.__seenFuncs=[];
  for (var i=s.length;i--;) if (s[i]===f) return true;
  evt.handlers.push(f);
  return false;
}

// Used like so:
function myHandler(evt){
  if (seenHandler(evt,myHandler)) return;
  // ...otherwise, run code normally
}

別の方法として、一般的ではない解決策を示しますが、これはわずかに、しかしおそらくそれほど速くはありません (これも IE8 ではなく、将来的に動作することが保証されていない可能性があります)。

// Implemented per handler; small chance of error with a conflicting name
function myHandler(evt){
  if (evt.__ranMyHandler) return;
  // ...otherwise, run code normally
  evt.__ranMyHandler = true;
}

適切な仕様が提供された別の回答に受け入れを喜んで切り替えます。

于 2012-04-12T14:28:37.003 に答える
-1

これは Firefox で見栄えがよく、jQuery は基本的に と を使用してこれを行うため、おそらく非標準のブラウザーに適応させることができdelegateますlive。と を使用stopPropagationtargetます。どちらも標準です。

var d = document.querySelector('div'),
    b = document.querySelector('button');
d.addEventListener('mousedown',clickPick,false);
d.addEventListener('mousedown',startDrag,false);
function clickPick(evt){
    console.log('click', this.tagName);
}
function startDrag(evt){
    console.log('drag', evt.target.tagName);
    evt.stopPropagation();
}

私にとっては、ボタンをクリックしたときの順序が逆になっています。あなたのものは「ボタンをドラッグ」してから「DIVをクリック」していますが、私のものは反対です。ただ、原文では未定義だと思います。

編集: を使用するように変更されましたtargetsrcElement9 より前の IEは、同じことを意味する非標準のプロパティを使用します。

于 2012-04-11T23:07:10.730 に答える