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
const karta = document.querySelector('#film');
const tytul = karta.querySelector('h2');
tytul.textContent = 'Kosmos';
karta.classList.add('wyrozniona');
console.log(document.querySelectorAll('.karta').length);
- #film wskazuje id="film", .karta klasę karta, a h2 nazwę znacznika. querySelector na karcie szuka w jej wnętrzu.
- textContent zastępuje tekst. Jeśli wpiszesz <b>Kosmos</b>, pozostanie dosłownym tekstem; innerHTML interpretowałoby HTML.
- classList.add dodaje jedną klasę. remove usuwa wskazaną, toggle przełącza jej obecność — pozostałe klasy pozostają.
- 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
const przycisk = document.querySelector('#opis');
przycisk.textContent = 'Pokaż opis';
przycisk.style.backgroundColor = '#5ee4c6';
przycisk.setAttribute('aria-label', 'Pokaż opis filmu');
- textContent zmienia widoczny tekst przycisku.
- 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ą.
- 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?
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);
true false
- 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.
- 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.
- some oddaje true po pierwszej pasującej linii. W pełnym projekcie jest8 trójek:3 wiersze,3 kolumny,2 przekątne.
- 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
| Zapis | Znaczenie | Przykład użycia |
|---|---|---|
some / every | some 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 : B | Wyraż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 / append | Pierwsza 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.
Elementy dla przykładu
<article id="karta" class="karta"><h2>Film</h2><a href="#szczegoly">Szczegóły</a></article>
Prześledź kod, potem zmień jedną rzecz
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.
Pełny projekt: index.html
<!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
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.
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ć
- Wybierzesz właściwe elementy DOM i zmienisz tekst, klasę oraz atrybut.
- Umiesz wskazać przyczynę wyniku w kodzie i sprawdzić przypadek graniczny.
- 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
- JS / WARSZTAT.zip — 4 pełne projekty i START.txt
- CKE — informator INF.03 (wymagania INF.03.5)
- CKE — arkusz INF.03-02-24.06-SG, czat (kopia szkoły)
- CKE — arkusz INF.03-07-26.06-SG, plansza (kopia archiwalna)
- CKE — arkusz INF.03-06-26.06-SG, dane i DOM (kopia archiwalna)
- MDN — przewodnik JavaScript
- MDN — const
- MDN — fetch
- Dokumentacja składni omawianego języka