addEventListener の使い分け
addEventListener のクリックイベントを設定するとき、クラス名をすべて取得してすべての要素にイベントを設定するか、document で一つだけ設定し取得した要素によって分岐するか簡単にまとめたものです。
| 方法 | 向いているケース |
|---|---|
| 各要素に addEventListener | 要素数が少ない/静的 |
| document で1つだけ(イベント委譲) | 要素が多い/動的に増える |
クラス名をすべて取得して設定する方法
document.querySelectorAll('.btn').forEach(btn => {
btn.addEventListener('click', () => {
// 処理
});
}); Sample
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<script src="./script.js"></script>
</head>
<body>
<div>
<button class="btn">copy</button>
<pre><code class="target">console.log("Hello, World");</code></pre>
</div>
<div>
<button class="btn">copy</button>
<pre><code class="target">console.log("Hello, Javascript");</code></pre>
</div>
<div>
<button class="btn">copy</button>
<pre><code class="target">console.log("Hello, Python");</code></pre>
</div>
</body>
</html> async function copyText(text) {
if (!text) {
return "コピーする内容がありません。";
}
try {
await navigator.clipboard.writeText(text);
return `コピーしました\n${text}`;
} catch(e) {
console.error(e);
return "コピーに失敗しました。";
}
}
window.addEventListener('DOMContentLoaded', () => {
document.querySelectorAll('.btn').forEach(btn => {
btn.addEventListener('click', async () => {
const target = btn.parentElement.querySelector('.target');
const code = target?.textContent ?? '';
const message = await copyText(code);
alert(message);
});
});
}); document に1つだけ設定(イベント委譲)
document.addEventListener('click', (e) => {
const btn = e.target.closest('.btn');
if (!btn) return;
// 処理
}); container基準で data 属性を解決
「 container基準 」とは、1つの“機能単位の親要素(コンテナ)”を基準にして、その中だけで要素を探して・処理する設計
const block = btn.closest('.copy-block');
const target = block.querySelector('[data-copy-target]'); 「 このボタンが属する塊 」だけを見るため、ほかのブロックに影響しません。
container基準には以下のような利点があります。
- DOM構造変更に強い
- 並び順が変わってもOK
- divが増えてもOK
- 入れ子になってもOK
- 動的追加と相性がいい
- まったく同じ構造を何個追加しても壊れない
- ID管理が不要
- ロジックが直感的
- このボタンの仲間の中から探す
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<script src="./script.js"></script>
</head>
<body>
<div class="copy-block">
<button class="btn" data-copy>copy</button>
<pre><code data-copy-target>console.log("Hello, World");</code></pre>
</div>
<div class="copy-block">
<button class="btn" data-copy>copy</button>
<pre><code data-copy-target>console.log("Hello, Javascript");</code></pre>
</div>
<div class="copy-block">
<button class="btn" data-copy>copy</button>
<pre><code data-copy-target>console.log("Hello, Python");</code></pre>
</div>
</body>
</html> async function copyText(text) {
if (!text) return false;
try {
await navigator.clipboard.writeText(text);
return true;
} catch (e) {
console.error(e);
return false;
}
}
window.addEventListener('DOMContentLoaded', () => {
document.addEventListener('click', async (e) => {
const btn = e.target.closest('.btn');
if (!btn) return;
const block = btn.closest('.copy-block');
const target = block?.querySelector('[data-copy-target]');
const code = target?.textContent ?? '';
const success = await copyText(code);
alert(
success
? `コピーしました\n${code}`
: 'コピーに失敗しました。'
);
});
});