INF.03 · JavaScript

Model DOM — wybieranie i modyfikowanie elementów

Kod wybiera element, strona pokazuje zmianę

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

Po tej lekcji: Wybierzesz właściwe elementy DOM i zmienisz tekst, klasę oraz atrybut.

DOM to drzewo obiektów

Po odczytaniu HTML przeglądarka buduje DOM. document reprezentuje dokument; element ma rodzica, dzieci, tekst i atrybuty. getElementById("wynik") szuka po id bez #. querySelector("#wynik") używa selektora CSS i zwraca pierwszy pasujący element albo null. querySelectorAll(".karta") zwraca statyczną NodeList wszystkich pasujących; możesz przejść po niej for...of lub forEach.

Tekst, klasa, atrybut — różne zadania

textContent wstawia zwykły tekst. innerHTML interpretuje znaczniki: nie wkładaj tam wpisów użytkownika, bo to ryzyko XSS. classList.add/remove/toggle zmienia klasy bez kasowania pozostałych. style zmienia pojedynczy styl inline, lecz do zestawu reguł lepsza jest klasa. setAttribute ustawia np. aria-label. Wartość input odczytujesz przez value, nie textContent.

Dlaczego element jest null?

Sprawdź nazwę id, # w selektorze, moment wykonania skryptu i istnienie elementu na tej podstronie. defer pomaga, ale nie naprawia literówki. W Elements porównaj drzewo przed i po operacji: Wyświetl źródło pokazuje pierwotny HTML, a nie wszystkie późniejsze zmiany DOM.

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ę.

Selektor: wybierz element i sprawdź rezultat

Do body dodaj <article class="karta" id="film"><h2>Ocean</h2></article>. Zobacz różnicę między identyfikatorem, klasą i nazwą znacznika.

Podstawowa forma: document.querySelector(selektorCSS) → pierwszy element albo null

selector-js.jsjavascript
const karta = document.querySelector('#film');
const tytul = karta.querySelector('h2');
tytul.textContent = 'Kosmos';
karta.classList.add('wyrozniona');
console.log(document.querySelectorAll('.karta').length);
  1. #film wskazuje id="film", .karta klasę karta, a h2 nazwę znacznika. querySelector na karcie szuka w jej wnętrzu.
  2. textContent zastępuje tekst. Jeśli wpiszesz <b>Kosmos</b>, pozostanie dosłownym tekstem; innerHTML interpretowałoby HTML.
  3. classList.add dodaje jedną klasę. remove usuwa wskazaną, toggle przełącza jej obecność — pozostałe klasy pozostają.
  4. querySelectorAll zwraca statyczną NodeList. length jest liczbą znalezionych elementów. Brak dopasowania w querySelector to null, więc literówka może wywołać błąd przy odczycie jego właściwości.

Właściwość, styl i atrybut to różne zapisy

Do HTML dodaj <button id="opis" type="button">Opis</button>. Zmień jego podpis, kolor i atrybut dostępności — świadomie trzema operacjami.

Podstawowa forma: element.setAttribute(nazwa, wartosc); element.style.wlasciwosc = wartosc

attributes-js.jsjavascript
const przycisk = document.querySelector('#opis');
przycisk.textContent = 'Pokaż opis';
przycisk.style.backgroundColor = '#5ee4c6';
przycisk.setAttribute('aria-label', 'Pokaż opis filmu');
  1. textContent zmienia widoczny tekst przycisku.
  2. style.backgroundColor ustawia jedną właściwość stylu inline. Nazwa CSS background-color w tym zapisie JS ma postać backgroundColor. Większy zestaw reguł zwykle lepiej przełączać klasą.
  3. setAttribute dostaje nazwę atrybutu i jego wartość jako dwa napisy. aria-label jest dostępną nazwą przycisku; nie jest kolorem ani zdarzeniem.

Wygrana i remis: some, every i rozpakowanie tablicy

Pełna plansza sprawdza osiem możliwych linii. Pokażmy samą zasadę na jednej trójce indeksów i jednym gotowym wierszu.

Podstawowa forma: tablica.some(test) — czy choć jeden?; tablica.every(test) — czy wszystkie?

some-every.jsjavascript
const pola = ['O', 'O', 'O', '', '', '', '', '', ''];
const linie = [[0, 1, 2]];
const wygrana = linie.some(([a, b, c]) =>
    pola[a] !== '' && pola[a] === pola[b] && pola[b] === pola[c]
);
const pelna = pola.every(znak => znak !== '');
console.log(wygrana);
console.log(pelna);
Wynik
true
false
  1. linie jest tablicą tablic. Callback some dostaje jeden element, tutaj [0,1,2]. Zapis ([a,b,c]) rozpakowuje jego trzy wartości do nazw a,b,c.
  2. pola[a] wybiera pole o indeksie0. Warunek wymaga niepustego znaku i równości wszystkich trzech wartości. && wymaga spełnienia każdego porównania.
  3. some oddaje true po pierwszej pasującej linii. W pełnym projekcie jest8 trójek:3 wiersze,3 kolumny,2 przekątne.
  4. every sprawdza wszystkie pola. Tu są puste napisy, więc false: nie ma jeszcze remisu. Wygraną sprawdzamy przed pełnością planszy.

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);
&& / || / ??&& 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.

Elementy dla przykładu

index.htmlhtml
<article id="karta" class="karta"><h2>Film</h2><a href="#szczegoly">Szczegóły</a></article>

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

skrypt.jsjavascript
const karta = document.querySelector("#karta");
const tytul = karta.querySelector("h2");
tytul.textContent = "Nowa premiera";
karta.classList.toggle("wyrozniona");
karta.setAttribute("aria-label", "Wyróżniona premiera");
for (const link of document.querySelectorAll(".karta a")) {
    link.classList.add("aktywny");
}

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

Zmień tytuł karty, przełącz wyróżnienie i sprawdź w Elements klasę. Wpisz <b>Film</b>: ma pozostać tekstem.

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. Wybierzesz właściwe elementy DOM i zmienisz tekst, klasę oraz atrybut.
  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