2026.09.23 泊まれる場所4,223 立ち寄りスポット59,693 営業中を確認したキャンプ場1,005 note を読む

JavaScriptのイベントバブリングとは何か|子要素をクリックしたら、親要素のクリックイベントまで一緒に発火していた

クリックしたのは子要素だけなのに、なぜか親要素のイベントまで動いてしまう。
原因は、要素から要素へイベントが伝わる「バブリング」という仕組みだった。

モーダルを閉じるボタンを作っていたときだった。
背景をクリックしたらモーダルが閉じる仕様にしたくて、背景の要素にクリックイベントを仕込んだ。
ところが、モーダルの中の普通のボタンを押しただけなのに、背景のクリックイベントまで一緒に動いてしまった。

クリックしたのは子要素なのに、親要素まで反応していた

原因を調べて知ったのが「イベントバブリング」という仕組みだった。
ある要素でクリックなどのイベントが発生すると、そのイベントはまずクリックされた要素自身で処理され、そのあと親要素、さらにその親、というように外側へ向かって伝わっていく。
僕のモーダルで言えば、ボタン(子)をクリックしたイベントが、そのまま背景(親)にも伝わって、背景のクリックイベントまで発火していた、ということだった。

伝播を止める、たった1行

対処はシンプルで、ボタンのクリックイベントの中で event.stopPropagation() を呼ぶだけだった。
このメソッドを呼ぶと、イベントがそこから先の親要素へ伝わるのを止められる。
ボタンのクリック処理の先頭に1行足しただけで、背景のクリックイベントは発火しなくなった。

止めてよい場面と、止めるとまずい場面

ただ、何でもかんでも stopPropagation() で止めればいいわけではない、ということも分かってきた。
例えばサイト全体でクリック位置を計測しているような仕組みがある場合、途中でイベントを止めてしまうと、その計測まで届かなくなる。
自分が今触っている要素の都合だけで止めてしまうと、別の場所で動いているはずの処理が静かに動かなくなる、ということが起こり得る。
モーダルのケースでは、他に伝播を利用している処理が無いことを確認してから使った。

バブリングのおかげで動いている処理もある

逆に、バブリングの仕組みをそのまま利用して書かれているコードも多い。
一覧の行をクリックしたら詳細を開く、というような処理を、行ひとつひとつにイベントを付けるのではなく、一覧全体(親要素)に1つだけイベントを付けて、どの子要素がクリックされたかを event.target で調べて判定する、という書き方がある。
これはイベントデリゲーションと呼ばれていて、行の数があとから増えても親要素のイベントは1つのままでいい、という利点がある。
バブリングは厄介者というより、意図して使えば便利な仕組みだったのだと、ここでようやく腑に落ちた。

意図しない発火を見つけたら、まず伝わり方を疑う

ボタンを押しただけなのに別の場所が反応する、というバグに当たったら、まず子要素から親要素へイベントが伝わっていないかを疑うようにしている。
止めるべきか、むしろ利用すべきかは場面によって変わるので、仕組みを知らないまま触ると、どちらの判断もできないままになってしまう。

まる子パパ

まる子パパ

会社員。受託開発のエンジニアを15年以上続けていて、その前は寿司職人・長距離運転手・農業。 技術ブログは、キャンプの合間に踏んだバグと、AI・Web開発の運用の失敗を書いています。 このサイト自体が、開発に携わっているCMS「コンテナ」の稼働中の実例です。 このサイトについて / 小さな自動化の相談

—

ほかにも書いています

受託・note・スタンプ

※受託の相談とnoteは、運営者が個人で受けている・書いているものです。

技術ブログ一覧へ戻る