up
This commit is contained in:
parent
fc84391bd2
commit
8360ebbe90
60 changed files with 920 additions and 1672 deletions
|
@ -0,0 +1,6 @@
|
|||
|
||||
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.
|
|
@ -0,0 +1,47 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<body>
|
||||
|
||||
<p>Press "Q" and "W" together (can be in any language).</p>
|
||||
|
||||
<script>
|
||||
function runOnKeys(func, ...codes) {
|
||||
let pressed = new Set();
|
||||
|
||||
document.addEventListener('keydown', function(event) {
|
||||
pressed.add(event.code);
|
||||
|
||||
for (let code of codes) { // are all keys in the set?
|
||||
if (!pressed.has(code)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// yes, they are
|
||||
|
||||
// during the alert, if the visitor releases the keys,
|
||||
// JavaScript does not get the "keyup" event
|
||||
// and pressed set will keep assuming that the key is pressed
|
||||
// so, to evade "sticky" keys, we reset the status
|
||||
// if the user wants to run the hotkey again - let him press all keys again
|
||||
pressed.clear();
|
||||
|
||||
func();
|
||||
});
|
||||
|
||||
document.addEventListener('keyup', function(event) {
|
||||
pressed.delete(event.code);
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
runOnKeys(
|
||||
() => alert("Hello!"),
|
||||
"KeyQ",
|
||||
"KeyW"
|
||||
);
|
||||
</script>
|
||||
|
||||
</body>
|
||||
|
||||
</html>
|
|
@ -0,0 +1,19 @@
|
|||
importance: 5
|
||||
|
||||
---
|
||||
|
||||
# Extended hotkeys
|
||||
|
||||
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)
|
||||
|
||||
```js no-beautify
|
||||
runOnKeys(
|
||||
() => alert("Hello!"),
|
||||
"KeyQ",
|
||||
"KeyW"
|
||||
);
|
||||
```
|
||||
|
||||
[demo src="solution"]
|
154
2-ui/3-event-details/5-keyboard-events/article.md
Normal file
154
2-ui/3-event-details/5-keyboard-events/article.md
Normal file
|
@ -0,0 +1,154 @@
|
|||
# Keyboard: keydown and keyup
|
||||
|
||||
Let's study keyboard events now.
|
||||
|
||||
Before we start, please note that on modern devices there are other ways to "input something" then just a keyboard. For instance, people use speech recognition (tap microphone, say something, see it entered) or copy/paste with a mouse.
|
||||
|
||||
So if we want to track any input into an `<input>` field, then keyboard events is 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 a bit later [todo link].
|
||||
|
||||
Keyboard events should be used when we want to handle keyboard actions (virtual keyboard usually also counts). For instance, to react on arrow keys `key:Up` and `key:Down` or hotkeys (including combinations of keys).
|
||||
|
||||
[cut]
|
||||
|
||||
|
||||
## Teststand [#keyboard-test-stand]
|
||||
|
||||
```offline
|
||||
To better understand keyboard events, you can use the [teststand](sandbox:keyboard-dump).
|
||||
```
|
||||
|
||||
```online
|
||||
To better understand keyboard events, you can use the teststand below.
|
||||
|
||||
Try different key combinations in the text field.
|
||||
|
||||
[codetabs src="keyboard-dump" height=480]
|
||||
```
|
||||
|
||||
As you read on, if you want to try things out -- return to the stand and press keys.
|
||||
|
||||
|
||||
## Keydown and keyup
|
||||
|
||||
The `keydown` events happens when a key is pressed, 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 `key:Z` can be pressed with or without `Shift`. That gives us two different characters: a lowercase and uppercase `z`, right?
|
||||
|
||||
The `event.key` is exactly the character, and it will be different. But `event.code` is the same:
|
||||
|
||||
| Key | `event.key` | `event.code` |
|
||||
|--------------|-------------|--------------|
|
||||
| `key:Z` |`z` (lowercase) |`KeyZ` |
|
||||
| `key:Shift+Z`|`Z` (uppercase) |`KeyZ` |
|
||||
|
||||
```smart header="\"KeyZ\" and other key codes"
|
||||
Key codes like `"KeyZ"` in the example above are described in the [UI Events code specification](https://www.w3.org/TR/uievents-code/).
|
||||
|
||||
For instance:
|
||||
- Letter keys have codes `"Key<letter>"`
|
||||
- Special keys are coded by their names: `"Enter"`, `"Backspace"`, `"Tab"` etc.
|
||||
|
||||
See [alphanumeric section](https://www.w3.org/TR/uievents-code/#key-alphanumeric-section) for more examples, or just try the [teststand](#keyboard-test-stand) above.
|
||||
```
|
||||
|
||||
```warn header="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.
|
||||
```
|
||||
|
||||
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"`.
|
||||
|
||||
What is a key does not give any character? For instance, `key:Shift` or `key:Tab` or others. For those keys `event.key` is approximately the same as `event.code`:
|
||||
|
||||
|
||||
| Key | `event.key` | `event.code` |
|
||||
|--------------|-------------|--------------|
|
||||
| `key:Tab` |`Tab` |`Tab` |
|
||||
| `key:F1` |`F1` |`F1` |
|
||||
| `key:Backspace` |`Backspace` |`Backspace` |
|
||||
| `key:Shift`|`Shift` |`ShiftRight` or `ShiftLeft` |
|
||||
|
||||
Please note that `event.code` specifies exactly which key is pressed. For instance, most keyboards have two `key: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: `key:Ctrl+Z` (or `key:Cmd+Z` for Mac). That's an "undo" action in most text editors. We can set a listener on `keydown` and check which key is pressed -- to detect when we have the hotkey.
|
||||
|
||||
How do you think, 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`, and we don't want `event.key` here. 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:
|
||||
|
||||
```js run
|
||||
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.return=true`.
|
||||
|
||||
|
||||
## Default actions
|
||||
|
||||
Default actions vary, as there are many possible things that may be initiated by keyboard:
|
||||
|
||||
For instance:
|
||||
|
||||
- A character appears on the screen (the most obvious one).
|
||||
- A character is deleted (`key:Delete` key).
|
||||
- The page is scrolled (`key:PageDown` key).
|
||||
- The browser opens the "Save Page" dialog (`key:Ctrl+S`)
|
||||
- ...and so on.
|
||||
|
||||
Preventing the default actions cancels most of them, with the sole exception of OS-based special keys.
|
||||
|
||||
For instance, on Windows `key:Alt+F4` closes the current browser window. And there's no way to stop it by preventing the default action in JavaScript.
|
||||
|
||||
For instance, we can't use keyboard to enter something into the `<input>` below:
|
||||
|
||||
```html run
|
||||
<input *!*onkeydown="return false"*/!* placeholder="No keyboard input" type="text">
|
||||
```
|
||||
|
||||
If we type-in something, it's just ignored. Please note that special combinations like `Ctrl+V` (paste) also don't work. But using a mouse and right-click + Paste still can add the text. So checking in `keydown` doesn't work as a 100% reliable filter.
|
||||
|
||||
## Legacy
|
||||
|
||||
In the past, there was a `keypress` event, and also `keyCode`, `charCode`, `which` properties of the event object.
|
||||
|
||||
But there were so many incompatibilities between browsers that all of them are deprecated and removed from the standard. So the old code still works (and sometimes works around a bunch of quirks), 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
|
||||
|
||||
All keys yield keyboard events -- be it symbol keys or special keys like `key:Shift` or `key:Ctrl`.
|
||||
|
||||
The only exception is `key:Fn` key that is sometimes present on laptop keyboards. There's no keyboard event for it, because it's often implemented on lower-level than OS.
|
||||
|
||||
Keyboard events:
|
||||
|
||||
- `keydown` -- on press the key (auto-repeats if the key is pressed for long),
|
||||
- `keyup` -- on releasing the key.
|
||||
|
||||
Main keyboard event properties:
|
||||
|
||||
- `code` -- the "key code", specific to the physical location of the key on keyboard.
|
||||
- `key` -- the character, for non-character keys a "code value", usually same as `code`.
|
||||
|
||||
In the past, keyboard events were sometimes used to track user input in form fields. That's not the case any more, because we have `input` and `change` events for that (to be covered later). They are better, because they work for all ways of input, including mouse or speech recognition.
|
||||
|
||||
We still should use them when we really want keyboard. For example, to react on hotkeys or special keys.
|
15
2-ui/3-event-details/5-keyboard-events/head.html
Normal file
15
2-ui/3-event-details/5-keyboard-events/head.html
Normal file
|
@ -0,0 +1,15 @@
|
|||
<script>
|
||||
function getChar(event) {
|
||||
if (event.which == null) { // IE
|
||||
if (event.keyCode < 32) return null; // спец. символ
|
||||
return String.fromCharCode(event.keyCode)
|
||||
}
|
||||
|
||||
if (event.which != 0 && event.charCode != 0) { // все кроме IE
|
||||
if (event.which < 32) return null; // спец. символ
|
||||
return String.fromCharCode(event.which); // остальные
|
||||
}
|
||||
|
||||
return null; // спец. символ
|
||||
}
|
||||
</script>
|
|
@ -0,0 +1,38 @@
|
|||
<!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"></textarea>
|
||||
<input type="button" value="Clear" onclick="area.value = ''" />
|
||||
</form>
|
||||
<script src="script.js"></script>
|
||||
|
||||
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,28 @@
|
|||
kinput.onkeydown = kinput.onkeyup = kinput.onkeypress = handle;
|
||||
|
||||
var lastTime = Date.now();
|
||||
|
||||
function handle(e) {
|
||||
if (form.elements[e.type + 'Ignore'].checked) return;
|
||||
|
||||
var 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();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,18 @@
|
|||
#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;
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue