キーボードイベントとは何か(keydown / keyup)
keydown は「キーが押された瞬間」、keyup は「キーが離された瞬間」に発火するイベントです。ショートカット、ゲームの操作、フォームの補助などで“押した/離した”のタイミングを正確に扱えます。ここが重要です:keydown はキーを押し続けていると連続で発火します(キーリピート)、keyup は一度だけです。押下中の連打を検知したいか、終了の合図だけ欲しいかで使い分けます。
基本の使い方(登録・判定・既定動作の制御)
ショートカットの基本(Ctrl/⌘+S で保存)
<script>
function save() { console.log("保存しました"); }
document.addEventListener("keydown", (e) => {
const isSave = (e.ctrlKey || e.metaKey) && e.key.toLowerCase() === "s";
if (!isSave) return;
e.preventDefault(); // ブラウザの「ページ保存」を抑止
save();
});
</script>
HTMLここが重要です:ショートカットは e.key と修飾キー(ctrlKey, metaKey, shiftKey, altKey)で判定します。既定動作がある場合は preventDefault を忘れない。
フォーム操作の補助(Enter で送信、Esc でキャンセル)
<form id="f">
<input id="q" placeholder="検索">
<button>送信</button>
</form>
<script>
f.addEventListener("keydown", (e) => {
if (e.key === "Escape") { e.preventDefault(); q.value = ""; }
if (e.key === "Enter") { e.preventDefault(); console.log("検索:", q.value.trim()); }
});
</script>
HTMLここが重要です:Enter の既定動作(フォーム送信)をカスタム処理へ置き換えるときは preventDefault を使います。意図が明確な場面だけに限定しましょう。
キーの識別と詳細(key / code / repeat)
e.key と e.code の違い
<script>
document.addEventListener("keydown", (e) => {
console.log("key:", e.key, "code:", e.code);
});
</script>
HTMLここが重要です:e.key は「入力される文字や意味」(例: “a”, “A”, “Enter”)。e.code は「物理キーの位置」(例: “KeyA”, “ArrowLeft”)。英字のレイアウト差や IME の影響を受けず位置で判定したいなら e.code、文字・記号そのものを判定したいなら e.key を使います。
長押しの検知(repeat)
<script>
document.addEventListener("keydown", (e) => {
if (e.key === "ArrowRight") {
if (e.repeat) { /* 長押し中の繰り返し */ }
else { /* 最初の一発だけ */ }
}
});
</script>
HTMLここが重要です:keydown は長押しで繰り返し発火します。初回だけ処理したいなら e.repeat を見て弾く、連続移動やオートスクロールなら repeat を活用します。
日本語入力(IME)との付き合い方(compositionstart / end)
未確定文字の扱いと Enter 判定の分離
<input id="name" placeholder="名前">
<script>
let composing = false;
name.addEventListener("compositionstart", () => composing = true);
name.addEventListener("compositionend", () => composing = false);
name.addEventListener("keydown", (e) => {
if (e.key === "Enter") {
if (composing) return; // IME確定の Enter はスルー
e.preventDefault();
console.log("確定入力:", name.value.trim());
}
});
</script>
HTMLここが重要です:日本語入力中(composition)はキーイベントの意味が変わります。Enter・Backspace 等の処理は“確定後”に行うのが安全です。compositionend で状態を戻し、判定を分けましょう。
継続入力の設計(ゲーム・ナビゲーションでの押下/解放)
押下中のキー集合で制御する
<div id="box" style="width:120px;height:80px;background:#eee"></div>
<script>
const pressed = new Set();
document.addEventListener("keydown", (e) => {
pressed.add(e.code); // 物理キーで管理
});
document.addEventListener("keyup", (e) => {
pressed.delete(e.code);
});
// ループで状態に応じて動かす
function tick() {
if (pressed.has("ArrowRight")) box.style.transform = "translateX(5px)";
if (pressed.has("ArrowLeft")) box.style.transform = "translateX(-5px)";
requestAnimationFrame(tick);
}
tick();
</script>
HTMLここが重要です:押下中かどうかは keydown/keyup の“集合管理”が堅牢です。判定は e.code を使うとキーレイアウト差に強くなります。
スコープとアクセシビリティ(どこで受けるか、誰がフォーカスか)
document 全体で受ける vs 特定要素だけで受ける
<div id="pane" tabindex="0" style="border:1px solid #ccc">ここで矢印キーを受ける</div>
<script>
pane.addEventListener("keydown", (e) => {
if (e.key.startsWith("Arrow")) console.log("矢印:", e.key);
});
// pane をフォーカス可能にするため tabindex="0" を付ける
</script>
HTMLここが重要です:キーボードイベントは“フォーカスのある要素”に届きます。特定の領域でだけ受けたいなら、tabindex を付けてフォーカス可能にし、その要素にリスナーを付けます。グローバルショートカットは document に付けますが、フォーム入力と衝突しないよう条件分岐を入れます。
よくある落とし穴と回避策
無闇な preventDefault
Enter・Backspace・Space などの既定動作を止めると、ユーザーの期待を裏切りやすいです。明確な意図がある場面のみに限定し、フォーカス状態や入力要素かどうかで条件分岐を入れてください。
key と code の取り違え
文字で判定すべき場面(”a”, “A”, “+”)と位置で判定すべき場面(矢印、ゲーム操作)を混同すると、配列が違うキーボードや IME で誤動作します。目的に合わせて選び分ける癖をつけましょう。
長押しの二重実行
keydown は連続発火します。初回だけ処理したい場合は e.repeat を弾くか、押下状態を Set で管理して“押されたときだけ”の分岐を作ると安定します。
フォーカスの抜け
想定外の要素がフォーカスされているとショートカットが効かないことがあります。必要領域に tabindex を付ける、初期表示で focus() を適切に呼ぶ、document で受ける場合は入力要素のときはスキップするなど、フォーカス設計を意識します。
実践例(検索ボックス、ショートカット、編集モード)
検索ボックス:Enterで検索、Escでクリア(IME対応)
<input id="q" placeholder="検索">
<script>
let composing = false;
q.addEventListener("compositionstart", () => composing = true);
q.addEventListener("compositionend", () => composing = false);
q.addEventListener("keydown", (e) => {
if (e.key === "Escape") { e.preventDefault(); q.value = ""; }
if (e.key === "Enter" && !composing) {
e.preventDefault();
console.log("検索:", q.value.trim());
}
});
</script>
HTMLアプリショートカット:Ctrl/⌘+K でコマンドパレット
<div id="palette" hidden>コマンド...</div>
<script>
document.addEventListener("keydown", (e) => {
const hotkey = (e.ctrlKey || e.metaKey) && e.key.toLowerCase() === "k";
if (!hotkey) return;
e.preventDefault();
palette.hidden = !palette.hidden;
});
</script>
HTML編集モード:Enterで確定、Shift+Enter で改行
<textarea id="note" rows="4"></textarea>
<script>
note.addEventListener("keydown", (e) => {
if (e.key === "Enter" && !e.shiftKey) {
e.preventDefault();
console.log("確定:", note.value.trim());
}
// Shift+Enter は既定動作(改行)を許可
});
</script>
HTMLまとめ
keydown は押下、keyup は解放。keydown は長押しで繰り返し発火し、keyup は一度だけ。キー判定は e.key(意味)と e.code(位置)を目的で使い分け、修飾キーでショートカットを組む。既定動作の制御は preventDefault を必要最小限にし、日本語入力は composition の状態を考慮して Enter 等の判定を分ける。押下中管理は Set+keydown/keyup が堅牢。フォーカスとスコープを正しく設計すれば、初心者でも意図通りで滑らかなキーボードインタラクションを作れます。
