event オブジェクト
このレッスンで分かること
- イベントの処理には、出来事の情報が入った
eventが第 1 引数で渡されますevent.targetは実際に押された要素で、登録した要素と同じとはかぎりません
押されたことは分かる。どれが押されたかが分からない
前回のボタンは 1 つだけでした。押されたら決まった文字を出せばよかったので、それ以上の情報は要りません。ところが作品の一覧には、いまカードが 5 枚ならんでいます。しかも配列から描いているので、データを足せば 10 枚にも 20 枚にも増えます。押されたかどうかだけでは足りず、どれが押されたかを知る必要があります。
その答えが event オブジェクトです。イベントが起きたとき、ブラウザは処理の関数を呼ぶだけでなく、その出来事に関する情報をまとめたオブジェクトを第 1 引数として渡してくれます。要らなければ書かなくてかまいません。要るときは引数名を 1 つ書くだけです。
JavaScript
const table = document.querySelector(".skill-table");
table.addEventListener("click", function (event) {
console.log(event.type); // "click"
console.log(event.target); // 実際に押された要素
});引数の名前は何でも動きますが、event か e と書くのが慣習です。この教材では event で統一します。自分でつくって渡すものではないので、引数を書き忘れても undefined になるだけで、次の行の event.target ではじめて止まります。
押したのは行なのに、返ってくるのはセル
スキル表のセルを押してみてください。event.target に入っているのは td です。行の tr ではありません。target は、いちばん内側で出来事が起きた要素だからです。
いっぽう event.currentTarget は、addEventListener を書いた相手を指します。.skill-table に登録したなら、どこを押しても .skill-table のままです。
| プロパティ | 何が入るか |
|---|---|
target | 実際に出来事が起きた、いちばん内側の要素 |
currentTarget | addEventListener を付けた側の要素 |
type | イベントの種類の文字列 |
target をそのまま判定に使うと、押した場所で結果が変わってしまいます。見出しを押したのか、説明文を押したのか、余白を押したのかで、3 通りに分かれます。そこで closest をはさみます。
JavaScript
const row = event.target.closest("tr");closest は、その要素から親をたどって、条件に合う最初の要素を返します。td から始めても、その親の tr にたどり着きます。どこを押しても同じ相手にそろえられるので、判定はここから始めます。
あとから増えたカードには、登録が付いていない
カードが 5 枚あるなら、5 枚それぞれに addEventListener を付ければよさそうに見えます。ところが第8章で、カードは配列から JavaScript が描くようになりました。あとから増えたカードには、登録済みの処理は付いていません。増えるたびに登録し直す面倒がついてまわります。
そこで、親にまとめて 1 つだけ登録します。イベント委譲と呼ばれるやり方です。
JavaScript
table.addEventListener("click", function (event) {
const row = event.target.closest("tr");
if (!row) {
return;
}
row.setAttribute("data-picked", "yes");
});クリックは、押された要素から親へ順に伝わっていきます。この伝わり方をバブリングと呼び、イベント委譲はその性質をそのまま使っています。子で起きた出来事が親まで届くから、親で待ち構えていられます。
登録は最初の 1 回だけです。あとから行が何行増えても、押せばそのまま反応します。表の見出しの外や、行と行のすき間を押したときは closest が null を返すので、早期 return で抜けます。100 行の一覧でも登録は 1 回、処理は 1 本です。
よくある間違い
- 引数を書き忘れる —
function () {のまま中でeventを使うとevent is not definedで止まります。丸かっこの中にeventと書き足します closestをはさまずにtargetを使う — 説明文を押したときのevent.targetはpです。その中に見出しは無いので、querySelectorはnullを返し、次の行で止まります。まずclosestで目的の要素まで戻します- 外側を押したときを考えていない — すき間を押すと
closestはnullです。if (!row) return;の 1 行を入れるだけで防げます
課題
- HTML の
#worksの中、.work-listのすぐ下にid="work-message"の空のpを足す - JavaScript で
.work-listにclickを 1 つだけ登録する - 処理の引数に
eventを受け取り、event.target.closest(".work-card")で押されたカードを特定する - カードが見つからなければ早期 return し、見つかれば中の
h3の文字を使って「〇〇 を選びました」と表示する