Keyboard: keydown and keyup

Before we get to keyboard, please note that on modern devices there are other ways to “input something”. For instance, people use speech recognition (especially on mobile devices) or copy/paste with the mouse.

So if we want to track any input into an <input> field, then keyboard events are not enough. There’s another event named input to handle changes of an <input> field, by any means. And it may be a better choice for such task. We’ll cover it later in the chapter Events: change, input, cut, copy, paste.

Keyboard events should be used when we want to handle keyboard actions (virtual keyboard also counts). For instance, to react on arrow keys and or hotkeys (including combinations of keys).

Teststand

To better understand keyboard events, you can use the teststand below.

Try different key combinations in the text field.

Result
script.js
style.css
index.html
kinput.onkeydown = kinput.onkeyup = kinput.onkeypress = handle;

let lastTime = Date.now();

function handle(e) {
  if (form.elements[e.type + 'Ignore'].checked) return;

  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>&nbsp;&nbsp;&nbsp;
    <label>
      <input type="checkbox" name="keyupStop" value="1"> keyup</label>

    <p>
      Ignore:
      <label>
        <input type="checkbox" name="keydownIgnore" value="1"> keydown</label>&nbsp;&nbsp;&nbsp;
      <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"></textarea>
    <input type="button" value="Clear" onclick="area.value = ''" />
  </form>
  <script src="script.js"></script>


</body>
</html>

Keydown and keyup

The keydown events happens when a key is pressed down, and then keyup – when it’s released.

event.code and event.key

The key property of the event object allows to get the character, while the code property of the event object allows to get the “physical key code”.

For instance, the same key Z can be pressed with or without Shift. That gives us two different characters: lowercase z and uppercase Z.

The event.key is exactly the character, and it will be different. But event.code is the same:

Key event.key event.code
Z z (lowercase) KeyZ
Shift+Z Z (uppercase) KeyZ

If a user works with different languages, then switching to another language would make a totally different character instead of "Z". That will become the value of event.key, while event.code is always the same: "KeyZ".

“KeyZ” and other key codes

Every key has the code that depends on its location on the keyboard. Key codes described in the UI Events code specification.

For instance:

  • Letter keys have codes "Key<letter>": "KeyA", "KeyB" etc.
  • Digit keys have codes: "Digit<number>": "Digit0", "Digit1" etc.
  • Special keys are coded by their names: "Enter", "Backspace", "Tab" etc.

There are several widespread keyboard layouts, and the specification gives key codes for each of them.

See alphanumeric section of the spec for more codes, or just try the teststand above.

Case matters: "KeyZ", not "keyZ"

Seems obvious, but people still make mistakes.

Please evade mistypes: it’s KeyZ, not keyZ. The check like event.code=="keyZ" won’t work: the first letter of "Key" must be uppercase.

What if a key does not give any character? For instance, Shift or F1 or others. For those keys event.key is approximately the same as event.code:

Key event.key event.code
F1 F1 F1
Backspace Backspace Backspace
Shift Shift ShiftRight or ShiftLeft

Please note that event.code specifies exactly which key is pressed. For instance, most keyboards have two Shift keys: on the left and on the right side. The event.code tells us exactly which one was pressed, and event.key is responsible for the “meaning” of the key: what it is (a “Shift”).

Let’s say, we want to handle a hotkey: Ctrl+Z (or Cmd+Z for Mac). Most text editors hook the “Undo” action on it. We can set a listener on keydown and check which key is pressed – to detect when we have the hotkey.

Please answer the question – in such a listener, should we check the value of event.key or event.code?

Please, pause and answer.

Made up your mind?

If you’ve got an understanding, then the answer is, of course, event.code, as we don’t want event.key there. The value of event.key can change depending on the language or CapsLock enabled. The value of event.code is strictly bound to the key, so here we go:

document.addEventListener('keydown', function(event) {
  if (event.code == 'KeyZ' && (event.ctrlKey || event.metaKey)) {
    alert('Undo!')
  }
});

Auto-repeat

If a key is being pressed for a long enough time, it starts to repeat: the keydown triggers again and again, and then when it’s released we finally get keyup. So it’s kind of normal to have many keydown and a single keyup.

For all repeating keys the event object has event.repeat property set to true.

Default actions

Default actions vary, as there are many possible things that may be initiated by the keyboard.

For instance:

  • A character appears on the screen (the most obvious outcome).
  • A character is deleted (Delete key).
  • The page is scrolled (PageDown key).
  • The browser opens the “Save Page” dialog (Ctrl+S)
  • …and so on.

Preventing the default action on keydown can cancel most of them, with the exception of OS-based special keys. For instance, on Windows Alt+F4 closes the current browser window. And there’s no way to stop it by preventing the default action in JavaScript.

For instance, the <input> below expects a phone number, so it does not accept keys except digits, +, () or -:

<script>
function checkPhoneKey(key) {
  return (key >= '0' && key <= '9') || key == '+' || key == '(' || key == ')' || key == '-';
}
</script>
<input onkeydown="return checkPhoneKey(event.key)" placeholder="Phone, please" type="tel">

Please note that special keys like Backspace, , , Ctrl+V do not work in the input. That’s a side-effect effect of the strict filter checkPhoneKey.

Let’s relax it a little bit:

<script>
function checkPhoneKey(key) {
  return (key >= '0' && key <= '9') || key == '+' || key == '(' || key == ')' || key == '-' ||
    key == 'ArrowLeft' || key == 'ArrowRight' || key == 'Delete' || key == 'Backspace';
}
</script>
<input onkeydown="return checkPhoneKey(event.key)" placeholder="Phone, please" type="tel">

Now arrows and deletion works well.

…But we still can enter anything by using a mouse and right-click + Paste. So the filter is not 100% reliable. We can just let it be like that, because most of time it works. Or an alternative approach would be to track the input event – it triggers after any modification. There we can check the new value and highlight/modify it when it’s invalid.

Legacy

In the past, there was a keypress event, and also keyCode, charCode, which properties of the event object.

There were so many browser incompatibilities that developers of the specification decided to deprecate all of them. The old code still works, as the browser keep supporting them, but there’s totally no need to use those any more.

There was time when this chapter included their detailed description. But as of now we can forget about those.

Summary

Pressing a key always generates a keyboard event, be it symbol keys or special keys like Shift or Ctrl and so on. The only exception is Fn key that sometimes presents on a laptop keyboard. There’s no keyboard event for it, because it’s often implemented on lower level than OS.

Keyboard events:

  • keydown – on pressing the key (auto-repeats if the key is pressed for long),
  • keyup – on releasing the key.

Main keyboard event properties:

  • code – the “key code” ("KeyA", "ArrowLeft" and so on), specific to the physical location of the key on keyboard.
  • key – the character ("A", "a" and so on), for non-character keys usually has the same value as code.

In the past, keyboard events were sometimes used to track user input in form fields. That’s not reliable, because the input can come from various sources. We have input and change events to handle any input (covered later in the chapter Events: change, input, cut, copy, paste). They trigger after any input, including mouse or speech recognition.

We should use keyboard events when we really want keyboard. For example, to react on hotkeys or special keys.

Tasks

importance: 5

Create a function runOnKeys(func, code1, code2, ... code_n) that runs func on simultaneous pressing of keys with codes code1, code2, …, code_n.

For instance, the code below shows alert when "Q" and "W" are pressed together (in any language, with or without CapsLock)

runOnKeys(
  () => alert("Hello!"),
  "KeyQ",
  "KeyW"
);

Demo in new window

We should use two handlers: document.onkeydown and document.onkeyup.

The set pressed should keep currently pressed keys.

The first handler adds to it, while the second one removes from it. Every time on keydown we check if we have enough keys pressed, and run the function if it is so.

Open the solution in the sandbox.

Tutorial map

Comments

read this before commenting…
  • You're welcome to post additions, questions to the articles and answers to them.
  • To insert a few words of code, use the <code> tag, for several lines – use <pre>, for more than 10 lines – use a sandbox (plnkr, JSBin, codepen…)
  • If you can't understand something in the article – please elaborate.