Moduł6 - Zajęcia 12 - Zdarzenia

Zdarzenia

Zdarzenie (event) to sygnał z przeglądarki, że coś się wydarzyło na stronie internetowej. Zdarzenia umożliwiają reagowanie na działania użytkownika i wykonywanie kodu związanego z określonym zdarzeniem. Istnieje wiele źródeł zdarzeń: mysz, klawiatura, elementy formularzy, ładowanie obrazu, lokalny schowek, zmiana etapu animacji CSS lub przejścia, zmiana rozmiaru okna i wiele innych.

Jedna akcja może wywołać wiele zdarzeń. Na przykład kliknięcie wywołuje najpierw mousedown, następnie mouseup i click. W przypadku, gdy jedna akcja generuje kilka zdarzeń, ich kolejność jest ustalona. Oznacza to, że ich obsługa będzie wywoływana (w tym przypadku) zawsze w kolejności mousedown → mouseup → click

Element musi posiadać detektor zdarzeń (event handler) aby móc reagować na działania użytkownika. To znaczy że musimy zdefiniować funkcję, która zostanie wywołana, gdy tylko wystąpi zdarzenie.

Metoda addEventListener()

Dodaje detektor zdarzeń do elementu.

            element.addEventListener(event, handler, options);
          
  • event - nazwa zdarzenia, jedna z góry określonych wartości, np. "click"
  • handler - funkcja wywołania zwrotnego, która zostanie wywołana po wystąpieniu zdarzenia
  • options - opcjonalny obiekt z zaawansowanymi opcjami.
            const button = document.querySelector(".my-button");

                button.addEventListener("click", () => {
                    console.log("Button was clicked");
                });
          

W przypadku wywołania zwrotnego możesz (i najlepiej tak zrobić) użyć oddzielnej funkcji i przekazać do niej link. Osobno zdefiniowana funkcja poprawi czytelność kodu i będzie re-używalna dla różnych elementów.

            const button = document.querySelector(".my-button");

            const handleClick = () => {
                console.log("Button was clicked");
            };

            button.addEventListener("click", handleClick);
          

Jeden element może mieć dowolną liczbę funkcji które obsługują zdarzenia, nawet zdarzenia tego samego typu. Funkcje wywołania zwrotnego będą wywoływane w kolejności ich wystąpienia w kodzie.

Przykład -------------------------------

            const singleBtn = document.querySelector("#single");

            const handleClick = () => {
                console.log("click event listener callback");
            };

            singleBtn.addEventListener("click", handleClick);

            // ===============================================
            const multiBtn = document.querySelector("#multiple");

            const firstCallback = () => {
                console.log("First callback!");
            };
            const secondCallback = () => {
                console.log("Second callback!");
            };
            const thirdCallback = () => {
                console.log("Third callback!");
            };

            multiBtn.addEventListener("click", firstCallback);
            multiBtn.addEventListener("click", secondCallback);
            multiBtn.addEventListener("click", thirdCallback);
          

PrzykładEND -------------------------------

Metoda removeEventListener()

Usuwa detektor zdarzeń z elementu. Argumenty są takie same jak w metodzie addEventListener().

            element.removeEventListener(event, handler, options);
          

Aby usunąć event handler, musisz przekazać link dokładnie do tej funkcji zwrotnej, która została przypisana w addEventListener(). Dlatego w tym przypadku do wywołań zwrotnych wykorzystywana jest osobna funkcja przekazywana przez nazwę (link).

Musimy to zrobić ponieważ jak już wspomnieliśmy, jeden element na ten sam typ wydarzenia może mieć różne handlers więc musimy jakoś odróżnić który powinien zostać usunięty.

Przykład -------------------------------

            const addListenerBtn = document.querySelector('.js-add');
            const removeListenerBtn = document.querySelector('.js-remove');
            const btn = document.querySelector(".target-btn");

            const handleClick = () => {
                console.log("click event listener callback");
            };

            addListenerBtn.addEventListener("click", () => {
                btn.addEventListener("click", handleClick);
                console.log("click event listener was added to btn");
            });

            removeListenerBtn.addEventListener("click", () => {
                btn.removeEventListener("click", handleClick);
                console.log("click event listener was removed from btn");
            });
          

PrzykładEND -------------------------------

Słowo kluczowe this

Jeśli wywołanie zwrotne to funkcja, która używa this, domyślnie kontekst wewnątrz niej będzie odnosić się do elementu DOM, na którym "założony" jest detektor zdarzeń.

            const mango = {
                username: "Mango",
                showUsername() {
                    console.log(this);
                    console.log(`My username is: ${this.username}`);
                },
            };

            const btn = document.querySelector(".js-btn");

            // ✅ Działa
            mango.showUsername();

            // ❌ this będzie się odnosić do button, jeśli będziemy używać showUsername jako callback
            btn.addEventListener("click", mango.showUsername);// nie działa

            // ✅ Nie zapomnij dołączyć kontekstu metod obiektowych
            btn.addEventListener("click", mango.showUsername.bind(mango));
          

Obiekt zdarzenia

Aby prawidłowo obsłużyć zdarzenie, czasami nie wystarczy wiedzieć tylko to, że jest to kliknięcie lub naciśnięcie klawisza, mogą być potrzebne szczegóły. Na przykład bieżąca wartość pola tekstowego, element w którym wystąpiło zdarzenie, wbudowane metody i nie tylko.

Każde zdarzenie to obiekt, który zawiera informacje o szczegółach zdarzenia i jest automatycznie przekazywany jako pierwszy argument do funkcji obsługującej zdarzenie. Wszystkie zdarzenia pochodzą od klasy bazowej Event.

            const handleClick = event => {
                console.log(event);
            };

            button.addEventListener("click", handleClick);
          

Parametr event to obiekt zdarzenia, który jest automatycznie przekazywany jako pierwszy argument podczas wywoływania funkcji zwrotnej. Możemy go nazwać jak chcemy, ale według konwencji jest deklarowany jako e, evt lub event.

Niektóre właściwości obiektu zdarzenia:

  • event.type - typ zdarzenia.
  • event.currentTarget - element, na którym wykonywana jest procedura obsługi zdarzenia.

Przykład -------------------------------

            const button = document.querySelector(".btn");

            const handleClick = (event) => {
                console.log("event: ", event);
                console.log("event type: ", event.type);
                console.log("currentTarget: ", event.currentTarget);
            };
            button.addEventListener("click", handleClick); 
          

PrzykładEND -------------------------------

Domyślne działania przeglądarki

Niektóre zdarzenia uruchamiają domyślne event handlery przeglądarki w odpowiedzi na określony typ zdarzenia. Na przykład kliknięcie w link inicjuje przejście na nowy adres podany w href, a wysłanie formularza powoduje ponowne załadowanie strony. W większości przypadków to zachowanie jest dla nas niepożądane i należy je zmienić.

Aby skasować domyślne działanie przeglądarki na obiekcie zdarzenia, istnieje standardowa metoda dla event - preventDefault().

Przykład -------------------------------

            const form = document.querySelector(".register-form");

            form.addEventListener("submit", (event) => {
                event.preventDefault();
                const {
                    elements: { username, password }
                } = event.currentTarget;
                console.log(username.value, password.value);
            });
          

PrzykładEND -------------------------------

Zdarzenia związane z klawiaturą

Istnieją dwa główne zdarzenia klawiatury: keydown i keyup. W przeciwieństwie do innych, zdarzenia klawiatury są przetwarzane w dokumencie, a nie w określonym elemencie. Obiekty zdarzeń klawiatury pochodzą z klasy bazowej KeyboardEvent.

            document.addEventListener("keydown", event => {
                console.log("Keydown: ", event);
            });

            document.addEventListener("keyup", event => {
                console.log("Keyup: ", event);
            });
          

Po naciśnięciu klawisza najpierw wydarza się keydown, a po puszczeniu - keyup. W praktyce, na ogół obsługiwane jest tylko zdarzenie keydown, ponieważ jest ono bardziej responsywne na akcję użytkownika niż keyup, czyli użytkownik wcześniej zobaczy wynik naciśnięcia klawisza. Zdarzenia keydown i keyup są uruchamiane po naciśnięciu dowolnego klawisza, w tym klawiszy Ctrl, Shift, Alt, Escape i inne.

Kiedyś istniało inne zdarzenie związane z klawiaturą o nazwie keypress. Wiele postów na forach i blogach może nadal z niego korzystać, ale bądź ostrożny - jest przestarzałe, a wsparcie w nowych przeglądarkach może zakończyć się w dowolnym momencie.

Właściwości key i code

Właściwość key zwraca znak wygenerowany przez naciśnięcie klawisza, biorąc pod uwagę stan klawiszy modyfikujących, takich jak Shift, a także bieżący język. Właściwość code zwraca kod fizycznego klawisza na klawiaturze i nie zmienia się w zależności od ustawienia klawiatury użytkownika.

            document.addEventListener("keydown", event => {
                console.log("key: ", event.key);
                console.log("code: ", event.code);
            });
          

Ustaw focus na okno przykładu, klikając je myszą, śledzenie zdarzeń klawiatury będzie na elemencie document. Wpisz coś na klawiaturze i zobacz wynik.

Przykład -------------------------------

            const clearLogBtn = document.querySelector(".js-clear");
            const logList = document.querySelector(".log-list");
            let keypressCounter = 1;

            console.log(clearLogBtn)
            document.addEventListener("keydown", logMessage);
            document.addEventListener("keyup", logMessage);
            clearLogBtn.addEventListener("click", reset);

            function logMessage({ type, key, code }) 
                const markup = `div class="log-item">
                span class="chip">${keypressCounter}/span>
                ul>
                    li>b>Event/b>: ${type}/li>
                    li>b>Key/b>: ${key}/li>
                    li>b>Code/b>: ${code}/li>
                /ul>  
                /div>`;

                logList.insertAdjacentHTML("afterbegin", markup);
                if (type === "keyup") {
                    incrementKeypressCounter();
                }
              }

            function reset() {
                keypressCounter = 1;
                logList.innerHTML = "";
            }

            function incrementKeypressCounter() {
                keypressCounter += 1;
            }
          

PrzykładEND -------------------------------

Klawisze modyfikujące

Aby obsłużyć kombinację klawiszy, na przykład Ctrl + s lub dowolną inną, obiekt zdarzenia ma właściwości ctrlKey, altKey, shiftkey i metaKey, które przechowują wartość logiczną wskazującą, czy klucz modyfikujący został naciśnięty, czy nie.

            document.addEventListener("keydown", event => {
              event.preventDefault();

              if ((event.ctrlKey || event.metaKey) && event.code === "KeyS") {
                console.log("«Ctrl + s» or «Command + s» combo");
              }
            });
          

Niektóre skróty klawiszowe mogą powodować konflikty z domyślnym zachowaniem przeglądarki. Na przykład Ctrl + d albo Command + d tworzy zakładkę. Należy starać się zaprojektować skróty klawiszowe tak, aby nie pokrywały się z wbudowanymi w przeglądarkę. Jednak w wyjątkowych okolicznościach, domyślne zachowanie można anulować, wywołując metodę event.preventDefault().

Nie tak dawno temu zamiast właściwości key i code używano właściwości keyCode. Wiele postów na forach i blogach może nadal jej używać, ale uważaj - jest przestarzała, nie używaj właściwości keyCode.

Zdarzenia elementów formularza

Zdarzenie submit

Przesłanie formularza następuje po kliknięciu przycisku z atrybutem type="submit" lub naciśnięciu klawisza Enter, w dowolnym z jego pól tekstowych. Zdarzenie submit może być użyte do walidacji formularza przed przesłaniem, ponieważ obiekt zdarzenia ma wiele przydatnych właściwości związanych z elementami formularza. Przesłanie formularza ponownie ładuje stronę, więc pamiętaj, aby anulować domyślną akcję za pomocą metody preventDefault().

Przykład -------------------------------

            const registerForm = document.querySelector(".form");

            registerForm.addEventListener("submit", handleSubmit);

            function handleSubmit(event) {
              event.preventDefault();
              const form = event.target;
              const login = form.elements.login.value;
              const password = form.elements.password.value;
  
              if (login === "" || password === "") {
                return console.log("Please fill in all the fields!");
              }

              console.log(`Login: ${login}, Password: ${password}`);
              form.reset();
            }
          

PrzykładEND -------------------------------

Właściwość elements elementu DOM formularza zawiera obiekt z linkami do wszystkich jego elementów, które mają atrybut name. Dlatego w przykładzie otrzymujemy wartość pól odwołując się do login.value i password.value.

Zdarzenie change

Występuje po zmianie elementu formularza. W przypadku pól tekstowych lub textarea zdarzenie wystąpi nie za każdym razem, gdy zostanie wprowadzony znak, ale w przypadku utraty fokusu, co nie zawsze jest wygodne. Na przykład, gdy wpisujesz coś w polu tekstowym - nie ma zdarzenia, ale jak tylko fokus zniknie, nastąpi zdarzenie change. W przypadku innych elementów, na przykład select, pól wyboru i przycisków radiowych, zdarzenie change jest wyzwalane natychmiast po wybraniu wartości.

Przykład -------------------------------

            const select = document.querySelector(".pizza-select");
            const textOutput = document.querySelector(".text-output");
            const valueOutput = document.querySelector(".value-output");

            select.addEventListener("change", setOutput);

            function setOutput(event) {
              const selectedOptionValue = event.currentTarget.value;
              const selectedOptionIndex = event.currentTarget.selectedIndex;
              const selectedOptionText =
              event.currentTarget.options[selectedOptionIndex].text;

              textOutput.textContent = selectedOptionText;
              valueOutput.textContent = selectedOptionValue;
            }
          

Selected option text: none

Selected option value: none

PrzykładEND -------------------------------

Zwróć uwagę w przykładzie na przydatne właściwości podczas pracy z elementem "select". Sprawdź, co jest przechowywane we właściwościach value, selectedIndex i options.

Zdarzenie input

Występuje tylko w polach tekstowych i textarea i jest generowane za każdym razem, gdy zmienia się wartość elementu, bez czekania na utratę fokusu. W praktyce to właśnie input jest najważniejszym zdarzeniem przy pracy z polami tekstowymi formularza.

Przykład -------------------------------

            const textInput = document.querySelector(".text-input");
            const output = document.querySelector(".output");

            textInput.addEventListener("input", (event) => {
              output.textContent = event.currentTarget.value;
            });
          

Text field value:

PrzykładEND -------------------------------

Zdarzenie focus i blur

Element otrzymuje fokus po kliknięciu myszą lub "przejściu" na niego klawiszem Tab. Moment fokusu i utraty fokusu jest bardzo ważny, ponieważ przy fokusie możemy załadować dane do autouzupełniania, rozpocząć śledzenie zmian itp. W przypadku utraty fokusu często sprawdzamy wprowadzone dane.

Gdy element posiada fokus, pojawia się zdarzenie focus, a gdy fokus znika, na przykład użytkownik kliknie w innym miejscu ekranu, następuje zdarzenie blur. Możesz aktywować lub anulować fokus w obrębie JavaScriptu, wywołując w kodzie metody o odpowiedniej nazwie focus() i blur() na elemencie

Przykład -------------------------------

            const textInput = document.querySelector(".text-input");
            const setFocusBtn = document.querySelector('[data-action="set"]');
            const removeFocusBtn = document.querySelector('[data-action="remove"]');

            setFocusBtn.addEventListener("click", () => {
              textInput.focus();
            });

            removeFocusBtn.addEventListener("click", () => {
              textInput.blur();
            });

            textInput.addEventListener("focus", () => {
              textInput.value = "This input has focus";
            });

            textInput.addEventListener("blur", () => {
              textInput.value = "";
            });
          


PrzykładEND -------------------------------

Focus może być stanem tylko w jednym elemencie strony w danym momencie, a bieżący element, który ma fokus, jest dostępny jako document.activeElement.

Wiele elementów nie może otrzymać fokusu. Na przykład, jeśli klikniesz na "div", nie ustawi się na nim focus, ponieważ nie jest to element interaktywny.