イベント伝播
バブリングとは
一言で言うと「一番内側の要素 → 外側の要素へイベントが伝搬」することです。
DOM(ブラウザ)は以下のように動いています。
*内容は一例です
button をクリック
↓
div に伝わる
↓
body に伝わる
↓
document に伝わる
実際にサンプルコードで確認してみます。
export default function Toolbar() {
return (
<div className="Toolbar" onClick={() => {
alert('You clicked on the toolbar!');
}}>
<button onClick={() => alert('Playing!')}>
Play Movie
</button>
<button onClick={() => alert('Uploading!')}>
Upload Image
</button>
</div>
);
}
このコードでは、div(親)とbutton(子)のどちらもonClickが設定されています。
実際に画面で挙動を確認してみます。
「Play Movie」ボタンをクリックすると
- 「Playing!」のアラートが表示される(
buttonのイベント) - 「You clicked on the toolbar!」のアラートが表示される(
divのイベント)
のような挙動となりました。
この状態のことをbutton → div にイベントがバブリングすると呼びます。
onScroll は、それをアタッチした JSX タグでのみ機能します。
伝播の停止
では次にバブリングさせたくないケースをやってみます。
ReactのonClickやonChangeなどのイベントハンドラには、自動的にイベントオブジェクトが渡されます。
<button onClick={(e) => { ... }}>
このeは「今起きたイベントのすべての情報を持ったオブジェクト」です。
中にはこんな情報などが入っています。
- どの要素がクリックされたか
- マウスの位置
- キー入力の種類
- 「イベントを止める」ための関数
これをeを使用して、e.stopPropagation()というものを記述すると「このイベントはここで止めて、親に伝えるな」という動きをしてくれます。
先ほどの<button>をコンポーネントにして、e.stopPropagation()を組み込んでみます。
function Button({ onClick, children }) {
return (
<button onClick={e => {
e.stopPropagation();
onClick();
}}>
{children}
</button>
);
}
export default function Toolbar() {
return (
<div className="Toolbar" onClick={() => {
alert('You clicked on the toolbar!');
}}>
<Button onClick={() => alert('Playing!')}>
Play Movie
</Button>
<Button onClick={() => alert('Uploading!')}>
Upload Image
</Button>
</div>
);
}
では実際に画面で確認してみます。
実際の動きの流れは以下のようになります(公式ドキュメント流用)
1.Reactが<button>に渡されたonClickハンドラを呼び出す
2. そのハンドラはButtonで定義されており、次のことを行う
-
e.stopPropagation()を呼び出し、イベントがさらにバブリングされるのを防ぐ -
Toolbarコンポーネントから渡されたpropsであるonClick関数を呼び出す
3.その関数はToolbarコンポーネントで定義されており、そのボタン固有のアラートを表示する
4.伝播が停止されたため、親の<div>のonClickハンドラは実行されない

