Prima di scendere nei dettagli della tastiera, è bene ricordare che nei dispositivi moderni esistono tanti modi per âinserire qualche datoâ. Per esempio, è doveroso citare lâuso del riconoscimento vocale (specialmente sui dispositivi mobile) o il copia/incolla tramite il mouse.
Quindi, se vogliamo tenere traccia di qualunque input dentro un campo <input>, allora gli eventi della tastiera non saranno sufficienti. Esiste però un altro evento chiamato input per tenere traccia delle modifiche degli <input>, indipendentemente dalla modalità di inserimento. Questa potrebbe essere la scelta corretta per questo tipo di attività . Lâargomento verrà affrontato più avanti nel capitolo Eventi: change, input, cut, copy, paste.
Gli eventi da tastiera dovrebbero essere usati per gestire, appunto, azioni da tastiera (comprese quelle virtuali). Ad esempio, per reagire ai tasti freccia Up e Down, oppure per lâuso delle scorciatoie e/o tasti di scelta rapida (includendo quindi combinazioni di tasti).
Banco di test
Per capire meglio gli eventi da tastiera, possiamo usare il seguente banco di test.
Proviamo diverse combinazioni di tasti nel campo di testo.
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 e keyup
Lâevento keydown scaturisce alla pressione di un tasto; keyup, invece, quando viene rilasciato.
event.code e event.key
La proprietà key dellâoggetto evento permette di ottenere il carattere, mentre la proprietà code ci restituisce il âcodice fisico del tastoâ.
Ad esempio, a parità di Z, questâultimo potrebbe essere stato premuto con o senza il Shift, cosa che potrebbe restituirci due differenti caratteri: z minuscolo oppure Z maiuscolo.
event.key è esattamente il carattere, che può essere diverso secondo alcuni criteri. Invece event.code, a parità di tasto, restituisce sempre lo stesso valore:
| Key | event.key |
event.code |
|---|---|---|
| Z | z (minuscolo) |
KeyZ |
| Shift+Z | Z (maiuscolo) |
KeyZ |
Prendendo questo tasto come riferimento, se un utente facesse uso di diverse lingue nello stesso sistema operativo, il passaggio ad unâaltra lingua potrebbe portare ad avere tuttâaltro carattere rispetto a "Z". Questâultimo sarebbe il valore di event.key, mentre event.code sarebbe sempre "KeyZ".
Ogni tasto ha un codice che dipende dalla sua posizione sulla tastiera. Questi codici vengono descritti nelle specifiche dei codici Evento UI.
Per esempio:
- I tasti lettera hanno dei codici:
"Key<letter>":"KeyA","KeyB"etc. - I tasti numerici hanno dei codici:
"Digit<number>":"Digit0","Digit1"etc. - I tasti speciali sono codificati con i loro nomi:
"Enter","Backspace","Tab"etc.
Esiste una grande varietà di layout di tastiera, e le specifiche danno un codice per ognuno di essi.
Per avere informazioni sui vari codici, fare riferimento alla sezione alfanumerica delle specifiche, oppure, è sufficiente premere un tasto nel banco di test precedente.
"KeyZ", e non "keyZ"Sembra ovvio, ma le persone sbagliano ancora.
Bisogna evitare di scrivere in modo errato: la dicitura corretta è KeyZ, e non keyZ. Di conseguenza, un controllo scritto così event.code=="keyZ" non funziona: la prima lettera di "Key" deve essere maiuscola.
Cosa succederebbe se un tasto non restituisse nessun carattere? Per esempio, Shift oppure F1 o altri ancora. Per questi tasti il valore di event.key è, con buona approssimazione, lo stesso di event.code:
| Key | event.key |
event.code |
|---|---|---|
| F1 | F1 |
F1 |
| Backspace | Backspace |
Backspace |
| Shift | Shift |
ShiftRight or ShiftLeft |
à importante sottolineare che event.code specifica esattamente il tasto premuto. Per esempio, la maggioranza delle tastiere ha due tasti Shift: uno nel lato sinistro e uno nel lato destro. event.code ci dice esattamente quale dei due viene premuto, event.key è invece responsabile del âsignificatoâ del tasto: cosa è (cioè uno âShiftâ).
Mettiamo il caso che volessimo gestire una scorciatoia: Ctrl+Z (o Cmd+Z su Mac). La maggior parte degli editor di testo associa a questa combinazione, lâazione âUndoâ. A quel punto potremmo impostare un listener sul keydown e controllare quale tasto venga premuto.
Ma qui ci troveremo di fronte a un dilemma: in questo listener, cosa dovremmo controllare? Il valore di event.key oppure quello di event.code?
Da una parte, il valore di event.key è un carattere, e cambia a seconda del linguaggio. Se il visitatore gestisce più lingue nel suo sistema operativo e passa da una allâaltra, lo stesso tasto restituirebbe caratteri differenti. Quindi ha senso controllare event.code, che è sempre lo stesso.
Ecco un esempio:
document.addEventListener('keydown', function(event) {
if (event.code == 'KeyZ' && (event.ctrlKey || event.metaKey)) {
alert('Undo!')
}
});
Dâaltra parte, câè un problema anche con event.code, dato che per layout di tastiera differenti, possono corrispondere caratteri differenti.
Per esempio, qui abbiamo un layout americano (âQWERTYâ) e sotto di esso un layout Tedesco (âQWERTZâ) (da Wikipedia):
A parità di tasto, sul layout americano corrisponderà il carattere âZâ, invece per quello tedesco sarà âYâ (le lettere sono scambiate tra di loro).
Letteralmente, event.code equivale a KeyZ per gli utenti con il layout tedesco quando premono Y.
Se andiamo a controllare event.code == 'KeyZ' nel nostro codice, per gli utenti con il layout tedesco, il test passerà alla pressione del tasto Y.
Questo può sembrare strano, ma è così. Le specifiche menzionano in modo esplicito questo comportamento.
Quindi, event.code può corrispondere a un carattere errato su layout non contemplati. A parità di lettera, per layout differenti potrebbero essere mappati tasti fisici differenti, portando ad avere codici differenti. Fortunatamente, questo avviene solo con alcuni di questi, ad esempio keyA, keyQ, keyZ (come abbiamo visto), ma non avviene con i tasti speciali come Shift. Si può vedere la lista nelle specifiche.
Per il tracciamento affidabile dei caratteri che sono dipendenti dal layout, event.key potrebbe essere la soluzione migliore.
Di contro, event.code ha il beneficio di essere sempre lo stesso, legato alla posizione fisica del tasto, anche se il visitatore dovesse modificare la lingua. E le scorciatoie ad esso relative funzioneranno bene anche in caso di cambio lingua.
Vogliamo gestire dei tasti dipendenti dal layout? Ecco che event.key è quello che fa per noi.
Oppure, vogliamo una scorciatoia che funzioni anche modificando la lingua? Allora event.code sarebbe più adatto.
Auto-repeat
Se un tasto viene premuto abbastanza a lungo, si instaura lââauto-repeatâ: lâevento keydown scaturisce ancora e ancora e, alla fine, quando viene rilasciato, otteniamo un evento keyup. Quindi è abbastanza normale avere molti keydown ed un solo keyup.
Per eventi generati da auto-repeat, lâoggetto evento coinvolto avrà la proprietà event.repeat impostata a true.
Azioni default
Le azioni di default possono essere tante e variegate, dal momento che sono tante le cose che possono essere attivate tramite la tastiera.
Per esempio:
- Appare un nuovo carattere sullo schermo (lo scenario più frequente).
- Viene cancellato un carattere (tasto Delete).
- Si scrolla la pagina (tasto PageDown).
- Il browser apre la finestra di dialogo âSalva la paginaâ (Ctrl+S)
- â¦e così via.
Prevenire le azioni di default sul keydown può annullare la maggioranza di esse, con lâeccezione delle combinazioni di tasti del sistema operativo. Per esempio, su Windows Alt+F4 chiude la finestra attuale del browser. E non câè modo per prevenire questa azione predefinita attraverso JavaScript.
Ora un esempio: il seguente campo <input> si aspetta un numero di telefono, quindi non accetta tasti che non siano numeri, +, () o -:
<script>
function checkPhoneKey(key) {
return (key >= '0' && key <= '9') || ['+','(',')','-'].includes(key);
}
</script>
<input onkeydown="return checkPhoneKey(event.key)" placeholder="Inserire un numero di telefono" type="tel">
Qui il gestore onkeydown utilizza checkPhoneKey per controllare i testi premuti. Se sono validi (da 0..9 o uno tra +-()), allora ritorna true, altrimenti false.
Come sappiamo, il valore false restituito da un gestore di evento, assegnato usando un attributo o una proprietà DOM, come in questo caso, previene lâazione default, quindi non appare nulla in <input> per i tasti che non passano il tets. (Il valore true restituito non influenza nulla, conta solo la restituzione di false)
à interessante notare che i tasti speciali, come Backspace, Left, Right, Ctrl+V, non funzionano nel campo input. Questo è un effetto collaterale delle restrizioni del filtro checkPhoneKey.
Rendiamolo un poâ più rilassato permettendo i tasti freccia Left, Right e Delete, Backspace::
Please note that special keys, such as Backspace, Left, Right, do not work in the input. Thatâs a side-effect of the strict filter checkPhoneKey. These keys make it return false.
Letâs relax the filter a little bit by allowing arrow keys Left, Right and 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">
Adesso le frecce e il tasto cancella funzionano.
Anche se abbiamo il filtro chiave, è comunque possibile inserire qualsiasi cosa utilizzando il mouse e facendo clic con il pulsante destro del mouse + Incolla. I dispositivi mobili forniscono altri mezzi per immettere valori. Quindi il filtro non è affidabile al 100%.
Lâapproccio alternativo sarebbe quello di tenere traccia dellâevento oninput, che si attiva dopo qualsiasi modifica. Lì possiamo controllare il nuovo input.value e modificarlo/evidenziare <input> quando non è valido. Oppure possiamo usare entrambi i gestori di evento insieme.
EreditÃ
In passato, câera lâevento keypress, ed anche le proprietà keyCode, charCode, which dellâoggetto evento.
Câerano tante di quelle incompatibilità tra i vari browser, anche durante lo sviluppo delle specifiche da parte degli sviluppatori che cercavano di implementarle, che lâunica soluzione fu quella di deprecarli tutti, e creare degli eventi nuovi e moderni (descritti sopra in questo capitolo). Il codice vecchio funziona ancora, dal momento che i browser continuano a supportarli, ma non câè assolutamente nessuna ragione per continuare a farlo.
Tastiere dei dispositivi mobile
Usando le tastiere virtuali dei dispositivi mobile, conosciute formalmente come IME (Input-Method Editor), lo standard W3C ha stabilito che per quanto riguarda il KeyboardEvent, il e.keyCode dovrebbe essere 229 ed il e.key dovrebbe essere "Unidentified".
Mentre alcune tastiere potrebbero usare il valore corretto per e.key, e.code, e.keyCode⦠premendo certi tasti come le frecce o lo barra spaziatrice, non esistono garanzie di aderenza alle specifiche, quindi la logica della tastiera potrebbe non funzionare sui dispositivi mobile.
Riepilogo
La pressione di una tasto genera sempre un evento da tastiera, che sia un tasto simbolo o un tasto speciale come Shift, Ctrl e così via. Lâunica eccezione è rappresentata dal tasto Fn che a volte è presente nelle tastiere dei portatili. Per questo tasto non ci sono eventi, perché spesso il funzionamento di questo tasto è implementato a un livello più basso del sistema operativo.
Eventi da tastiera:
keydownpremendo il tasto (auto-repeat se il tasto viene tenuto premuto a lungo),keyuprilasciando il tasto.
Le principali proprietà degli eventi da tastiera sono:
codeil âcodice del tastoâ ("KeyA","ArrowLeft"e così via), specifico della posizione fisica del tasto sulla tastiera.keyâ il carattere ("A","a"e così via), per tasti che non rappresentano caratteri, come Esc, solitamente ha lo stesso valore dicode.
In passato, gli eventi da tastiera erano talvolta usati per tenere traccia degli input dellâutente nei campi dei form, cosa non molto affidabile perché lâinput può avvenire in vari modi. Per gestire qualunque tipo di input abbiamo input e change (affrontati più avanti nel capitolo Eventi: change, input, cut, copy, paste). Questi scaturiscono da qualunque tipo di input, inclusi il copia-incolla o il riconoscimento vocale.
In generale, dovremmo usare gli eventi da tastiera solamente quando vogliamo usare, appunto, la tastiera. Ad esempio per scorciatoie o tasti speciali.
Commenti
<code>, per molte righe â includile nel tag<pre>, per più di 10 righe â utilizza una sandbox (plnkr, jsbin, codepenâ¦)