こんにちは。
業務内でlabel
タグの内側にinput
タグが含まれているような要素にクリックイベントを設定しようとした際、クリックイベントが意図しない回数発火していることに起因する問題が発生しました。
問題が起きた事象とその解決策について、備忘録の意味も込めて記載します。
実装コードと発生した問題
下記CodepenのようなHTML構造の、チェックボックスとテキストが一体化した要素を作成しました。
当該labelタグにclickイベントを設定したところ、本来1回のみ発火してほしいclickイベントが2回発火してしまうという問題が発生しました。
(発生した現象のログは下記CodePenから見ることができます)
See the Pen Untitled by n4gi-dev (@N4gi-dev) on CodePen.
複数回発火する現象の原因
大まかな概要としてはclickイベントを設定しているlabel
要素とinput
要素の間でバブリングが発生し、クリックイベントが二重登録されていることが原因でした。
より詳細な理由としてはこれらの記事も参考になりました。
対処法
今回のケースでは解決しない例
labelタグの内側がButton
タグのようなケースではe.preventDefault();
を先に記載することで解決するケースもあります。
const checkLabel = document.querySelector(".label-area");
checkLabel.addEventListener("click", (e)=>{
e.preventDefault();
console.log("clicked");
})
ですが今回のように内側がinputタグの場合、e.preventDefault();
だと本来動作してほしいcheckboxの挙動も防ぐことになってしまいます。
See the Pen preventDefault_input by n4gi-dev (@N4gi-dev) on CodePen.
対処法
今回のようなケースではclickイベントではなくchange
イベントをイベントリスナに登録することで解消できました。
イベントリスナを変更することでconsole上で発火する回数を1回に抑えられています。
See the Pen Untitled by n4gi-dev (@N4gi-dev) on CodePen.
change
イベントはinput要素、select要素、textarea要素においてユーザーが要素の値を変更した際に発火するイベントのため、label要素自体ではイベントが発生しません。
label要素に登録したイベントの子要素であるinputタグで発生する1回のみが親要素に伝播されるため、発火回数を1回に抑えられていると考えられます。
最後に
label要素の内側にinput要素が入っているタイプのHTMLを改修するケースは、一からHTMLを構築するよりは元々システムで組み込まれているケースの方が多いと思います。
似た場面で詰まっている人の助けになれば幸いです。
参考記事