【JavaScript】 onclick属性とaddEventListenerの関連性
更新日:2022/12/15
DOM要素をクリックした時に処理をおこないたいとき、onclickまたはaddEventListenerで処理内容を登録することができます。
しかしこの二つの方法は何が異なるのか明示されていることが少ない。
そこで今回は、onclickとaddEventListenerの違いについてお伝えします。
DOMイベントのおさらい
次の記事でDOM要素のイベントについて解説していますが、ここで簡単におさらししておきます。
【JavaScript】 addEventListener()の第三引数useCaptureの謎
DOM要素がクリックされると、まずは一番上位の要素から下にたどっていき、クリック要素を探す処理が走ります。
そしてクリックされた要素を探すまでがキャプチャフェーズとよびます。
しかしこれで終わりではなく、今度は下から一番上の要素に戻る処理が走ります。
これをバブルフェーズと呼びます。
このたどる課程で、要素がそれぞれのフェーズに対応した処理関数(イベントハンドラー)を持っているか確認して、持っていたら処理を実行します。
つまり経路上にクリック用の関数が登録されていたら、全て実行されるということです。
また一つの要素に対して、処理関数は複数登録することができます。
onclick属性とaddEventListener()で登録した処理は、上書きされないで両方とも登録されるということです。
onclick属性はバブリングフェーズで実行される
onclick属性とaddEventListenerの関連性をお伝えする前に、onclickで指定した処理はキャプチャフェーズとバブリングフェーズのどちらで実行されるのか確認してみます。
次の簡単なHTMLで確認できます。
HTML
<div onclick="console.log('div要素のonclick')">
<p onclick="console.log('p要素のonclick')">ここをクリック</p>
</div>
onclickがキャプチャフェーズで実行されるなら、次のようにコンソールに出力されるはずです。
div要素のonclick
p要素のonclick
バブリングフェーズなら、次のようになります。
p要素のonclick
div要素のonclick
下のデモで確認してみてください。
デモ
ここをクリック
onclick属性とaddEventListenerの優先度
では次は、onclick属性とaddEventListenerで登録した処理は、どちらが先に実行されるのでしょうか。
onclick属性が先に実行されます。
div要素とp要素にそれぞれにキャプチャとバブリング用のリスナーをを登録して確認してみます。
HTML
<div id="testDiv" onclick="console.log('div要素のonclick')">
<p id="testP" onclick="console.log('p要素のonclick')">ここをクリック</p>
</div>
JavaScript
window.addEventListener( "DOMContentLoaded" , ()=> {
const div = document.getElementById("testDiv");
div.addEventListener("click",()=>console.log("[キャプチャ]div要素のaddEventListener"),true);
div.addEventListener("click",()=>console.log("[バブル]div要素のaddEventListener"),false);
const p = document.getElementById("testP");
p.addEventListener("click",()=>console.log("[キャプチャ]p要素のaddEventListener"),true);
p.addEventListener("click",()=>console.log("[バブル]p要素のaddEventListener"),false);
});
要素をクリックすると、結果は次のようになります。
[キャプチャ]div要素のaddEventListener
[キャプチャ]p要素のaddEventListener
p要素のonclick
[バブル]p要素のaddEventListener
div要素のonclick
[バブル]div要素のaddEventListener
まずはキャプチャフェーズでクリックされた要素を探しながら、リスナーがあったら随時処理されていきます。
クリックされた要素がみつかったら、次はバブリングフェーズです。
onclickとバブリングフェーズ用のリスナーが順番に処理されていきます。
実際に下の要素をクリックして確認してみてください。
デモ
ここをクリック
上の解説と違う!
実は、上のようにならないブラウザがあります。
現行のFirefox(88.0)は、次のようになります。
[キャプチャ]div要素のaddEventListener
p要素のonclick
[キャプチャ]p要素のaddEventListener
[バブル]p要素のaddEventListener
div要素のonclick
[バブル]div要素のaddEventListener
p要素の処理順が、
キャプチャ→onclick→バブリング
ではなくて、
onclick→キャプチャ→バブリング
になっています。
実験的に、p要素内にspanをいれてみます。
spanにはリスナーを追加していません。
HTML
<div id="testDiv" onclick="console.log('div要素のonclick')">
<p id="testP" onclick="console.log('p要素のonclick')"><span>ここをクリック</span></p>
</div>
この結果は次のようになります。
[キャプチャ]div要素のaddEventListener
[キャプチャ]p要素のaddEventListener
p要素のonclick
[バブル]p要素のaddEventListener
div要素のonclick
[バブル]div要素のaddEventListener
つまりFireFoxは、クリックされた要素の処理のみonclickが優先されているようです。
ブラウザによって挙動がことなっているのです。
そのため、onclickとaddEventListenerの処理順に依存したプログラムコードを組むべきではありません。
stopImmediatePropagationでイベント処理を止める
addEventListenerは一つの要素に対して複数のイベントハンドラを登録できます。
しかしときは、addEventListenerの処理を全て無効にしたいケースがあります。
このケースでは、onclick属性内でstopImmediatePropagation()を呼ぶことで、以降のaddEventListenerの処理をスキップできます。
HTML
<div id="test" onclick="console.log('div要素のonclick');arguments[0].stopImmediatePropagation();">
<p onclick="console.log('p要素のonclick')">ここをクリック</p>
</div>
JavaScript
window.addEventListener( "DOMContentLoaded" , ()=> {
const div = document.getElementById("test");
div.addEventListener("click",()=>console.log("div要素のaddEventListener"));
});
結果は次のようになります。
p要素のonclick
div要素のonclick
div.addEventListener()で登録した処理が呼ばれていないことがわかります。
この例では、onclick属性でEventオブジェクトを取得しています。
詳しくは次のページを読んでみてください。
■【JavaScript】 htmlのonclick属性でイベント引数を取得する方法
onclickは必ず処理されるとは限らない
WordPressやその他のCMSを使用する場合、onclickが必ず処理されるとは限りません。
例えばWordPressなどのテーマが、リンククリック時のキャプチャリスナーを組み込んでいるケースがあります。
このリスナーでstopImmediatePropagationを呼び出している場合、Firefox以外のブラウザはonclickが呼び出されません。
Firefoxで動作して安心していても、他のブラウザで動作しないため後で慌ててしまう典型的な例です。
更新日:2022/12/15
関連記事
スポンサーリンク
記事の内容について
こんにちはけーちゃんです。
説明するのって難しいですね。
「なんか言ってることおかしくない?」
たぶん、こんなご意見あると思います。
裏付けを取りながら記事を作成していますが、僕の勘違いだったり、そもそも情報源の内容が間違えていたりで、正確でないことが多いと思います。
そんなときは、ご意見もらえたら嬉しいです。
掲載コードについては事前に動作確認をしていますが、貼り付け後に体裁を整えるなどをした結果動作しないものになっていることがあります。
生暖かい視線でスルーするか、ご指摘ください。
ご意見、ご指摘はこちら。
https://note.affi-sapo-sv.com/info.php
このサイトは、リンクフリーです。大歓迎です。