Events - klawisze
Jak powiedzieliśmy sobie w poprzednim rozdziale, różnych zdarzeń jest bardzo dużo. Część z nich używana jest częściej, inne rzadziej. Wśród tej pierwszej grupy z pewnością znajdują się zdarzenia związane z klawiaturą.
keyDown, keyUp, keyPress
Najczęściej używanymi zdarzeniami w przypadku klawiatury są:
keyDown | naciśnięcie klawisza |
keyUp | puszczenie klawisza |
keyPress | naciśnięcie i puszczenie klawisza |
Zdarzenie keyUp odpalane jest tylko jeden raz - przy puszczeniu klawisza.
Najwięcej problemów zawsze sprawia rozróżnienie, kiedy mamy używać keyDown a kiedy keyPress.
Podstawowa różnica między tymi zdarzeniami jest taka, że zdarzenie keyPress odpalane jest cyklicznie tylko kiedy przytrzymujemy klawisz znakowy (litery, cyfry itp).
Oznacza to, że keyPress nie zadziała dla przykładu dla klawiszy Backspace, Delete czy Home.
Drugą różnicą jest nieco inny efekt finałowy. W przypadku zdarzenia keyPress, gdy naciśniemy dwa klawisze równocześnie - np. Shift + A, zdarzenie to zostanie odpalone cyklicznie dla trzymanego klawisza A wraz z ustawioną flagą isShift na true.
W przypadku keyDown, po naciśnięciu klawisza Shift zdarzenie to zacznie być odpalane dla tego klawisza. Gdy następnie naciśniemy A, zdarzenie to będzie dalej odpalane dla klawisza A wraz z włączoną flagą isShift na true.
Dość opisów. Po prostu przetestujmy to w praktyce:
Input
Na szczególną uwagę zasługuje event input. Nie jest on bezpośrednio związany z klawiszami (dlatego nie pobierzemy tutaj wszystkich dodatkowych informacji za pomocą event), ale jest też bardzo często używany w przypadku reakcji na wpisywane rzeczy do formularzy. Event ten jest odpalany w reakcji na zmianę wartości inputa przez użytkownika i może być użyty dla każdego rodzaju pola.
Mamy też event change, ale ten odpalany jest dla inputów dopiero po zatwierdzeniu zmian (opuszczenie pola input, zmiana optiona w select, kliknięcie w radio).
W poniższym przykładzie zrobiłem pętlę po wszystkich elementach formularza, podpinając każdemu z nich zdarzenie input
.
const form = document.querySelector("#testInput");
for (const el of form.elements) {
el.addEventListener("input", e => {
console.log(`Wartość pola: ${el.value}`);
})
}
Zdarzenie input
jest o tyle ważne, że w przypadku inputa typu number w niektórych przeglądarkach
(np. Chrome) pojawiają się w takim polu dodatkowe strzałeczki pozwalające zwiększać/zmniejszać liczbę w polu.
Żadne ze zdarzeń keyup
, keydown
, keypress
nie działa na ich naciśnięcie, natomiast input
już tak.
const input1 = document.querySelector("#inputTestKeyPress");
input1.addEventListener("keypress", e => {
console.log(input1.value);
})
const input2 = document.querySelector("#inputTestInput");
input2.addEventListener("input", e => {
console.log(input2.value);
})
Zmień wartości pola klikając na strzałeczki przy polu i zobacz w konsoli:
Podobna sytuacja ma miejsce też przy innych polach - np. type="search"
, gdzie po wpisaniu czegokolwiek do pola, pojawia się [X] służący do czyszczenia zawartości. Żadne ze zdarzeń key...
nie zadziała gdy wyczyścimy w ten sposób dany element. Pozostaje zdarzenie input.
Który klawisz został naciśnięty
Wartość naciśniętego klawisza jest przechowywana w właściwości key zdarzenia związanego z klawiszami.
document.addEventListener("keyup", e => {
console.log("Klawisz: ", e.key);
});
Aby wykryć czy dodatkowo został naciśnięty jeden z funkcyjnych klawiszy możemy skorzystać z dodatkowych właściwości:
e.altKey | Czy klawisz Alt jest naciśnięty |
---|---|
e.ctrlKey | Czy klawisz Ctrl jest naciśnięty |
e.shiftKey | Czy klawisz Shift jest naciśnięty |
e.keyCode | Zwraca kod klawisza. Przydatne przy sprawdzaniu zakresów klawiszy - np. klawisz to liczba |
const textarea = document.querySelector("#keyTest");
textarea.addEventListener("keyup", e => {
const keys = [];
if (e.shiftKey) {
keys.push("shift");
}
if (e.altKey) {
keys.push("alt");
}
if (e.ctrlKey) {
keys.push("ctrl");
}
keys.push(e.key);
console.log("Naciśnięte klawisze: " + keys.join(" + "));
if (e.keyCode >= 48 && e.keyCode <= 57) {
console.log("klawisz to cyfra");
}
});
Używany w powyższych przykładach keyCode jest uznany za depreaced, czyli nie zaleca się go stosować na rzecz e.code lub e.key. Niestety nie jest to takie proste, ponieważ żadna z tych właściwości nie zwraca kodu klawisza a tylko słowny opis. Możemy to obejść poprzez zastosowanie konstrukcji:
const keyCode = e.key.charCodeAt(0);
Instrukcja ta sprawdzi się jednak tylko w przypadku klawiszy, dla których e.key zwraca tylko jedną wartość. W przypadku innych klawiszy (np. strzałka w górę, backspace itp), trzeba będzie dodać dodatkowe warunki. Czasami będzie to oznaczało użycie bardziej skomplikowanych warunków.
Możliwych zastosowań dla powyższych funkcjonalności jest cała masa. Niektóre strony za pomocą klawiszy odpalają niektóre funkcjonalności - dla przykładu facebook. W takim przypadku nasze zdarzenie powinniśmy podpiąć pod document:
document.addEventListener("keyup", e => {
if (e.key.toUpperCase() === "?") {
showHelpPopup();
}
if (e.key.toUpperCase() === "P") {
newPost();
}
})
Blokowanie wpisywania
Ostatni temat, którym się zajmiemy, a który jest dość popularny, to blokowanie wpisywania konkretnych znaków do pola.
Dość często zdarza się, że chcesz, by użytkownik mógł do danego pola wpisać przykładowo tylko liczby. Pole typu number nie jest tutaj rozwiązaniem, ponieważ pozwala wpisywać literę "e" (1), a w niektórych przeglądarkach w ogóle nie nakłada restrykcji przez co możemy do niego wpisać dowolny znak.
Rozwiązanie może wydawać się bardzo proste - użyję keypress i zareaguję na e.key
. Niestety nie tym razem. Zdarzenia keypress
oraz keydown
są odpalane tuż przed zaktualizowaniem wartości pola. Właściwym zdarzeniem będzie keyup
. W tym przypadku jednak użytkownik może przytrzymać klawisz, co też nie będzie dla nas dobre.
Rozwiązania są minimum dwa. Pierwsze to połączenie dwóch zdarzeń - keydown
wraz z keyup
. W keydown sprawdzamy naciśnięty klawisz, natomiast w keyup robimy dodatkowo test wartości pola (np. gdy chcemy pokazać błąd walidacji).
Możemy też skorzystać ze zdarzeń beforeinput
oraz input
.
W zdarzeniach tych nie sprawdzimy naciśniętych klawiszy (bo nie mamy dostępu do e.key), natomiast bez problemu możemy to obejść poprzez zastosowanie dodatkowej zmiennej:
const input = document.querySelector("input");
input.addEventListener("beforeinput", e => {
e.currentTarget.previousValue = e.currentTarget.value;
});
input.addEventListener("input", e => {
if (/^\d*$/g.test(e.currentTarget.value)) { //tylko liczby
} else {
e.currentTarget.value = e.currentTarget.previousValue;
}
});