クリック ・ キー入力 ・ 文字の入力など、画面で起きた出来事をイベントと呼びます。要素.addEventListener("イベント名", 関数) で、起きたときに呼ばれる関数(イベントハンドラ)を登録します。06 章のコールバックと同じ形です。
body の中身
<button id="btn">押した回数: 0</button>
スクリプト
const btn = document.querySelector("#btn");
let count = 0;
btn.addEventListener("click", () => {
count++;
btn.textContent = `押した回数: ${count}`;
console.log("押された", count);
});
btn.click(); // コードから押してみる
btn.click();
押された 1
押された 2
最後の 2 行は、コードからボタンを押しています(click())。画面のボタンを自分で押しても、同じように数が増えます。count は、ハンドラが覚えている変数の箱です(09 章のクロージャ)。
| イベント名 | 起きるとき |
|---|---|
click | クリックされた |
input | 入力欄の中身が 1 文字変わるたび |
change | 選択肢 ・ チェックボックスが変わった、入力欄の確定 |
keydown | キーが押された |
submit | フォームが送信された(17 章) |
DOMContentLoaded | HTML を最後まで読み終えた |
ハンドラには、起きたことの詳しい情報が入ったイベントオブジェクトが渡されます。
| プロパティ | 中身 |
|---|---|
event.type | イベント名 |
event.target | 実際に操作された要素 |
event.currentTarget | ハンドラを登録した要素 |
event.key | 押されたキー(キーのイベント) |
body の中身
<input id="name" placeholder="名前を入れて Enter">
<p id="echo"></p>
スクリプト
const input = document.querySelector("#name");
const echo = document.querySelector("#echo");
input.addEventListener("input", (event) => {
echo.textContent = `こんにちは、${event.target.value} さん`;
});
input.addEventListener("keydown", (event) => {
if (event.key === "Enter") {
console.log("Enter が押された:", input.value);
}
});
// コードから入力してみる
input.value = "ゲスト";
input.dispatchEvent(new Event("input"));
input.dispatchEvent(new KeyboardEvent("keydown", { key: "Enter" }));
Enter が押された: ゲスト
dispatchEvent は、イベントをコードから起こす命令です。画面の入力欄に文字を打っても、同じハンドラが呼ばれます。
要素の上で起きたイベントは、その要素で終わらず、親 → そのまた親 → … と順に伝わっていきます(バブリング)。
body の中身
<div id="outer" class="box">外
<div id="inner" class="box">内
<button id="btn">ボタン</button>
</div>
</div>
CSS
.box { border: 2px solid #7a8699; padding: 8px 12px; margin: 4px; }
スクリプト
for (const id of ["outer", "inner", "btn"]) {
document.querySelector(`#${id}`).addEventListener("click", (event) => {
console.log(`${id} が受け取った(押されたのは ${event.target.id})`);
});
}
document.querySelector("#btn").click();
btn が受け取った(押されたのは btn)
inner が受け取った(押されたのは btn)
outer が受け取った(押されたのは btn)
画面で「内」の文字のあたりを押すと、inner と outer だけが受け取ります。event.target は、どこで受け取っても実際に押された要素です。
伝わる性質を使うと、子の 1 つ 1 つに登録する代わりに、親に 1 つだけ登録して、event.target で誰が押されたかを見分けられます。あとから足した子にも、自動で効きます。
body の中身
<ul id="list">
<li>りんご</li>
<li>みかん</li>
<li>ぶどう</li>
</ul>
CSS
li { cursor: pointer; }
.done { text-decoration: line-through; color: #6b7280; }
スクリプト
const list = document.querySelector("#list");
list.addEventListener("click", (event) => {
const li = event.target.closest("li");
if (!li) return;
li.classList.toggle("done");
console.log(`${li.textContent} → ${li.classList.contains("done") ? "済" : "未"}`);
});
list.children[1].click();
list.children[1].click();
list.children[2].click();
みかん → 済
みかん → 未
ぶどう → 済
リンクを押すとページを移動する、フォームの送信ボタンを押すとページを送り直す、といったブラウザの既定の動きがあります。event.preventDefault() で止められます。
body の中身
<a id="link" href="https://example.com/">外へのリンク</a>
スクリプト
document.querySelector("#link").addEventListener("click", (event) => {
event.preventDefault();
console.log("移動を止めた:", event.currentTarget.href);
});
document.querySelector("#link").click();
移動を止めた: https://example.com/
1 回だけ反応したいときは { once: true } を、登録を外したいときは removeEventListener を使います。
body の中身
<button id="start">はじめる</button>
スクリプト
const start = document.querySelector("#start");
start.addEventListener("click", () => console.log("1 回だけ反応する"), { once: true });
function onClick() {
console.log("外すまで反応する");
}
start.addEventListener("click", onClick);
start.click();
start.removeEventListener("click", onClick);
start.click();
1 回だけ反応する
外すまで反応する
removeEventListener には、登録したときと同じ関数を渡します。その場で書いたアロー関数は、後から外せないので、外す予定があるなら名前を付けておきます。
07 章の決まりどおり、this は呼ばれ方で決まります。
function で書いたハンドラの this は、登録した要素(event.currentTarget と同じ)this は、外側の this。クラスの中で登録すると、インスタンスになるbody の中身
<button id="a">A</button>
<button id="b">B</button>
スクリプト
document.querySelector("#a").addEventListener("click", function () {
console.log("function の this:", this.id);
});
class Counter {
count = 0;
constructor(button) {
button.addEventListener("click", () => this.up());
}
up() {
this.count++;
console.log("count:", this.count);
}
}
new Counter(document.querySelector("#b"));
document.querySelector("#a").click();
document.querySelector("#b").click();
function の this: a
count: 1
注意: クラスの中で button.addEventListener("click", this.up) と書くと、up の中の this はボタンになり、this.count が読めません(08 章の落とし穴)。() => this.up() か this.up.bind(this) で渡します。