キーボードに入る前に、現代のデバイスには「何かを入力する」ための他の方法があることに注意してください。たとえば、人々は音声認識(特にモバイルデバイスで)や、マウスによるコピー/ペーストを使用します。
したがって、<input>フィールドへの入力を追跡したい場合、キーボードイベントだけでは十分ではありません。<input>フィールドの変更をあらゆる手段で追跡するためのinputという名前の別のイベントがあります。また、それはそのようなタスクにとってより良い選択肢かもしれません。それについては、後の章イベント: change, input, cut, copy, pasteで説明します。
キーボードイベントは、キーボードアクション(仮想キーボードもカウントします)を処理したい場合に使用する必要があります。たとえば、矢印キー↑と↓やホットキー(キーの組み合わせを含む)に反応する場合です。
テストスタンド
キーボードイベントをよりよく理解するために、以下のテストスタンドを使用できます。
テキストフィールドで異なるキーの組み合わせを試してください。
kinput.onkeydown = kinput.onkeyup = kinput.onkeypress = handle;
let lastTime = Date.now();
function handle(e) {
if (form.elements[e.type + 'Ignore'].checked) return;
area.scrollTop = 1e6;
let text = e.type +
' key=' + e.key +
' code=' + e.code +
(e.shiftKey ? ' shiftKey' : '') +
(e.ctrlKey ? ' ctrlKey' : '') +
(e.altKey ? ' altKey' : '') +
(e.metaKey ? ' metaKey' : '') +
(e.repeat ? ' (repeat)' : '') +
"\n";
if (area.value && Date.now() - lastTime > 250) {
area.value += new Array(81).join('-') + '\n';
}
lastTime = Date.now();
area.value += text;
if (form.elements[e.type + 'Stop'].checked) {
e.preventDefault();
}
}#kinput {
font-size: 150%;
box-sizing: border-box;
width: 95%;
}
#area {
width: 95%;
box-sizing: border-box;
height: 250px;
border: 1px solid black;
display: block;
}
form label {
display: inline;
white-space: nowrap;
}<!DOCTYPE HTML>
<html>
<head>
<meta charset="utf-8">
<link rel="stylesheet" href="style.css">
</head>
<body>
<form id="form" onsubmit="return false">
Prevent default for:
<label>
<input type="checkbox" name="keydownStop" value="1"> keydown</label>
<label>
<input type="checkbox" name="keyupStop" value="1"> keyup</label>
<p>
Ignore:
<label>
<input type="checkbox" name="keydownIgnore" value="1"> keydown</label>
<label>
<input type="checkbox" name="keyupIgnore" value="1"> keyup</label>
</p>
<p>Focus on the input field and press a key.</p>
<input type="text" placeholder="Press keys here" id="kinput">
<textarea id="area" readonly></textarea>
<input type="button" value="Clear" onclick="area.value = ''" />
</form>
<script src="script.js"></script>
</body>
</html>keydownとkeyup
keydownイベントはキーが押されたときに発生し、keyupイベントはキーが離されたときに発生します。
event.codeとevent.key
イベントオブジェクトのkeyプロパティを使用すると文字を取得でき、イベントオブジェクトのcodeプロパティを使用すると「物理キーコード」を取得できます。
たとえば、同じキーZはShiftキーと組み合わせて、または単独で押すことができます。これにより、小文字のzと大文字のZという2つの異なる文字が得られます。
event.keyは正確に文字であり、異なる値になります。しかし、event.codeは同じ値になります。
| キー | event.key |
event.code |
|---|---|---|
| Z | z(小文字) |
KeyZ |
| Shift+Z | Z(大文字) |
KeyZ |
ユーザーが異なる言語を使用している場合、別の言語に切り替えると、"Z"の代わりにまったく異なる文字になります。それがevent.keyの値になりますが、event.codeは常に同じ"KeyZ"になります。
すべてのキーには、キーボード上の場所によって異なるコードがあります。キーコードはUI Events code specificationに記述されています。
たとえば
- 文字キーには
"Key<文字>"というコードがあります。例:"KeyA","KeyB"など - 数字キーには
"Digit<数字>"というコードがあります。例:"Digit0","Digit1"など - 特殊キーは名前でコード化されています。例:
"Enter","Backspace","Tab"など
いくつかの普及したキーボードレイアウトがあり、仕様ではそれらのそれぞれにキーコードが提供されています。
その他のコードについては、仕様の英数字セクションを読むか、上記のテストスタンドでキーを押してください。
"KeyZ", "keyZ"ではない明白に見えますが、人々はまだミスをします。
タイプミスを避けてください。keyZではなく、KeyZです。event.code=="keyZ"のようなチェックは機能しません。"Key"の最初の文字は大文字である必要があります。
キーが文字を生成しない場合はどうなりますか?たとえば、ShiftやF1などです。これらのキーの場合、event.keyはevent.codeとほぼ同じです。
| キー | event.key |
event.code |
|---|---|---|
| F1 | F1 |
F1 |
| バックスペース | バックスペース |
バックスペース |
| Shift | Shift |
ShiftRightまたはShiftLeft |
event.codeは、どのキーが押されたかを正確に指定することに注意してください。たとえば、ほとんどのキーボードには、左側と右側に2つのShiftキーがあります。event.codeは、どのキーが押されたかを正確に示し、event.keyはキーの「意味」を担当します: それが何であるか("Shift")。
たとえば、ホットキーを処理したいとします: Ctrl+Z (またはMacの場合はCmd+Z)。ほとんどのテキストエディタは、それに「元に戻す」アクションをフックします。keydownでリスナーを設定し、どのキーが押されているかを確認できます。
ここにはジレンマがあります。このようなリスナーでは、event.keyとevent.codeのどちらの値を確認する必要がありますか?
一方、event.keyの値は文字であり、言語によって変化します。訪問者のOSに複数の言語があり、それらを切り替える場合、同じキーで異なる文字が生成されます。したがって、常に同じであるevent.codeを確認するのが理にかなっています。
このように。
document.addEventListener('keydown', function(event) {
if (event.code == 'KeyZ' && (event.ctrlKey || event.metaKey)) {
alert('Undo!')
}
});
他方、event.codeには問題があります。異なるキーボードレイアウトでは、同じキーで異なる文字になる場合があります。
たとえば、ここに米国レイアウト("QWERTY")とその下のドイツレイアウト("QWERTZ")があります(Wikipediaより)
同じキーの場合、米国レイアウトには"Z"があり、ドイツレイアウトには"Y"があります(文字が入れ替わっています)。
文字通り、ドイツレイアウトを使用している人がYを押すと、event.codeはKeyZと等しくなります。
コードでevent.code == 'KeyZ'をチェックすると、ドイツレイアウトを使用している人がYを押したときに、そのようなテストに合格します。
それは非常に奇妙に聞こえますが、その通りです。仕様では、このような動作を明示的に述べています。
そのため、event.codeは、予期しないレイアウトに対して誤った文字と一致する場合があります。異なるレイアウトの同じ文字が異なる物理キーにマップされ、異なるコードになる可能性があります。幸いなことに、それはいくつかのコード(例: keyA, keyQ, keyZ (これまで見てきたように))でのみ発生し、Shiftなどの特殊キーでは発生しません。 仕様でリストを見つけることができます。
レイアウトに依存する文字を確実に追跡するには、event.keyの方が良い方法かもしれません。
一方、event.codeには、物理的なキーの位置に常に同じままになるという利点があります。したがって、それに依存するホットキーは、言語を切り替えた場合でも正常に機能します。
レイアウトに依存するキーを処理したいですか? その場合は、event.keyを使用するのが良いでしょう。
それとも、言語を切り替えた後でもホットキーを機能させたいですか? その場合は、event.codeの方が良いかもしれません。
自動反復
キーが十分に長い時間押されている場合、キーは「自動反復」を開始します: keydownが何度もトリガーされ、離されたときに最終的にkeyupが発生します。したがって、多くのkeydownと1つのkeyupを持つのが正常です。
自動反復によってトリガーされたイベントの場合、イベントオブジェクトにはevent.repeatプロパティがtrueに設定されています。
デフォルトアクション
キーボードで開始できることがたくさんあるため、デフォルトアクションは異なります。
たとえば
- 文字が画面に表示されます(最も明らかな結果)。
- 文字が削除されます(Deleteキー)。
- ページがスクロールされます(PageDownキー)。
- ブラウザが「ページを保存」ダイアログを開きます(Ctrl+S)。
- …など。
keydownでデフォルトアクションを防止すると、OSベースの特殊キーを除いて、ほとんどのアクションをキャンセルできます。たとえば、Windowsでは、Alt+F4で現在のブラウザウィンドウが閉じられます。そして、JavaScriptでデフォルトアクションを防止しても、それを止めることはできません。
たとえば、以下の<input>は電話番号を想定しているため、数字、+、()、または-以外のキーを受け入れません。
<script>
function checkPhoneKey(key) {
return (key >= '0' && key <= '9') || ['+','(',')','-'].includes(key);
}
</script>
<input onkeydown="return checkPhoneKey(event.key)" placeholder="Phone, please" type="tel">
ここでのonkeydownハンドラーは、checkPhoneKeyを使用して押されたキーをチェックします。それが有効(0..9、または+-()のいずれか)の場合、trueを返し、それ以外の場合はfalseを返します。
上記のように、DOMプロパティまたは属性を使用して割り当てられたイベントハンドラーから返されたfalse値は、デフォルトのアクションを防止するため、テストに合格しないキーの場合、<input>には何も表示されないことがわかります。(返されたtrue値は何も影響しません。falseを返すことのみが重要です)
Backspace, ←, →などの特殊キーが入力で機能しないことに注意してください。これは、厳密なフィルターcheckPhoneKeyの副作用です。これらのキーは、falseを返すようにします。
←, →の矢印キーと、Delete, Backspaceを許可して、フィルターを少し緩和しましょう。
<script>
function checkPhoneKey(key) {
return (key >= '0' && key <= '9') ||
['+','(',')','-','ArrowLeft','ArrowRight','Delete','Backspace'].includes(key);
}
</script>
<input onkeydown="return checkPhoneKey(event.key)" placeholder="Phone, please" type="tel">
これで、矢印と削除が正常に機能します。
キーフィルターがあっても、マウスと右クリック+貼り付けを使用して何でも入力できます。モバイルデバイスには、値を入力するための他の手段があります。したがって、フィルターは100%信頼できるとは限りません。
別の方法は、oninputイベントを追跡することです。これは、変更後にトリガーされます。ここで、新しいinput.valueをチェックし、無効な場合は<input>を変更/強調表示できます。または、両方のイベントハンドラーを一緒に使用することもできます。
レガシー
過去には、keypressイベントと、イベントオブジェクトのkeyCode, charCode, whichプロパティがありました。
それらを扱うときに多くのブラウザの非互換性があったため、仕様の開発者は、それらすべてを廃止し、新しい最新のイベント(この章で上記に説明)を作成する以外に方法がありませんでした。古いコードは、ブラウザがサポートを継続しているため、まだ機能しますが、それらをもう使用する必要はまったくありません。
モバイルキーボード
IME (Input-Method Editor)として正式に知られている仮想/モバイルキーボードを使用する場合、W3C標準では、KeyboardEventのe.keyCodeは229で、e.keyは"Unidentified"である必要があると述べています。
これらのキーボードの中には、矢印キーやバックスペースキーなどの特定のキーを押したときに、e.key、e.code、e.keyCode に正しい値が設定されるものもありますが、保証はありません。そのため、キーボードのロジックがモバイルデバイスで常に動作するとは限りません。
まとめ
キーを押すと、シンボルキーでも、ShiftやCtrlなどの特殊キーでも、常にキーボードイベントが発生します。唯一の例外は、ラップトップのキーボードにあることがあるFnキーです。これはOSよりも低いレベルで実装されていることが多いため、キーボードイベントは発生しません。
キーボードイベント
keydown– キーを押したとき (キーを長く押すと自動的に繰り返される)、keyup– キーを離したとき。
主なキーボードイベントプロパティ
code– 「キーコード」("KeyA"、"ArrowLeft"など)。キーボード上のキーの物理的な位置に固有。key– 文字 ("A"、"a"など)。Escなどの非文字キーの場合、通常はcodeと同じ値を持ちます。
以前は、キーボードイベントがフォームフィールドでのユーザー入力を追跡するために使用されることがありました。しかし、入力はさまざまなソースから来る可能性があるため、信頼性がありません。あらゆる入力 (コピー&ペーストや音声認識を含む) を処理するために、input イベントと change イベントがあります (この章の後半にある「イベント: change, input, cut, copy, paste」で説明します)。
キーボードイベントは、本当にキーボードが必要な場合に使用する必要があります。たとえば、ホットキーや特殊キーに反応する場合などです。
コメント
<code>タグを使用し、複数行の場合は<pre>タグで囲み、10行を超える場合はサンドボックス (plnkr, jsbin, codepen…) を使用してください。