在我們進入鍵盤之前,請注意在現代裝置上還有其他「輸入內容」的方法。例如,人們使用語音辨識(特別是在行動裝置上)或使用滑鼠複製/貼上。
因此,如果我們想要追蹤輸入到 <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
。
event.key
正好是字元,而且會不同。但 event.code
是相同的。
按鍵 | event.key |
event.code |
---|---|---|
Z | z (小寫) |
KeyZ |
Shift+Z | Z (大寫) |
KeyZ |
如果使用者使用不同的語言,切換到另一種語言會產生與 "Z"
完全不同的字元。這將成為 event.key
的值,而 event.code
始終相同:"KeyZ"
。
"KeyZ"
,而不是 "keyZ"
這似乎很明顯,但人們還是會犯錯。
請避免錯字:是 KeyZ
,而不是 keyZ
。像 event.code=="keyZ"
這樣的檢查無法運作:"Key"
的第一個字母必須是大寫。
如果一個按鍵沒有提供任何字元,該怎麼辦?例如,Shift 或 F1 或其他。對於這些按鍵,event.key
大約等於 event.code
按鍵 | event.key |
event.code |
---|---|---|
F1 | F1 |
F1 |
Backspace | Backspace |
Backspace |
Shift | Shift |
ShiftRight 或 ShiftLeft |
請注意,event.code
精確指定按下的按鍵。例如,大多數鍵盤都有兩個 Shift 按鍵:一個在左側,一個在右側。event.code
告訴我們確切按下哪一個,而 event.key
負責按鍵的「意義」:它是什麼(一個「Shift」)。
假設我們想要處理一個熱鍵:Ctrl+Z(或 Mac 的 Cmd+Z)。大多數文字編輯器都會在上面掛接「復原」動作。我們可以在 keydown
上設定一個監聽器,並檢查按下哪個按鍵。
這裡有一個兩難:在這樣的監聽器中,我們應該檢查 event.key
或 event.code
的值嗎?
一方面,event.key
的值是一個字元,它會根據語言而改變。如果訪客在作業系統中有多種語言並在它們之間切換,則相同的按鍵會提供不同的字元。因此檢查 event.code
是有道理的,它始終相同。
像這樣
document.addEventListener('keydown', function(event) {
if (event.code == 'KeyZ' && (event.ctrlKey || event.metaKey)) {
alert('Undo!')
}
});
另一方面,event.code
有問題。對於不同的鍵盤配置,相同的按鍵可能具有不同的字元。
例如,以下是美國配置(「QWERTY」)和其下方的德語配置(「QWERTZ」)(來自維基百科)
對於相同的按鍵,美國配置有「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
和一個 keyup
是很正常的。
對於由自動重複觸發的事件,事件物件會將 event.repeat
屬性設定為 true
。
預設動作
預設動作會有所不同,因為鍵盤可以啟動許多可能的動作。
例如
- 螢幕上會出現一個字元(最明顯的結果)。
- 刪除一個字元(Delete 鍵)。
- 捲動頁面(PageDown 鍵)。
- 瀏覽器開啟「儲存頁面」對話方塊(Ctrl+S)
- …等等。
除了作業系統的特殊鍵之外,在 keydown
上防止預設動作可以取消大部分的動作。例如,在 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
。
正如我們所知,事件處理常式傳回的 false
值會防止預設動作,例如上面使用 DOM 屬性或屬性指定的方式,因此對於未通過測試的按鍵,<input>
中不會出現任何內容。(傳回的 true
值不會影響任何內容,只有傳回 false
才會有影響)
請注意,特殊鍵,例如 Backspace、Left、Right,在輸入時不會運作。這是嚴格篩選器 checkPhoneKey
的副作用。這些按鍵會讓它傳回 false
。
讓我們放寬篩選器,允許使用箭頭鍵 Left、Right 和 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))時,W3C 標準指出 KeyboardEvent 的 e.keyCode
應該是 229
,而 e.key
應該是 "Unidentified"
。
雖然其中一些鍵盤在按下特定按鍵(例如箭頭或退格鍵)時,仍可能對 e.key
、e.code
、e.keyCode
… 使用正確的值,但並無保證,因此您的鍵盤邏輯可能無法在行動裝置上正常運作。
摘要
按下按鍵總是會產生鍵盤事件,無論是符號鍵或特殊鍵,例如 Shift 或 Ctrl 等。唯一的例外是 Fn 鍵,有時會出現在筆電鍵盤上。它沒有鍵盤事件,因為它通常是在比作業系統更低層級實作的。
鍵盤事件
keydown
– 按下按鍵時(如果按鍵按住很長一段時間,會自動重複),keyup
– 放開按鍵時。
主要的鍵盤事件屬性
code
– 「按鍵代碼」("KeyA"
、"ArrowLeft"
等),特定於鍵盤上按鍵的物理位置。key
– 字元("A"
、"a"
等),對於非字元按鍵,例如 Esc,通常與code
的值相同。
過去,鍵盤事件有時用於追蹤表單欄位中的使用者輸入。這並不可靠,因為輸入可能來自各種來源。我們有 input
和 change
事件來處理任何輸入(稍後在本章 事件:change、input、cut、copy、paste 中介紹)。它們會在任何類型的輸入後觸發,包括複製貼上或語音辨識。
當我們真的需要鍵盤時,應該使用鍵盤事件。例如,對熱鍵或特殊鍵做出反應。
留言