20

イベント バブルの開始に使用された DOM 要素、またはイベント バブルを開始した子の DOM 要素を削除した場合、どのような動作が予想されますか?要素が削除された場合、バブルは継続しますか?

たとえば、テーブルがあり、テーブル セルのクリック イベントを検出したいとします。別の JS が AJAX リクエストを実行しました。このリクエストは、リクエストが完了すると、最終的にテーブルを完全に置き換えます。

テーブルをクリックすると、テーブルが AJAX リクエストの正常な完了に置き換えられた直後に何が起こりますか? クリックイベントがバブリングしていないように見える動作が見られるため、質問しますが、複製するのは困難です。

(イベントをすべての TD にアタッチするのではなく) テーブルの親要素でイベントを監視していますが、時々到達しないようです。

編集:この問題に再び遭遇し、最終的にその根本にたどり着きました。イベントバブリングの問題ではありませんでした。詳細については、以下の私の回答を参照してください。

4

3 に答える 3

24

経験的に:使用しているブラウザによって異なります。IE はイベントをキャンセルし、他のすべて (私が知る限り) はイベントを続行します。以下のテスト ページと説明を参照してください。

理論的には: Andy E の頭は、バブルはツリーの初期状態に基づいている必要があるため、DOM2がイベントを続行する必要があることを示していることを発見しました。したがって、大多数の動作は正しく、ここでは IE が単独で動作します。クェル・サプライズ。

しかし、それがあなたが見ているものに関連しているかどうかは、まったく別の問題です. テーブルの親要素のクリックを監視していますが、テーブルをクリックすると、テーブルを置き換える Ajax 補完を伴う競合状態が発生し、クリックが失われることが非常にまれであると思われます。現在のところ、ブラウザー上の Javascript はシングルスレッドであるため、その競合状態は Javascript インタープリター内に存在することはできません。(ただし、ワーカー スレッドは来ています — うわー!) しかし、理論的には、クリックが発生し、ブラウザーの非 Javascript UI スレッドによってキューに入れられ、その後 ajax が完了して要素を置き換え、キューに入れられた UI イベントが発生する可能性があります。処理され、要素が削除されて親がなくなったため、まったく発生しないか、バブルしません。ブラウザの実装に多く。オープン ソース ブラウザーで表示されている場合は、そのソースを調べて、インタープリターによる処理のために UI イベントをキューに入れることができます。しかし、それは、以下に示すように、イベント ハンドラーのコードで要素を実際に削除することとは別の問題です。

does-bubbling-continue アスペクトの経験的結果:

Chrome 4 および Safari 4 (WebKit など)、Opera 10.51、Firefox 3.6、IE6、IE7、および IE8 でテスト済み。要素を削除したときにイベントをキャンセルしたのは IE だけでした (そして、バージョン間で一貫してキャンセルしました)。DOM0 ハンドラーを使用しているか、より新しいハンドラーを使用しているかは問題ではないようです。

更新: テストでは、IE9 と IE10 はイベントを継続するため、IE の仕様への非準拠は IE8 で停止します。

DOM0 ハンドラを使用したテスト ページ:

<!DOCTYPE HTML>
<html>
<head>
<meta http-equiv="Content-type" content="text/html;charset=UTF-8">
<title>Test Page</title>
<style type='text/css'>
body {
    font-family: sans-serif;
}
#log p {
    margin:     0;
    padding:    0;
}
</style>
<script type='text/javascript'>
window.onload = pageInit;

function pageInit() {
    var parent, child;

    parent = document.getElementById('parent');
    parent.onclick = parentClickDOM0;
    child = document.getElementById('child');
    child.onclick = childClickDOM0;
}

function parentClickDOM0(event) {
    var element;
    event = event || window.event;
    element = event.srcElement || event.target;
    log("Parent click DOM0, target id = " + element.id);
}

function childClickDOM0(event) {
    log("Child click DOM0, removing");
    this.parentNode.removeChild(this);
}

function go() {
}

var write = log;
function log(msg) {
    var log = document.getElementById('log');
    var p = document.createElement('p');
    p.innerHTML = msg;
    log.appendChild(p);
}

</script>
</head>
<body><div>
<div id='parent'><div id='child'>click here</div></div>
<hr>
<div id='log'></div>
</div></body>
</html>

attachEvent/ハンドラを使用してページをテストしaddEventListenerます (プロトタイプ経由):

<!DOCTYPE HTML>
<html>
<head>
<meta http-equiv="Content-type" content="text/html;charset=UTF-8">
<title>Test Page</title>
<style type='text/css'>
body {
    font-family: sans-serif;
}
#log p {
    margin:     0;
    padding:    0;
}
</style>
<script type='text/javascript' src='http://ajax.googleapis.com/ajax/libs/prototype/1.6.1.0/prototype.js'></script>
<script type='text/javascript'>
document.observe('dom:loaded', pageInit);
function pageInit() {
    var parent, child;

    parent = $('parent');
    parent.observe('click', parentClick);
    child = $('child');
    child.observe('click', childClick);
}

function parentClick(event) {
    log("Parent click, target id = " + event.findElement().id);
}

function childClick(event) {
    log("Child click, removing");
    this.remove();
}

function go() {
}

var write = log;
function log(msg) {
    $('log').appendChild(new Element('p').update(msg));
}
</script>
</head>
<body><div>
<div id='parent'><div id='child'>click here</div></div>
<hr>
<div id='log'></div>
</div></body>
</html>
于 2010-04-28T20:39:24.743 に答える
6

この質問を最初に投稿してからかなりの時間が経ちました。TJCrowder の回答は (Andy E の場合と同様に) 非常に有益であり、動作するはずであるとのことでしたが、引き続き問題が発生しました。しばらく脇に置いていましたが、別の Web アプリケーションで同じ問題が発生したため、今日再訪しました。

しばらくいじってみたところ、毎回問題を再現する方法に気づきました(少なくともFF3.6とChrome 8では)。問題は、イベント バブルがキャンセルされたり、DOM 要素が削除されたときに失われたりしたことではありません。代わりに、要素がマウスダウンとマウスアップの間で変更された場合、「クリック」が発生しないという問題があります。

Mozilla Development Networkによると:

ユーザーが要素をクリックすると、クリックイベントが発生します。クリック イベントは、mousedown および mouseup イベントの後に発生します。

そのため、まったく変更されている DOM 要素がある場合、この問題が発生する可能性があります。そして、私はイベントバブルが失われつつあると誤って信じていました. たまたま、頻繁に更新される要素がある場合は、それをより頻繁に表示し (これが私の場合です)、まぐれとして見過ごす可能性が低くなります。

追加のテスト ( jsfiddleの例を参照) は、クリックしてボタンを押したままにして DOM 要素が変更されるのを待ってからボタンを離すと、(jquery ライブ コンテキストで) 観察できることを示しています。

  • 「クリック」イベントが発生しない
  • 最初のノードで「mousedown」イベントが発生します
  • 更新されたノードに対して「mouseup」イベントが発生します

編集: IE8 でテスト済み。IE8 は、最初のノードに対して mousedown を、更新されたノードに対して mouseup を起動します、実際には、更新されたノードをイベント ソースとして使用して「クリック」を起動します。

于 2011-01-05T17:12:11.793 に答える
5

はい、それは伝播し続ける必要があります。targetイベントは、プロパティを除いて、発生したイベントに実際に関連付けられていません。要素を削除すると、イベントを伝播する内部コードは、元の要素が表示されているドキュメントから削除されたことを「認識」してはなりません。

余談ですが、 を使用してremoveChildもすぐに要素が削除されるわけではなく、ドキュメント ツリーから切り離されるだけです。要素への参照がない場合にのみ、要素を削除/ガベージ コレクションする必要があります。したがって、要素がevent.targetプロパティを介して参照され、ガベージ コレクションされる前に再挿入される可能性もあります。 とはいえ、試したことがないのであくまで推測です。


TJ Crowder のコメントにより、簡単な例を作成することにしました。私は両方の点で正しかったです。バブルが発生し、削除されたノードへの参照を を使用して取得できますevent.target

http://jsbin.com/ofese/2/


TJ が発見したように、IE ではそうではありません。しかし、DOM レベル 2 イベントの仕様では、正しい動作として定義されています[私の強調]:.

バブリングとして指定されたイベントは、最初はバブリングしないイベントと同じイベント フローで処理されます。イベントはそのターゲット EventTarget にディスパッチされ、そこで見つかったイベント リスナーがトリガーされます。バブリング イベントは、EventTarget の親チェーンを上方向にたどることで見つかった追加のイベント リスナーをトリガーし、連続する各 EventTarget に登録されているイベント リスナーをチェックします。この上方への伝搬は、ドキュメントを含めて継続します。キャプチャーとして登録された EventListeners は、このフェーズではトリガーされません。イベント ターゲットからツリーの最上部までの EventTargets のチェーンは、イベントの最初のディスパッチの前に決定されます。イベント処理中にツリーに変更が加えられた場合、イベント フローはツリーの初期状態に基づいて処理されます。

于 2010-04-28T20:21:00.907 に答える