3秒でわかる
クリックや入力など「何かが起きたこと」を検知して処理を走らせる仕組み。待ち受け側を登録しておく形で画面の反応を組み立てます。
30秒図解
もう少し詳しく
どういうものか
イベントは、プログラムの外側で起きた出来事を知らせる通知です。ボタンのクリック、キー入力、スクロール、タイマーの満了、通信の完了などがこれにあたります。プログラム側は「この出来事が起きたらこの関数を呼んでほしい」とあらかじめ登録しておき、実際に起きたときに呼び出してもらいます。登録する関数をイベントハンドラ、登録の仕組みをイベントリスナと呼びます。
なぜ必要か
利用者がいつ何をするかは、プログラムには予測できません。「クリックされるまで待つ」処理を自分で書こうとすると、状態を延々と監視し続ける無限ループになり、その間ほかの処理が止まります。イベントの仕組みなら、待っている間は何もせず、起きた瞬間だけ処理が走ります。
具体例
const button = document.querySelector("#save");
button.addEventListener("click", (e) => {
e.preventDefault();
console.log("保存が押された");
});
// 一覧の親に 1 つだけ登録して、子の分をまとめて受ける (イベント委譲)
document.querySelector("#list").addEventListener("click", (e) => {
const item = e.target.closest("li");
if (!item) return;
console.log(item.dataset.id);
});似た用語との違い
イベントは発生してから処理されるまでに 2 つの段階を通ります。外側の要素から内側へ降りるのがキャプチャリング、内側から外側へ戻るのがバブリングです。addEventListener は既定でバブリングの段階で反応します。子で起きたクリックが親でも拾えるのはこのためで、上の委譲はその性質を利用しています。
つまずきやすいところ
同じ要素に addEventListener を 2 回呼ぶと、ハンドラは 2 つ登録され、クリック 1 回で 2 回動きます。画面を再描画するたびに登録している箇所があると、押すたびに実行回数が増えていきます。removeEventListener で外すか、登録は 1 回だけ通る場所に置きます。
stopPropagation の使いすぎも事故のもとです。自分の都合で伝播を止めると、外側にある「画面のどこかを押したらメニューを閉じる」といった処理が動かなくなります。まず preventDefault で足りないかを確かめます。