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:

Przetestuj w konsoli

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 

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;
    }
});

Wszelkie prawa zastrzeżone. Jeżeli chcesz używać jakiejś części tego kursu, skontaktuj się z autorem.
Aha - i ta strona korzysta z ciasteczek.