イベントバブリングとキャプチャの違いは何ですか? 2つのうち、どちらを使用するのがより速く、より良いモデルですか?
イベントが他の要素内の要素で発生し、両方の要素がそのイベントのハンドルを登録した場合、イベントのバブリングとキャプチャはHTML DOM APIでのイベント伝播の2つの方法です。イベント伝播モードは 要素がイベントを受け取る順番 で決定します。
バブリングでは、イベントは最初に最も内側の要素によってキャプチャおよび処理され、次に外側の要素に伝播されます。
キャプチャでは、イベントは最初に最も外側の要素によってキャプチャされ、内側の要素に伝播されます。
キャプチャは「細流化」とも呼ばれ、伝播順序を思い出すのに役立ちます。
トリクルダウン、バブルアップ
昔、Netscapeはイベントキャプチャを提唱しましたが、Microsoftはイベントバブリングを推進しました。どちらもW3C ドキュメントオブジェクトモデルイベント 標準(2000)の一部です。
IE <9では イベントの発生のみ が使用されますが、IE 9以降とすべての主要ブラウザでは両方がサポートされています。一方、複雑なDOMの場合、 イベントバブリングのパフォーマンスが若干低下する可能性があります _です。
addEventListener(type, listener, useCapture)
を使用して、バブリング(デフォルト)モードまたはキャプチャモードのいずれかでイベントハンドラを登録できます。キャプチャモデルを使用するには、3番目の引数をtrue
として渡します。
<div>
<ul>
<li></li>
</ul>
</div>
上記の構造で、クリックイベントがli
要素で発生したと仮定します。
キャプチャモデルでは、イベントは最初にdiv
によって処理され(div
内のクリックイベントハンドラが最初に発生します)、次にul
内、最後にターゲット要素のli
内で処理されます。
バブリングモデルでは、逆のことが起こります。イベントは最初にli
、次にul
、そして最後にdiv
要素によって処理されます。
詳細については、
以下の例で、強調表示されている要素をクリックすると、イベント伝播フローの取得フェーズが最初に発生し、次にバブリングフェーズが発生することがわかります。
var logElement = document.getElementById('log');
function log(msg) {
logElement.innerHTML += ('<p>' + msg + '</p>');
}
function capture() {
log('capture: ' + this.firstChild.nodeValue.trim());
}
function bubble() {
log('bubble: ' + this.firstChild.nodeValue.trim());
}
function clearOutput() {
logElement.innerHTML = "";
}
var divs = document.getElementsByTagName('div');
for (var i = 0; i < divs.length; i++) {
divs[i].addEventListener('click', capture, true);
divs[i].addEventListener('click', bubble, false);
}
var clearButton = document.getElementById('clear');
clearButton.addEventListener('click', clearOutput);
p {
line-height: 0;
}
div {
display:inline-block;
padding: 5px;
background: #fff;
border: 1px solid #aaa;
cursor: pointer;
}
div:hover {
border: 1px solid #faa;
background: #fdd;
}
<div>1
<div>2
<div>3
<div>4
<div>5</div>
</div>
</div>
</div>
</div>
<button id="clear">clear output</button>
<section id="log"></section>
説明:
quirksmode.org には、これに関する素晴らしい説明があります。一言で言えば(quirksmodeからコピー):
イベントキャプチャ
イベントキャプチャを使用するとき
| | ---------------| |----------------- | element1 | | | | -----------| |----------- | | |element2 \ / | | | ------------------------- | | Event CAPTURING | -----------------------------------
element1のイベントハンドラが最初に起動し、element2のイベントハンドラが最後に起動します。
イベントバブリング
イベントバブリングを利用する場合
/ \ ---------------| |----------------- | element1 | | | | -----------| |----------- | | |element2 | | | | | ------------------------- | | Event BUBBLING | -----------------------------------
element2のイベントハンドラが最初に起動し、element1のイベントハンドラが最後に起動します。
何を使うの?
それはあなたがやりたいことによります。それ以上はありません。違いは、イベントハンドラの実行順序です。ほとんどの場合、バブリングフェーズでイベントハンドラを起動するのは問題ありませんが、それより早く起動する必要がある場合もあります。
要素1と要素2の2つの要素がある場合、要素2は要素1の内側にあり、両方の要素を持つイベントハンドラをアタッチします。ここで要素2をクリックすると、両方の要素に対するeventHandlerが実行されます。ここで問題は、イベントが実行される順序にあります。要素1に添付されたイベントが最初に実行される場合、それはイベント取得と呼ばれ、要素2に添付されたイベントが最初に実行される場合、これはイベントバブリングと呼ばれます。 W3Cによると、イベントはターゲットに到達するまでキャプチャ段階で開始され、要素に戻ってきてからバブリングを開始します。
キャプチャ状態とバブリング状態は、addEventListenerメソッドのuseCaptureパラメータによって認識されます。
eventTarget.addEventListener(type、listener、[、useCapture]);
デフォルトではuseCaptureはfalseです。それはバブリング段階にあることを意味します。
var div1 = document.querySelector("#div1");
var div2 = document.querySelector("#div2");
div1.addEventListener("click", function (event) {
alert("you clicked on div 1");
}, true);
div2.addEventListener("click", function (event) {
alert("you clicked on div 2");
}, false);
#div1{
background-color:red;
padding: 24px;
}
#div2{
background-color:green;
}
<div id="div1">
div 1
<div id="div2">
div 2
</div>
</div>
Trueとfalseを変えてみてください。
私はこの javascript.info のチュートリアルをこのトピックを説明する上で非常に明確であることがわかりました。そして最後の3点の要約は、本当に重要な点について語っています。ここに引用します。
- イベントは最初に最も深いターゲットまでキャプチャされ、次にバブルアップされます。 IE <9では、それらは泡立つだけです。
- 最後の引数がtrueのaddEventListenerを除き、すべてのハンドラがバブリングステージで動作します。これが、キャプチャステージでイベントをキャッチする唯一の方法です。
- 他のブラウザでは、event.cancelBubble = true(IE)またはevent.stopPropagation()でバブリング/キャプチャを停止できます。
Event.eventPhase
プロパティもあります。これは、イベントがターゲットに到達しているのか、それとも他の場所から来ているのかを知らせます。
ブラウザの互換性はまだ決まっていないことに注意してください。 Chrome(66.0.3359.181)とFirefox(59.0.3)でテストしましたが、そこでサポートされています。
すでに 受け入れられた答えからの抜粋を拡張した 、これはeventPhase
プロパティを使った出力です。
var logElement = document.getElementById('log');
function log(msg) {
if (logElement.innerHTML == "<p>No logs</p>")
logElement.innerHTML = "";
logElement.innerHTML += ('<p>' + msg + '</p>');
}
function humanizeEvent(eventPhase){
switch(eventPhase){
case 1: //Event.CAPTURING_PHASE
return "Event is being propagated through the target's ancestor objects";
case 2: //Event.AT_TARGET
return "The event has arrived at the event's target";
case 3: //Event.BUBBLING_PHASE
return "The event is propagating back up through the target's ancestors in reverse order";
}
}
function capture(e) {
log('capture: ' + this.firstChild.nodeValue.trim() + "; " +
humanizeEvent(e.eventPhase));
}
function bubble(e) {
log('bubble: ' + this.firstChild.nodeValue.trim() + "; " +
humanizeEvent(e.eventPhase));
}
var divs = document.getElementsByTagName('div');
for (var i = 0; i < divs.length; i++) {
divs[i].addEventListener('click', capture, true);
divs[i].addEventListener('click', bubble, false);
}
p {
line-height: 0;
}
div {
display:inline-block;
padding: 5px;
background: #fff;
border: 1px solid #aaa;
cursor: pointer;
}
div:hover {
border: 1px solid #faa;
background: #fdd;
}
<div>1
<div>2
<div>3
<div>4
<div>5</div>
</div>
</div>
</div>
</div>
<button onclick="document.getElementById('log').innerHTML = '<p>No logs</p>';">Clear logs</button>
<section id="log"></section>
バブリング
Event propagate to the Upto root element is Bubbling.
キャプチャ
Event propogate from body(root) element to eventTriggered Element is Capturing.