◆ target のときは useCapture の値は関係なくリスナつけた順
◆ 最近は useCapture に passive とか once も設定できるようになってる

body>div でイベントがあったとき JavaScript のイベントは

ルートの window から document → body → div のようにツリーの葉の部分までいくのが CAPTURING_PHASE
イベントのあった要素 div から body → document → window とルートに戻るのが BUBBLING_PHASE と思ってました

ですが Event の定数をみると
Event.CAPTURING_PHASE
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>span という構造で div に useCapture が false と true のリスナをつけています
useCapture というのは addEventListener の第三引数のことです

CAPTURING_PHASE / BUBBLING_PHASE

まずはこの図をみてください
span をクリックしたときの よくあるイベントの流れを書いたものです

event-01

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

AT_TARGET

次に div をクリックした場合

event-02

緑色の CAPTURING_PHASE でも BUBBLING_PHASE でもないところでリスナが実行されます
このときは useCapture の true/false 関係なく リスナを設定した順で実行されます

console.log の結果は
capture false
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)
[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

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)
clicked 1
clicked 2
clicked 1

passive

これはあまり詳しくないのですが passive event という新しい機能があって そのイベントであることをブラウザに伝えるようです

簡単にいうとタッチでスクロールしたりマウスでスクロールしたりを快適にするためのもの
通常 preventDefault される可能性を考えて JavaScript の処理を待たないと行けないのですが passive event は名前通り受動的なイベントになり preventDefault ができなくなります

preventDefault でスクロールをキャンセルされることがないので JavaScript の処理を待たずにスクロールでき ブラウザは快適にスクロールさせることができます
快適さのためにはスクロールにリスナつけるときはつけておいたほうがよさそうです