INF.03 · JavaScript

Obsługa zdarzeń — myszy i klawiatury

Kliknięcie, Enter i wpisywanie to różne zdarzenia

  • Kod → próba → wniosek
  • DOM i zdarzenia
  • Własny projekt

Po tej lekcji: Podepniesz zdarzenie do elementu i obsłużysz formularz także klawiaturą.

Zarejestruj reakcję, nie uruchamiaj jej od razu

element.addEventListener("click", funkcja) przekazuje funkcję do późniejszego wykonania. funkcja() wywołałaby ją teraz. Obiekt zdarzenia zawiera m.in. target i key. input reaguje w trakcie edycji, change zwykle po zatwierdzeniu wartości. keydown to naciśnięcie klawisza. Używaj zwykłego button zamiast klikalnego div: dostajesz fokus i klawiaturę.

Formularz ma zdarzenie submit

Obsługa submit działa przy przycisku oraz Enter w odpowiednim polu. preventDefault zatrzymuje domyślne wysłanie, np. w lokalnym kalkulatorze. Nie blokuj wszystkich Enter na document. Przyciski pomocnicze w formularzu powinny mieć type="button", bo domyślny button może wysłać formularz. hover nie wystarcza na telefonie; istotna akcja potrzebuje kliknięcia i widocznego fokusu.

Prześledź jedno zdarzenie

W modelu najpierw kliknij przycisk, potem wpisz imię i naciśnij Enter. To dwie drogi do tego samego submit. Z kolei suwak wywołuje input. W planszy każdy ruch jest click, a funkcja sprawdza stan przed zmianą. Zmiana wyglądu przycisku nie zastępuje reguły blokującej ponowny ruch.

Najpierw krótki przykład, potem cały projekt

Każdy krótki przykład uruchamiaj osobno: w oddzielnym pliku skrypt.js lub na nowej pustej stronie. Nie doklejaj kilku przykładów z tym samym const do jednego pliku, bo ponowna deklaracja tej samej nazwy jest błędem. Przykłady konsolowe pokażą wynik w F12 → Console. Przy przykładach DOM najpierw utwórz wskazane elementy HTML i podłącz skrypt z defer. Fragment wymagający gotowego projektu jest tak oznaczony — wykonuj go w jego kontekście, nie jako pustą stronę.

addEventListener: reakcja czeka na zdarzenie

W HTML potrzebujesz <button id="pokaz">Pokaż</button><p id="wynik"></p>. Skrypt podłącz z defer. Zarejestruj funkcję, zamiast wywoływać ją podczas ładowania.

Podstawowa forma: element.addEventListener("nazwaZdarzenia", funkcja);

listener-js.jsjavascript
const przycisk = document.querySelector('#pokaz');
function pokaz(zdarzenie) {
    document.querySelector('#wynik').textContent = 'Kliknięto';
    console.log(zdarzenie.type);
}
przycisk.addEventListener('click', pokaz);
  1. Pierwszy argument click jest nazwą zdarzenia; drugi to funkcja bez nawiasów wywołania. pokaz() wykonałoby się teraz, a nie po kliknięciu.
  2. Przeglądarka przekazuje do pokaz obiekt zdarzenia. type daje nazwę, target element będący źródłem zdarzenia, currentTarget element z tym listenerem.
  3. Wciśnięcie Enter lub spacji na przycisku również uruchamia kliknięcie. Dlatego button jest lepszy od div udającego przycisk.

input i keydown: co użytkownik właśnie zrobił?

Do body dodaj <input id="imie"><p id="licznik"></p>. input reaguje na zmianę wartości, także wklejenie tekstu. keydown reaguje na naciśnięcie klawisza.

Podstawowa forma: pole.addEventListener("input", callback); klawisz odczytasz z zdarzenie.key

input-key-js.jsjavascript
const pole = document.querySelector('#imie');
pole.addEventListener('input', () => {
    document.querySelector('#licznik').textContent = pole.value.length;
});
pole.addEventListener('keydown', (zdarzenie) => {
    if (zdarzenie.key === 'Escape') {
        pole.value = '';
        document.querySelector('#licznik').textContent = '0';
    }
});
  1. pole.value daje bieżący tekst. length to długość napisu liczona w jednostkach UTF-16, więc nie dla każdego emoji oznacza liczbę widocznych znaków.
  2. W keydown porównujemy key z napisem Escape. Dla innych klawiszy blok if się nie wykona.
  3. Ustawienie value w kodzie samo nie wywołuje input, dlatego po Escape aktualizujemy także licznik. Do wysyłania formularza służy submit, nie globalne przechwycenie Enter.

Plansza: fill, forEach, disabled i fokus

Zanim czytasz pełną grę, zobacz prosty stan planszy. Pusty napis oznacza wolne pole; nie jest obrazkiem pustego przycisku.

Podstawowa forma: Array(9).fill("") tworzy dziewięć pustych pól; tablica.forEach((wartosc, indeks) => {...}) odwiedza je po kolei

array-game.jsjavascript
const pola = Array(9).fill('');
pola[0] = 'O';
pola.forEach((znak, indeks) => {
    if (indeks < 2) console.log(indeks, znak || 'puste');
});
console.log(pola.length);
Wynik
0 O
1 puste
9
  1. Array(9) tworzy tablicę o długości9, a fill wypełnia miejsca pustym napisem. Na indeks0 wpisujemy O.
  2. forEach przekazuje do callbacku znak i indeks. Nie są to globalne zmienne ani słowa kluczowe — sami wybraliśmy ich nazwy.
  3. znak || "puste" wybiera drugi napis, gdy znak jest pusty. To nie to samo co ??, które reaguje tylko na null/undefined.
  4. W pełnej grze disabled=true blokuje przycisk zajętego pola. focus() przenosi fokus klawiatury na kolejny dostępny przycisk; nie wykonuje za niego ruchu.

Jak czytać pozostałe zapisy w pełnym przykładzie

ZapisZnaczeniePrzykład użycia
some / everysome pyta, czy choć jeden element spełnia warunek, every czy wszystkie. Dla pustej tablicy some=false, every=true.pola.every(znak => znak !== "")
([a,b,c]) => ...Parametr callbacku zostaje rozpakowany z jednej tablicy do trzech nazw. To nie trzy osobne argumenty przekazane przez some.linie.some(([a,b,c]) => pola[a] === pola[b])
warunek ? A : BWyrażenie warunkowe zwraca A przy prawdzie, B przy fałszu. Można go użyć po prawej stronie przypisania.gracz = gracz === "O" ? "X" : "O";
Array(9).fill("")Tworzy dziewięć pozycji i wypełnia je pustym napisem. fill z obiektem współdzieliłoby ten sam obiekt; tutaj używamy napisów.let pola = Array(9).fill("");
replaceChildren / appendPierwsza metoda zastępuje wszystkie dzieci elementu, druga dopisuje podane węzły/teksty na końcu.karta.append(tytul, data, lista);
addEventListener / preventDefaultPierwsza metoda rejestruje funkcję na zdarzenie, druga zatrzymuje jego domyślną akcję (np.wysłanieformularza), nie usuwa innych listenerów.form.addEventListener("submit", e => e.preventDefault());
&& / || / ??&& wymaga prawdy po obu stronach, || wybiera także dla pustego napisu/0, ?? tylko dla null/undefined. Te operatory mogą zwracać operandy, nie wyłącznie bool.znak || "puste"
=== i !==Porównanie bez konwersji typów. Przypisanie ma pojedyncze =.7 === "7" // false

Sprawdź w działaniu

Sprawdź mechanizm na działającym przykładzie

Zmień dane, przewidź wynik i wykonaj próbę. To działający JavaScript tego modelu, nie interpreter dowolnego wklejonego kodu.

Ta interakcja wymaga włączonego JavaScriptu.

HTML w body pliku index.html

index.htmlhtml
<form id="formularz"><label for="imie">Imię</label>
<input id="imie"><button>Przywitaj</button></form>
<p id="wynik"></p>

Prześledź kod, potem zmień jedną rzecz

skrypt.jsjavascript
const formularz = document.querySelector("#formularz");
formularz.addEventListener("submit", (zdarzenie) => {
    zdarzenie.preventDefault();
    const imie = document.querySelector("#imie").value.trim();
    document.querySelector("#wynik").textContent = imie ? `Cześć, ${imie}!` : "Wpisz imię.";
});

Odtwórz przykład u siebie

Zapisz pokazany JavaScript w skrypt.js, a HTML w body zwykłego dokumentu z <!doctype html>, lang="pl", meta charset="UTF-8" i tytułem. W head podłącz <script src="skrypt.js" defer></script>. Przykłady z samym console.log możesz wykonać też w konsoli własnej pustej strony. Nie wklejaj nieznanych skryptów do konsoli zalogowanych serwisów. F12 → Console: sprawdź błędy; Sources: ustaw breakpoint, obejrzyj zmienne i przejdź krokowo.

Projekt: kółko i krzyżyk — widok wynika ze stanu

INF.03-07-26.06-SG wymagał naprzemiennych ruchów, aktualizacji widoku i zablokowania zajętego pola. Nasz wariant dodatkowo sprawdza zwycięstwo i remis; ma własny układ przycisków.

1. Tablica pola ma 9 pustych napisów. gracz przechowuje O lub X, koniec blokuje dalszą grę. rysuj tworzy przyciski z nazwą pola i znakiem; disabled wynika ze stanu.

2. Kliknięcie przekazuje indeks do wykonajRuch. Najpierw sprawdza blokadę, potem zapisuje znak. To ważne: sam kolor pola nie zabezpiecza przed zmianą.

3. Osiem trójek indeksów opisuje wiersze, kolumny i przekątne. some szuka zwycięskiej trójki; every sprawdza zapełnienie. Jeśli nikt nie wygrał i zostały miejsca, zmień gracza i przerysuj.

4. Zagraj zwycięski wiersz, przekątną, remis; kliknij zajęte pole i zresetuj grę. Sprawdź także Tab i Enter. Nie używamy zdjęć ani treści z załączników egzaminu.

Sprawdź w działaniu

Uruchom gotowy projekt i porównaj z kodem

W podglądzie działa gotowy kod. Komplet HTML/CSS/JS pobierzesz w paczce na końcu. Podgląd jest izolowany od konta BitEdu.

Ta interakcja wymaga włączonego JavaScriptu.

Pełny projekt: index.html

plansza/index.htmlhtml
<!doctype html>
<html lang="pl">
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <title>
            Kółko i krzyżyk / stan gry
        </title>
        <link rel="stylesheet" href="styl.css">
        <script src="skrypt.js" defer>
        </script>
    </head>
    <body>
        <main>
            <h1>
                Kółko i krzyżyk / stan gry
            </h1>
            <p>
                Dwie osoby grają przy jednym ekranie. Zajętego pola nie można zmienić. Własne rozszerzenie: wykrywanie wygranej i remisu.
            </p>
            <div id="plansza" class="plansza" aria-label="Plansza trzy na trzy">
            </div>
            <p id="status" role="status">
            </p>
            <button id="reset" type="button">
                Nowa gra
            </button>
        </main>
    </body>
</html>

Pełny projekt: skrypt.js

plansza/skrypt.jsjavascript
const plansza = document.querySelector('#plansza');
const status = document.querySelector('#status');
const linie = [[0,1,2], [3,4,5], [6,7,8], [0,3,6], [1,4,7], [2,5,8], [0,4,8], [2,4,6]];
let pola = Array(9).fill('');
let gracz = 'O';
let koniec = false;
function rysuj() {
    plansza.replaceChildren();
    pola.forEach((znak, indeks) => {
        const przycisk = document.createElement('button');
        przycisk.type = 'button';
        przycisk.textContent = znak || '·';
        przycisk.setAttribute('aria-label', `Pole ${indeks + 1}: ${znak || 'puste'}`);
        przycisk.disabled = koniec || znak !== '';
        przycisk.addEventListener('click', () => wykonajRuch(indeks));
        plansza.appendChild(przycisk);
    });
}
function wykonajRuch(indeks) {
    if (koniec || pola[indeks] !== '') return;
    pola[indeks] = gracz;
    const wygrana = linie.some(([a,b,c]) => pola[a] !== '' && pola[a] === pola[b] && pola[b] === pola[c]);
    if (wygrana) {
        koniec = true;
        status.textContent = `Wygrywa ${gracz}.`;
    } else if (pola.every(znak => znak !== '')) {
        koniec = true;
        status.textContent = 'Remis.';
    } else {
        gracz = gracz === 'O' ? 'X' : 'O';
        status.textContent = `Ruch: ${gracz}.`;
    }
    rysuj();
    const nastepne = plansza.querySelector('button:not(:disabled)');
    if (nastepne) nastepne.focus();
}
document.querySelector('#reset').addEventListener('click', () => {
    pola = Array(9).fill(''); gracz = 'O'; koniec = false;
    status.textContent = 'Ruch: O.'; rysuj();
});
status.textContent = 'Ruch: O.';
rysuj();

Wygląd też jest częścią paczki

Folder projektu zawiera również pełny styl.css, a karty — dane.json. Rozpakuj ZIP JS / WARSZTAT ze „Źródeł i materiałów”. Otwórz index.html; dla dodatkowego odczytu JSON uruchom lokalny serwer opisany w START.txt. Podgląd nie zastępuje wykonania własnej wersji.

Ćwiczenie z pomocą · samodzielna praca

Twoja próba

Dodaj licznik znaków reagujący na input. Wyślij formularz myszą i Enter. Wynik ma być taki sam.

Oczekiwany rezultat
Zapisz: dane → przewidywany wynik → wynik próby → wyjaśnienie.
Potrzebujesz pomocy? Rozwiązuj krok po kroku.
Krok 1 — pokaż podpowiedź

Zacznij od działającego przykładu i przewidź wynik bez uruchamiania.

Krok 2 — pokaż podpowiedź

Zmień jedną instrukcję lub jedną wartość. Obejrzyj Console i stan DOM.

Krok 3 — pokaż podpowiedź

Dodaj przypadek pusty albo graniczny. Jeśli wynik jest inny, ustaw breakpoint.

Otwórz tylko tyle podpowiedzi, ile potrzebujesz. Po każdym kroku wróć do zadania i sprawdź wynik.

Podsumowanie

Co warto zapamiętać

  1. Podepniesz zdarzenie do elementu i obsłużysz formularz także klawiaturą.
  2. Umiesz wskazać przyczynę wyniku w kodzie i sprawdzić przypadek graniczny.
  3. Quiz i dodatkowe zadania są w biurku; kod ocenia nauczyciel.

Dalej na swoim koncie

Rozgrzewki, quiz i zadania są w biurku ucznia.

Teoria jest otwarta dla wszystkich. Żeby rozwiązywać zadania, zapisywać szkice i widzieć swój postęp, zaloguj się.

Zaloguj się →
Źródła i dalsza nauka