addEventListener と useCapture
- カテゴリ:
- JavaScript
- コメント数:
- Comments: 0
◆ target のときは useCapture の値は関係なくリスナつけた順
◆ 最近は useCapture に passive とか once も設定できるようになってる
◆ 最近は useCapture に passive とか once も設定できるようになってる
body>div でイベントがあったとき JavaScript のイベントは
ルートの window から document → body → div のようにツリーの葉の部分までいくのが CAPTURING_PHASE
イベントのあった要素 div から body → document → window とルートに戻るのが BUBBLING_PHASE と思ってました
ですが Event の定数をみると
の 3 つがあります
Event.AT_TARGET は名前通りイベントの起きたターゲットのときです
Phase がイベントを起きた要素ってことを表してるだけでそれ以上に意味は無いと思っていたのですが 挙動的にも違いがありました
section>div>span という構造で div に useCapture が false と true のリスナをつけています
useCapture というのは addEventListener の第三引数のことです
span をクリックしたときの よくあるイベントの流れを書いたものです

window から span まで伝わる途中で div の useCapture が true のリスナが実行されます
赤色のときは event.eventPhase が Event.CAPTURING_PHASE です
今回は span をクリックしたので span のときが event.eventPhase は Event.AT_TARGET です
図の緑色のところです
青色の span から window まで戻るときが event.eventPhase が Event.BUBBLING_PHASE になります
このときに div のところで div につけられた useCapture が false のリスナが実行されます
CAPTURING_PHASE では useCapture が true のものが実行され false のものは BUBBLING_PHASE で実行されます
console.log の結果はこうなります

緑色の CAPTURING_PHASE でも BUBBLING_PHASE でもないところでリスナが実行されます
このときは useCapture の true/false 関係なく リスナを設定した順で実行されます
console.log の結果は
useCapture を true にしたからと言って絶対 false のものより先に来るとは限らないので注意が必要です
useCapture 以外に once と passive を true/false で指定できるようになりました
getEventListeners でそれぞれの状態を確認できます
getEventListeners は CommandLine API で JavaScript の標準機能じゃないのでソース中では使えません
devtools で確認してみてください
引数名は useCapture ですがオブジェクトにしたときは capture というキー名になります
一度実行したら自動で remove されます
簡単にいうとタッチでスクロールしたりマウスでスクロールしたりを快適にするためのもの
通常 preventDefault される可能性を考えて JavaScript の処理を待たないと行けないのですが passive event は名前通り受動的なイベントになり preventDefault ができなくなります
preventDefault でスクロールをキャンセルされることがないので JavaScript の処理を待たずにスクロールでき ブラウザは快適にスクロールさせることができます
快適さのためにはスクロールにリスナつけるときはつけておいたほうがよさそうです
ルートの window から document → body → div のようにツリーの葉の部分までいくのが CAPTURING_PHASE
イベントのあった要素 div から body → document → window とルートに戻るのが BUBBLING_PHASE と思ってました
ですが Event の定数をみると
Event.CAPTURING_PHASE
Event.BUBBLING_PHASE
Event.AT_TARGET
Event.BUBBLING_PHASE
Event.AT_TARGET
の 3 つがあります
Event.AT_TARGET は名前通りイベントの起きたターゲットのときです
Phase がイベントを起きた要素ってことを表してるだけでそれ以上に意味は無いと思っていたのですが 挙動的にも違いがありました
AT_TARGET では useCapture 問わずにつけた順
このコードを例にします<section style="background:green;">
section
<div id="div" style="background:blue";>
div
<span style="background:red;">span</span>
</div>
</section>
<script>
div.addEventListener("click", e => console.log("capture false"), false)
div.addEventListener("click", e => console.log("capture true"), true)
</script>
section
<div id="div" style="background:blue";>
div
<span style="background:red;">span</span>
</div>
</section>
<script>
div.addEventListener("click", e => console.log("capture false"), false)
div.addEventListener("click", e => console.log("capture true"), true)
</script>
section>div>span という構造で div に useCapture が false と true のリスナをつけています
useCapture というのは addEventListener の第三引数のことです
CAPTURING_PHASE / BUBBLING_PHASE
まずはこの図をみてくださいspan をクリックしたときの よくあるイベントの流れを書いたものです

window から span まで伝わる途中で div の useCapture が true のリスナが実行されます
赤色のときは event.eventPhase が Event.CAPTURING_PHASE です
今回は span をクリックしたので span のときが event.eventPhase は Event.AT_TARGET です
図の緑色のところです
青色の span から window まで戻るときが event.eventPhase が Event.BUBBLING_PHASE になります
このときに div のところで div につけられた useCapture が false のリスナが実行されます
CAPTURING_PHASE では useCapture が true のものが実行され false のものは BUBBLING_PHASE で実行されます
console.log の結果はこうなります
capture true
capture false
capture false
AT_TARGET
次に div をクリックした場合
緑色の CAPTURING_PHASE でも BUBBLING_PHASE でもないところでリスナが実行されます
このときは useCapture の true/false 関係なく リスナを設定した順で実行されます
console.log の結果は
capture false
capture true
capture true
useCapture を true にしたからと言って絶対 false のものより先に来るとは限らないので注意が必要です
useCapture
最近では 第三引数が true/false の useCapture でなくオブジェクトになっていますuseCapture 以外に once と passive を true/false で指定できるようになりました
getEventListeners でそれぞれの状態を確認できます
window.addEventListener("click", function capture_true(){}, true)
window.addEventListener("click", function passive_and_once(){}, {once:true, passive: true})
window.addEventListener("click", function object_capture_true(){}, {capture: true})
console.log(getEventListeners(window).click)
window.addEventListener("click", function passive_and_once(){}, {once:true, passive: true})
window.addEventListener("click", function object_capture_true(){}, {capture: true})
console.log(getEventListeners(window).click)
[Object, Object, Object]
0: Object
listener: function capture_true()
once: false
passive: false
type: "click"
useCapture: true
1: Object
listener: function passive_and_once()
once: true
passive: true
type: "click"
useCapture: false
2: Object
listener: function object_capture_true(a)
once: false
passive: false
type: "click"
useCapture: true
length: 3
0: Object
listener: function capture_true()
once: false
passive: false
type: "click"
useCapture: true
1: Object
listener: function passive_and_once()
once: true
passive: true
type: "click"
useCapture: false
2: Object
listener: function object_capture_true(a)
once: false
passive: false
type: "click"
useCapture: true
length: 3
getEventListeners は CommandLine API で JavaScript の標準機能じゃないのでソース中では使えません
devtools で確認してみてください
引数名は useCapture ですがオブジェクトにしたときは capture というキー名になります
once
名前通り一度しか実行されません一度実行したら自動で remove されます
window.addEventListener("click", e => console.log("clicked 1"), false)
window.addEventListener("click", e => console.log("clicked 2"), {once: true})
const eve = new CustomEvent("click")
window.dispatchEvent(eve)
window.dispatchEvent(eve)
window.addEventListener("click", e => console.log("clicked 2"), {once: true})
const eve = new CustomEvent("click")
window.dispatchEvent(eve)
window.dispatchEvent(eve)
clicked 1
clicked 2
clicked 1
clicked 2
clicked 1
passive
これはあまり詳しくないのですが passive event という新しい機能があって そのイベントであることをブラウザに伝えるようです簡単にいうとタッチでスクロールしたりマウスでスクロールしたりを快適にするためのもの
通常 preventDefault される可能性を考えて JavaScript の処理を待たないと行けないのですが passive event は名前通り受動的なイベントになり preventDefault ができなくなります
preventDefault でスクロールをキャンセルされることがないので JavaScript の処理を待たずにスクロールでき ブラウザは快適にスクロールさせることができます
快適さのためにはスクロールにリスナつけるときはつけておいたほうがよさそうです