Przejdź do głównej treści
INF.03-02-25.06-SGCzerwiec 2025 · 34 kroki · ok. 81 min · 37 kryteriów z klucza

Rozwiązanie krok po kroku: Planer zadań z notatkami (Nowoczesny kod (grid))

To drugi sposób rozwiązania tego samego arkusza: planer zadań z bazą notatki, stroną notatki.html i skryptem, który przekreśla wykonane zadania i dodaje nowe. Obie wersje spełniają wszystkie 37 kryteriów klucza - różni je sposób pisania kodu.

Czym ta wersja różni się od podstawowej:

  • siatka CSS z nazwanymi obszarami (grid-template-areas) zamiast pięciu bloków z float: left - układ z ilustracji 3 jest narysowany wprost w kodzie, a szerokości 20% / 80% stoją w grid-template-columns, co klucz dopuszcza wprost,
  • kolory z arkusza w zmiennych CSS w :root,
  • przekreślenie przez klasę CSS (.done) zamiast stylu wpisywanego z JavaScriptu - cały wygląd zostaje w styl.css,
  • jeden nasłuchiwacz na całej liście (delegacja zdarzeń) zamiast onclick przy każdym przycisku - działa też dla zadań dodanych później,
  • pole i przycisk „Dodaj” w formularzu - zadanie dodaje się także klawiszem Enter,
  • skrypt podpięty w <head> z atrybutem defer, textContent zamiast innerText, zapytania SQL z aliasami i JOIN ... ON.

Kroki nadal idą zdanie po zdaniu za treścią arkusza, a podgląd rośnie z każdym krokiem. Obraz, baza i zadania.txt pochodzą z prawdziwego archiwum pliki2; zrzuty to wyniki zapytań wykonanych na tej bazie w MariaDB.

Jak liczą się punkty. Klucz ma 37 kryteriów (R.1.1 - R.5.7), każde za 1 punkt. Próg zdania części praktycznej to 75%, czyli 28 punktów. Przy każdym kroku jest napisane, które kryteria spełnia; egzaminator ocenia spełnienie kryteriów z klucza, nie zgodność z tym kodem.

  1. Etap 1: Baza danych

    Kroki 1-6 · po tym etapie masz 6 z 37 kryteriów (pkt)

    Krok 1: Baza danych notatki

    Kryteria:

    R.1.1 Wykonany import tabel do bazy danych notatki czynność udokumentowana w pliku import.png

    ok. 2 min
    Polecenie z arkusza

    Utwórz bazę danych o nazwie notatki, z zestawem polskich znaków (np. utf8_unicode_ci).

    W phpMyAdmin Nowa, nazwa notatki, porównywanie utf8_unicode_ci - albo jedno zapytanie w zakładce SQL:

    CREATE DATABASE notatki CHARACTER SET utf8 COLLATE utf8_unicode_ci;
    
    Typowe błędy

    Baza i jedna z tabel nazywają się tak samo (notatki) - baza to pojemnik, tabela leży w środku. Zapytania wykonuj przy zaznaczonej bazie.

  2. Krok 2: Import tabel i zrzut import.png

    Kryteria:

    R.1.1 Wykonany import tabel do bazy danych notatki czynność udokumentowana w pliku import.png

    ok. 3 min
    Polecenie z arkusza

    Do bazy zaimportuj tabele z pliku baza.sql z rozpakowanego archiwum. Wykonaj zrzut ekranu po imporcie. Zrzut zapisz w formacie PNG pod nazwą import. Nie kadruj zrzutu.

    Zaznacz bazę notatki, Import → baza.sql → Wykonaj. Powstają dwie tabele z ilustracji 1: osoby (4 wiersze) i notatki (13 wierszy) z kluczem obcym Osoby_id - jedna osoba ma wiele notatek. Nazwa Osoby_id ma wielką literę; w zapytaniach pisz ją tak samo, bo na serwerach linuksowych wielkość liter w nazwach bywa istotna.

    Potem Print Screen całego ekranu z paskiem zadań i zapis jako import.png (zrzuty kwerend będą w JPEG).

    Typowe błędy

    Wykadrowany zrzut (samo okno przeglądarki, bez paska zadań) nie jest oceniany.

  3. Krok 3: Zapytanie 1: najmniejszy priorytet osoby o id 3

    Kryteria: ,

    R.1.2 Zapisany plik kwerendy.txt zawierający co najmniej jedno zapytanie SQL, wynikające z treści zadania

    R.1.3 Wykonane zapytanie 1 wybierające jedynie najmniejszą wartość priorytetu notatki dla osoby o id równym 3. W pliku z kwerendami lub na zrzucie istnieje zapis np.: SELECT MIN(priorytet) FROM notatki WHERE Osoby_id = 3; rozwiązanie alternatywne z LIMIT 1 i ORDER BY oraz wynik działania udokumentowany zrzutem, na którym jest widoczna jedynie wartość 2

    ok. 3 min
    Polecenie z arkusza

    Zapytanie 1: wybierające jedynie najmniejszą wartość priorytetu notatki dla osoby o id równym 3.

    • SELECT MIN(priorytet) AS najnizszy_priorytet - funkcja grupująca zwraca jedną liczbę, a alias nadaje kolumnie wyniku nazwę, którą da się przeczytać (bez niego kolumna nazywa się MIN(priorytet)),
    • WHERE Osoby_id = 3 - arkusz podaje id, więc złączenie z tabelą osoby nie jest potrzebne.

    Wynik: 2. Zapytania zapisuj w kwerendy.txt z komentarzem -- nad każdym. Zrzut: kw1.jpg.

    kwerendy.txt Tekst nowy plik
    -- Zapytanie 1
    SELECT MIN(priorytet) AS najnizszy_priorytet
    FROM notatki
    WHERE Osoby_id = 3;
    Podgląd strony po tym kroku
    Podgląd strony po kroku 3
  4. Krok 4: Zapytanie 2: notatki z cząstką „na” w nazwie

    Kryteria:

    R.1.4 Wykonane zapytanie 2 wybierające jedynie nazwę notatki i jej priorytet dla notatek, których nazwa zawiera cząstkę „na”. W pliku z kwerendami lub na zrzucie istnieje zapis np.: SELECT nazwa, priorytet FROM notatki WHERE nazwa LIKE "%na%"; oraz wynik działania udokumentowany zrzutem, na którym są widoczne dokładnie 4 rekordy z danymi: Gimnastyka 1; Projekt na geografię 2; Wyjazd na wakacje 5; Weterynarz Brutus 5

    ok. 3 min
    Polecenie z arkusza

    Zapytanie 2: wybierające jedynie nazwę notatki i jej priorytet dla notatek, których nazwa zawiera cząstkę „na”.

    LIKE '%na%' - % to dowolny ciąg znaków, więc „na” może stać gdziekolwiek: Gimnastyka, Projekt na geografię, Wyjazd na wakacje, Weterynarz Brutus (4 wiersze). Napis we wzorcu w apostrofach - to zapis ze standardu SQL; cudzysłów działa w MariaDB, ale w innych bazach oznacza nazwę kolumny. Zrzut: kw2.jpg.

    Typowe błędy

    LIKE 'na%' znajdzie tylko nazwy zaczynające się od „na” - zero wierszy.

    kwerendy.txt Tekst zmienione: 5 linii
    -- Zapytanie 1
    SELECT MIN(priorytet) AS najnizszy_priorytet
    FROM notatki
    WHERE Osoby_id = 3;
    
    -- Zapytanie 2
    SELECT nazwa, priorytet
    FROM notatki
    WHERE nazwa LIKE '%na%';
    Podgląd strony po tym kroku
    Podgląd strony po kroku 4
  5. Krok 5: Zapytanie 3: imię i notatka dla priorytetu 5

    Kryteria:

    R.1.5 Wykonano zapytanie 3 wybierające jedynie imię osoby i odpowiadającą mu nazwę notatki dla notatek, których priorytet jest równy 5. W pliku z kwerendami lub na zrzucie istnieje zapis np.: SELECT imie, nazwa FROM notatki JOIN osoby ON osoby.id = Osoby_id WHERE priorytet=5; (możliwe również INNER JOIN lub porównanie kluczy po WHERE) oraz wynik działania udokumentowany zrzutem, na którym widoczne jest dokładnie 5 rekordów: dla Krzyśka wyświetlono Wyprowadzić psa, Spacer z kumplem; dla Eweliny wyświetlono Wyjazd na wakacje, Weterynarz Brutus, Lekarz

    ok. 4 min
    Polecenie z arkusza

    Zapytanie 3: wybierające jedynie imię osoby i odpowiadającą mu nazwę notatki dla notatek, których priorytet jest równy 5. Należy posłużyć się relacją.

    „Posłużyć się relacją” to złączenie tabel:

    • FROM notatki AS n JOIN osoby AS o - krótkie aliasy tabel n i o,
    • ON o.id = n.Osoby_id - warunek złączenia: id osoby równa się kluczowi obcemu notatki,
    • SELECT o.imie, n.nazwa i WHERE n.priorytet = 5 - każda kolumna z prefiksem aliasu, więc od razu widać, z której tabeli pochodzi (i nie ma kłopotu z id, które jest w obu).

    Zapis JOIN ... ON oddziela warunek łączenia tabel od warunku filtrowania (WHERE) - w starszym zapisie FROM notatki, osoby WHERE ... oba lądują w jednym miejscu i łatwo zgubić złączenie. Wynik: 5 wierszy (Krzysiek ×2, Ewelina ×3). Zrzut: kw3.jpg.

    kwerendy.txt Tekst zmienione: 6 linii
    -- Zapytanie 1
    SELECT MIN(priorytet) AS najnizszy_priorytet
    FROM notatki
    WHERE Osoby_id = 3;
    
    -- Zapytanie 2
    SELECT nazwa, priorytet
    FROM notatki
    WHERE nazwa LIKE '%na%';
    
    -- Zapytanie 3
    SELECT o.imie, n.nazwa
    FROM notatki AS n
    JOIN osoby AS o ON o.id = n.Osoby_id
    WHERE n.priorytet = 5;
    Podgląd strony po tym kroku
    Podgląd strony po kroku 5
  6. Krok 6: Zapytanie 4: liczba notatek każdej osoby

    Kryteria:

    R.1.6 Wykonane zapytanie 4 liczące dla każdego imienia osoby liczbę jego notatek. Kwerenda wybiera jedynie imię i odpowiadającą mu liczbę notatek. W pliku z kwerendami lub na zrzucie istnieje zapis np.: SELECT imie, COUNT(Osoby_id) FROM notatki JOIN osoby ON Osoby_id = osoby.id GROUP BY imie; (możliwe również INNER JOIN lub porównanie kluczy po WHERE, w COUNT * lub dowolne inne pole) oraz wynik działania udokumentowano zrzutem, na którym są widoczne dokładnie 4 rekordy z danymi: Ewelina 5; Grześ 1; Krzysiek 4; Paula 3

    ok. 4 min
    Polecenie z arkusza

    Zapytanie 4: liczące dla każdego imienia osoby liczbę jego notatek. Kwerenda wybiera jedynie imię i odpowiadającą mu liczbę notatek. Należy posłużyć się relacją.

    To samo złączenie, a do tego GROUP BY o.imie i COUNT(n.id) AS liczba_notatek: wiersze o tym samym imieniu tworzą grupę, a COUNT liczy notatki w każdej. Grupowanie po imieniu, bo arkusz mówi „dla każdego imienia” - przy dwóch osobach o tym samym imieniu wynik by je połączył (w tej bazie imiona się nie powtarzają). Wynik: Ewelina 5, Grześ 1, Krzysiek 4, Paula 3. Zrzut: kw4.jpg.

    Typowe błędy

    Kolumna spoza GROUP BY w SELECT (np. n.nazwa) to w MySQL 8 błąd, a w MariaDB przypadkowa wartość z grupy.

    kwerendy.txt Tekst zmienione: 6 linii
    -- Zapytanie 1
    SELECT MIN(priorytet) AS najnizszy_priorytet
    FROM notatki
    WHERE Osoby_id = 3;
    
    -- Zapytanie 2
    SELECT nazwa, priorytet
    FROM notatki
    WHERE nazwa LIKE '%na%';
    
    -- Zapytanie 3
    SELECT o.imie, n.nazwa
    FROM notatki AS n
    JOIN osoby AS o ON o.id = n.Osoby_id
    WHERE n.priorytet = 5;
    
    -- Zapytanie 4
    SELECT o.imie, COUNT(n.id) AS liczba_notatek
    FROM notatki AS n
    JOIN osoby AS o ON o.id = n.Osoby_id
    GROUP BY o.imie;
    Podgląd strony po tym kroku
    Podgląd strony po kroku 6
  7. Etap 2: Grafika i strona HTML

    Kroki 7-15 · po tym etapie masz 17 z 37 kryteriów (pkt)

    Krok 7: Przeskalowanie grafiki obraz.jpg

    Kryteria:

    R.2.1 Plik obraz.jpg przeskalowany z zachowaniem proporcji do wysokości 610 px, odpowiadająca temu szerokość wynosi 458 px (± 5 px)

    ok. 3 min
    Polecenie z arkusza

    Plik obraz.jpg, wypakowany z archiwum, należy przeskalować z zachowaniem proporcji do wysokości 610 px.

    Oryginał 480 × 640 px. W GIMP-ie Obraz → Skaluj obraz: wysokość 610 px przy zamkniętym łańcuchu - szerokość wyjdzie 458 px (480 · 610 / 640), a klucz sprawdza 458 ±5 px.

    Podgląd strony po tym kroku
    Podgląd strony po kroku 7
  8. Krok 8: Plik notatki.html: HTML5, język i kodowanie

    Kryteria:

    R.2.2 W pliku notatki.html zapisana deklaracja HTML5 <!DOCTYPE HTML>, deklaracja języka zawartości: polski np. <html lang="pl">, w sekcji head znajduje się jawnie podany standard kodowania polskich znaków

    ok. 2 min
    Polecenie z arkusza

    Składa się ze strony o nazwie notatki.html. Zapisana w języku HTML5. Zadeklarowany polski język zawartości witryny. Jawnie zastosowany właściwy standard kodowania polskich znaków.

    <!DOCTYPE html>, <html lang="pl"> i <meta charset="UTF-8"> jako pierwszy element <head>. Strona to czysty HTML z JavaScriptem - otwiera się nawet prosto z dysku, bez XAMPP-a.

    notatki.html HTML nowy plik
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
    </head>
    <body>
    </body>
    </html>
    Podgląd strony po tym kroku (notatki.html)
  9. Krok 9: Tytuł strony i arkusz stylów

    Kryteria: ,

    R.2.3 Nadany tytuł strony: Planer zadań

    R.3.2 Strona zawiera działające połączenie z zewnętrznym arkuszem stylów o nazwie styl.css, formatowanie CSS pochodzi jedynie z tego arkusza

    ok. 1 min
    Polecenie z arkusza

    Tytuł strony widoczny na karcie przeglądarki: „Planer zadań”. Arkusz stylów w pliku o nazwie styl.css prawidłowo połączony z kodem strony.

    <title> - tekst karty (R.2.3), <link rel="stylesheet" href="styl.css"> - połączenie z arkuszem stylów (R.3.2), bez ukośnika />. Meta viewport każe telefonom pokazywać stronę w prawdziwej szerokości ekranu.

    notatki.html HTML zmienione: 3 linie
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <title>Planer zadań</title>
        <link rel="stylesheet" href="styl.css">
    </head>
    <body>
    </body>
    </html>
    Podgląd strony po tym kroku (notatki.html)
  10. Krok 10: Bloki semantyczne

    Kryteria:

    R.2.4 W sekcji body zdefiniowany jest podział na bloki za pomocą semantycznych znaczników HTML 5: aside, header, nav (lub inny) , main, footer

    ok. 2 min
    Polecenie z arkusza

    Podział strony na blok boczny i obok niego bloki: blok nagłówkowy, blok dodający zadanie, blok główny, blok stopki. Podział strony na bloki zrealizowany za pomocą semantycznych znaczników bloków języka HTML5 tak, aby po uruchomieniu w przeglądarce układ bloków na stronie był zgodny z ilustracją 3.

    Znaczniki z tabeli 4 arkusza i klucza R.2.4: <aside>, <header>, <nav> (blok dodający zadanie), <main>, <footer>. Wszystkie są dziećmi <body>, a o tym, gdzie który stanie, zdecyduje siatka CSS - nie kolejność w kodzie. <aside> stoi pierwszy, bo tak czyta stronę czytnik ekranu, ale przy siatce z obszarami mógłby stać na końcu.

    notatki.html HTML zmienione: 10 linii
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <title>Planer zadań</title>
        <link rel="stylesheet" href="styl.css">
    </head>
    <body>
        <aside>
        </aside>
        <header>
        </header>
        <nav>
        </nav>
        <main>
        </main>
        <footer>
        </footer>
    </body>
    </html>
    Podgląd strony po tym kroku (notatki.html)
  11. Krok 11: Blok boczny: obraz

    Kryteria:

    R.2.6 W bloku bocznym umieszczony obraz o nazwie "obraz.jpg" z tekstem alternatywnym "notatki"

    ok. 1 min
    Polecenie z arkusza

    Zawartość bloku bocznego: obraz o nazwie obraz.jpg z tekstem alternatywnym „notatki”.

    Ścieżka względna obraz.jpg (plik obok strony) i tekst alternatywny dokładnie z arkusza.

    notatki.html HTML zmienione: 1 linia
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <title>Planer zadań</title>
        <link rel="stylesheet" href="styl.css">
    </head>
    <body>
        <aside>
            <img src="obraz.jpg" alt="notatki">
        </aside>
        <header>
        </header>
        <nav>
        </nav>
        <main>
        </main>
        <footer>
        </footer>
    </body>
    </html>
    Podgląd strony po tym kroku (notatki.html)
  12. Krok 12: Blok nagłówkowy

    ok. 1 min
    Polecenie z arkusza

    Zawartość bloku nagłówkowego: nagłówek drugiego stopnia o treści „Moje zadania”.

    <h2>, choć to pierwszy nagłówek na stronie - arkusz wskazuje stopień, a klucz R.2.5 sprawdza dokładnie ten znacznik.

    notatki.html HTML zmienione: 1 linia
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <title>Planer zadań</title>
        <link rel="stylesheet" href="styl.css">
    </head>
    <body>
        <aside>
            <img src="obraz.jpg" alt="notatki">
        </aside>
        <header>
            <h2>Moje zadania</h2>
        </header>
        <nav>
        </nav>
        <main>
        </main>
        <footer>
        </footer>
    </body>
    </html>
    Podgląd strony po tym kroku (notatki.html)
  13. Krok 13: Blok nawigacyjny: formularz z polem i przyciskiem

    Kryteria: ,

    R.2.7 W bloku nawigacyjnym umieszczone pole edycyjne i przycisk "Dodaj"

    R.3.3 W polu edycyjnym wyświetla się podpowiedź "Dodaj zadanie…", zastosowany do pola atrybut placeholder

    ok. 3 min
    Polecenie z arkusza

    Zawartość bloku nawigacyjnego: pole edycyjne z podpowiedzią o treści „Dodaj zadanie...”. Przycisk „Dodaj”, którego wciśnięcie powoduje wywołanie funkcji w skrypcie.

    Pole i przycisk są w <form> - dzięki temu zadanie doda się zarówno kliknięciem „Dodaj”, jak i klawiszem Enter w polu (przeglądarka wysyła formularz w obu przypadkach). Formularz nie ma action, bo nic nie trafia na serwer: wysłanie przechwyci skrypt.

    • placeholder - podpowiedź w pustym polu (R.3.3),
    • aria-label - nazwa pola dla czytnika ekranu; podpowiedź znika przy pisaniu, więc nie zastępuje etykiety,
    • required - przeglądarka nie wyśle pustego pola i sama pokaże komunikat,
    • type="submit" przy przycisku - to on wysyła formularz.
    Typowe błędy

    value="Dodaj zadanie..." zamiast placeholder wpisuje tekst na stałe - R.3.3 przepada.

    notatki.html HTML zmienione: 4 linie
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <title>Planer zadań</title>
        <link rel="stylesheet" href="styl.css">
    </head>
    <body>
        <aside>
            <img src="obraz.jpg" alt="notatki">
        </aside>
        <header>
            <h2>Moje zadania</h2>
        </header>
        <nav>
            <form id="task-form">
                <input type="text" id="task-input" placeholder="Dodaj zadanie..." aria-label="Nowe zadanie" required>
                <button type="submit">Dodaj</button>
            </form>
        </nav>
        <main>
        </main>
        <footer>
        </footer>
    </body>
    </html>
    Podgląd strony po tym kroku (notatki.html)
  14. Krok 14: Blok główny: lista sześciu zadań z przyciskami

    Kryteria: ,

    R.2.8 W bloku głównym umieszczona jedna lista punktowana z sześcioma elementami, zastosowane znaczniki <ul> i <li>

    R.2.9 Wewnątrz każdego elementu <li> znajduje się przycisk o treści "Wykonane"

    ok. 3 min
    Polecenie z arkusza

    Zawartość bloku głównego: lista punktowana (nieuporządkowana) zawierająca w stanie początkowym 6 elementów. Każdy element listy zawiera: wpis (kolejne wpisy można skopiować do kodu HTML z pliku zadania.txt), przycisk o treści „Wykonane”, którego wciśnięcie powoduje wywołanie skryptu dla danego elementu listy.

    <ul id="task-list"> z sześcioma <li> - teksty skopiowane z zadania.txt, a w każdym elemencie przycisk „Wykonane” (R.2.9). Przyciski nie mają onclick ani identyfikatorów: jeden nasłuchiwacz na liście obsłuży wszystkie, także te dodane później. type="button" mówi wprost, że przycisk niczego nie wysyła - gdyby lista kiedyś trafiła do formularza, nie zacząłby go wysyłać.

    Typowe błędy

    Przycisk poza <li> nie spełnia R.2.9, a skrypt nie wiedziałby, które zadanie przekreślić.

    notatki.html HTML zmienione: 8 linii
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <title>Planer zadań</title>
        <link rel="stylesheet" href="styl.css">
    </head>
    <body>
        <aside>
            <img src="obraz.jpg" alt="notatki">
        </aside>
        <header>
            <h2>Moje zadania</h2>
        </header>
        <nav>
            <form id="task-form">
                <input type="text" id="task-input" placeholder="Dodaj zadanie..." aria-label="Nowe zadanie" required>
                <button type="submit">Dodaj</button>
            </form>
        </nav>
        <main>
            <ul id="task-list">
                <li>Wyprowadzić psa <button type="button">Wykonane</button></li>
                <li>Gimnastyka <button type="button">Wykonane</button></li>
                <li>Zakupy <button type="button">Wykonane</button></li>
                <li>Spacer z kumplem <button type="button">Wykonane</button></li>
                <li>Odrabianie lekcji z młodszą siostrą <button type="button">Wykonane</button></li>
                <li>Projekt na geografię <button type="button">Wykonane</button></li>
            </ul>
        </main>
        <footer>
        </footer>
    </body>
    </html>
    Podgląd strony po tym kroku (notatki.html)
  15. Krok 15: Stopka z numerem zdającego

    Kryteria:

    R.2.5 W bloku nagłówkowym zapisany nagłówek <h2>: "Moje zadania", w bloku stopki nagłówek <h3>: "Notatki: " z numerem zdającego

    ok. 1 min
    Polecenie z arkusza

    Zawartość stopki: nagłówek trzeciego stopnia o treści: „Notatki: ”, dalej wstawiony numer zdającego.

    <h3> w <footer>. Tu stoi nazwisko autora rozwiązania - na egzaminie po dwukropku wpisujesz swój numer zdającego (PESEL).

    notatki.html HTML zmienione: 1 linia
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <title>Planer zadań</title>
        <link rel="stylesheet" href="styl.css">
    </head>
    <body>
        <aside>
            <img src="obraz.jpg" alt="notatki">
        </aside>
        <header>
            <h2>Moje zadania</h2>
        </header>
        <nav>
            <form id="task-form">
                <input type="text" id="task-input" placeholder="Dodaj zadanie..." aria-label="Nowe zadanie" required>
                <button type="submit">Dodaj</button>
            </form>
        </nav>
        <main>
            <ul id="task-list">
                <li>Wyprowadzić psa <button type="button">Wykonane</button></li>
                <li>Gimnastyka <button type="button">Wykonane</button></li>
                <li>Zakupy <button type="button">Wykonane</button></li>
                <li>Spacer z kumplem <button type="button">Wykonane</button></li>
                <li>Odrabianie lekcji z młodszą siostrą <button type="button">Wykonane</button></li>
                <li>Projekt na geografię <button type="button">Wykonane</button></li>
            </ul>
        </main>
        <footer>
            <h3>Notatki: Bartosz Bryniarski</h3>
        </footer>
    </body>
    </html>
    Podgląd strony po tym kroku (notatki.html)
  16. Etap 3: Styl CSS

    Kroki 16-26 · po tym etapie masz 28 z 37 kryteriów (pkt)

    Krok 16: Zmienne z kolorami i krój czcionki

    Kryteria:

    R.4.1 Ustawione domyślne formatowanie wszystkich selektorów (dla *): krój czcionki Verdana

    ok. 2 min
    Polecenie z arkusza

    Domyślne formatowanie wszystkich selektorów: krój czcionki Verdana.

    Na początku styl.css zmienne CSS w :root - pięć kolorów z arkusza, nazwanych według roli (akcent, blok główny, parzyste, kursor, cień). MediumPurple występuje w trzech miejscach (bloki, przyciski, ramka listy), więc ustawiony raz w --color-accent zmienia się jedną linią.

    Potem selektor uniwersalny * z font-family: Verdana, sans-serif (R.4.1) - rodzina zastępcza sans-serif na wypadek braku Verdany.

    styl.css CSS nowy plik
    :root {
        --color-accent: MediumPurple;
        --color-main: Thistle;
        --color-even: #DDCADD;
        --color-hover: Lavender;
        --color-shadow: DimGray;
    }
    
    * {
        font-family: Verdana, sans-serif;
    }
    Podgląd strony po tym kroku (notatki.html)
  17. Krok 17: Blok boczny i siatka strony

    ok. 4 min
    Polecenie z arkusza

    Dla bloku bocznego: szerokość 20%, wysokość 610 px.

    Szerokość bloku bocznego wynika z siatki, więc ten krok od razu ją zakłada:

    • display: grid na <body> - bloki stają się elementami siatki,
    • grid-template-columns: 20% 80% - dwie kolumny: 20% na blok boczny, 80% na resztę. Klucz R.4.5 dopuszcza szerokości zapisane właśnie tak („width lub grid-template-columns lub flex”),
    • grid-template-areas - mapa strony narysowana napisami: każdy wiersz w cudzysłowie to wiersz siatki, każde słowo - komórka. Nazwa aside powtórzona w czterech wierszach znaczy, że blok boczny zajmuje całą lewą kolumnę, jak na ilustracji 3.

    aside dostaje grid-area: aside (wejdź do obszaru o tej nazwie) i wysokość 610 px. Pozostałe bloki trafią do swoich obszarów w następnych krokach - do tego czasu przeglądarka układa je w kolejnych wolnych komórkach.

    styl.css CSS zmienione: 15 linii
    :root {
        --color-accent: MediumPurple;
        --color-main: Thistle;
        --color-even: #DDCADD;
        --color-hover: Lavender;
        --color-shadow: DimGray;
    }
    
    * {
        font-family: Verdana, sans-serif;
    }
    
    body {
        display: grid;
        grid-template-columns: 20% 80%;
        grid-template-areas:
            "aside header"
            "aside nav"
            "aside main"
            "aside footer";
    }
    
    aside {
        grid-area: aside;
        height: 610px;
    }
    Podgląd strony po tym kroku (notatki.html)
  18. Krok 18: Obraz

    ok. 1 min
    Polecenie z arkusza

    Dla obrazu: szerokość 100%, wysokość 610 px.

    width: 100% - cała szerokość komórki bloku bocznego, wysokość 610 px jak blok. Obraz może się lekko zniekształcić przy innej proporcji okna; arkusz tego wymaga. W prawdziwym projekcie dodałoby się object-fit: cover (przycięcie zamiast ściśnięcia), ale klucz sprawdza wymiary, więc tu go nie ma.

    styl.css CSS zmienione: 5 linii
    :root {
        --color-accent: MediumPurple;
        --color-main: Thistle;
        --color-even: #DDCADD;
        --color-hover: Lavender;
        --color-shadow: DimGray;
    }
    
    * {
        font-family: Verdana, sans-serif;
    }
    
    body {
        display: grid;
        grid-template-columns: 20% 80%;
        grid-template-areas:
            "aside header"
            "aside nav"
            "aside main"
            "aside footer";
    }
    
    aside {
        grid-area: aside;
        height: 610px;
    }
    
    img {
        width: 100%;
        height: 610px;
    }
    Podgląd strony po tym kroku (notatki.html)
  19. Krok 19: Nagłówek, nawigacja i stopka: wspólne style

    Kryteria:

    R.4.6 Ustawione wyrównanie tekstu do środka dla bloku nagłówkowego, stopki i bloku nawigacyjnego

    ok. 3 min
    Polecenie z arkusza

    Dla bloku nagłówkowego, stopki i nawigacyjnego: kolor tła MediumPurple, biały kolor czcionki, szerokość 80%, wysokość 50 px, wyrównanie tekstu do środka, cień tekstu o przesunięciu 4 px w obu osiach i kolorze DimGray.

    Jedna reguła dla trzech bloków: tło ze zmiennej, biały tekst, 50 px wysokości, wyśrodkowanie i text-shadow: 4px 4px z kolorem cienia (tabela 2 arkusza; rozmycie pominięte, bo arkusz go nie podaje).

    Szerokości 80% nie ma w tej regule - każdy z bloków trafia do obszaru w kolumnie 80% siatki i wypełnia ją całą. Dopisanie tu width: 80% dałoby 80% z 80%, czyli 64% okna. Trzy krótkie reguły z grid-area przypisują bloki do obszarów mapy.

    Typowe błędy

    box-shadow to cień całego prostokąta, a arkusz mówi o cieniu tekstu - text-shadow.

    styl.css CSS zmienione: 22 linie
    :root {
        --color-accent: MediumPurple;
        --color-main: Thistle;
        --color-even: #DDCADD;
        --color-hover: Lavender;
        --color-shadow: DimGray;
    }
    
    * {
        font-family: Verdana, sans-serif;
    }
    
    body {
        display: grid;
        grid-template-columns: 20% 80%;
        grid-template-areas:
            "aside header"
            "aside nav"
            "aside main"
            "aside footer";
    }
    
    aside {
        grid-area: aside;
        height: 610px;
    }
    
    img {
        width: 100%;
        height: 610px;
    }
    
    header,
    nav,
    footer {
        background-color: var(--color-accent);
        color: white;
        height: 50px;
        text-align: center;
        text-shadow: 4px 4px var(--color-shadow);
    }
    
    header {
        grid-area: header;
    }
    
    nav {
        grid-area: nav;
    }
    
    footer {
        grid-area: footer;
    }
    Podgląd strony po tym kroku (notatki.html)
  20. Krok 20: Nawigacja: górny margines wewnętrzny

    ok. 1 min
    Polecenie z arkusza

    Dodatkowo dla bloku nawigacyjnego: margines wewnętrzny górny 10 px.

    padding-top: 10px w regule nav, która już istnieje - tylko górny, jak w arkuszu. Wiersz siatki z nawigacją ma przez to 60 px wysokości (50 + 10), co razem z 50 + 450 + 50 daje dokładnie 610 px - tyle, ile blok boczny.

    Typowe błędy

    padding: 10px ze wszystkich stron zmienia wymiary bloku - arkusz mówi „jedynie górny” (R.4.7).

    styl.css CSS zmienione: 1 linia
    :root {
        --color-accent: MediumPurple;
        --color-main: Thistle;
        --color-even: #DDCADD;
        --color-hover: Lavender;
        --color-shadow: DimGray;
    }
    
    * {
        font-family: Verdana, sans-serif;
    }
    
    body {
        display: grid;
        grid-template-columns: 20% 80%;
        grid-template-areas:
            "aside header"
            "aside nav"
            "aside main"
            "aside footer";
    }
    
    aside {
        grid-area: aside;
        height: 610px;
    }
    
    img {
        width: 100%;
        height: 610px;
    }
    
    header,
    nav,
    footer {
        background-color: var(--color-accent);
        color: white;
        height: 50px;
        text-align: center;
        text-shadow: 4px 4px var(--color-shadow);
    }
    
    header {
        grid-area: header;
    }
    
    nav {
        grid-area: nav;
        padding-top: 10px;
    }
    
    footer {
        grid-area: footer;
    }
    Podgląd strony po tym kroku (notatki.html)
  21. Krok 21: Blok główny

    Kryteria: ,

    R.3.1 Układ bloków po uruchomieniu strony w przeglądarce jest zgodny z ilustracją 3 w arkuszu egzaminacyjnym (prawidłowo zastosowane właściwości CSS decydujące o układzie bloków na stronie, np. float i clear albo display flex albo grid)

    R.4.8 Ustawione dla bloku nagłówkowego, stopki i bloku nawigacyjnego cień text-shadow: 4px 4px DimGray oraz dla bloku głównego paski przewijania auto

    ok. 2 min
    Polecenie z arkusza

    Dla bloku głównego: kolor tła Thistle, szerokość 80%, wysokość 450 px, paski przewijania dodawane tylko, gdy zawartość nie mieści się w bloku.

    grid-area: main, tło Thistle ze zmiennej, wysokość 450 px i overflow: auto - pasek pojawi się dopiero, gdy lista przerośnie blok. Szerokość 80% daje kolumna siatki. Wszystkie bloki stoją teraz tak jak na ilustracji 3: blok boczny po lewej, cztery bloki jeden pod drugim po prawej (R.3.1). Cień tekstu trzech bloków i paski auto bloku głównego dają razem R.4.8.

    styl.css CSS zmienione: 7 linii
    :root {
        --color-accent: MediumPurple;
        --color-main: Thistle;
        --color-even: #DDCADD;
        --color-hover: Lavender;
        --color-shadow: DimGray;
    }
    
    * {
        font-family: Verdana, sans-serif;
    }
    
    body {
        display: grid;
        grid-template-columns: 20% 80%;
        grid-template-areas:
            "aside header"
            "aside nav"
            "aside main"
            "aside footer";
    }
    
    aside {
        grid-area: aside;
        height: 610px;
    }
    
    img {
        width: 100%;
        height: 610px;
    }
    
    header,
    nav,
    footer {
        background-color: var(--color-accent);
        color: white;
        height: 50px;
        text-align: center;
        text-shadow: 4px 4px var(--color-shadow);
    }
    
    header {
        grid-area: header;
    }
    
    nav {
        grid-area: nav;
        padding-top: 10px;
    }
    
    footer {
        grid-area: footer;
    }
    
    main {
        grid-area: main;
        background-color: var(--color-main);
        height: 450px;
        overflow: auto;
    }
    Podgląd strony po tym kroku (notatki.html)
  22. Krok 22: Przyciski w liście

    Kryteria: , ,

    R.4.2 Ustawiony kolor tła MediumPurple dla bloków: nagłówkowego, stopki, nawigacyjnego i jedynie dla przycisków w liście (id jest błędem) oraz Thistle dla bloku głównego

    R.4.3 Ustawiony biały kolor czcionki dla bloku nagłówkowego, stopki, bloku nawigacyjnego i przycisków

    R.4.4 Ustawiona wysokość bloku bocznego i obrazu 610px, bloku nagłówkowego, stopki i bloku nawigacyjnego 50px, głównego 450px, dla przycisków 30px

    ok. 2 min
    Polecenie z arkusza

    Dla przycisków w liście: opływanie do prawej strony, kolor tła MediumPurple, biały kolor czcionki, wysokość 30 px, bez obramowania.

    Selektor potomka li button - tylko przyciski w elementach listy, przycisk „Dodaj” zostaje domyślny (klucz R.4.2: „jedynie dla przycisków w liście”, id byłoby błędem).

    „Opływanie do prawej” to float: right - arkusz nazywa tu konkretną właściwość, więc zostaje, choć układ strony obywa się bez float. Do jednego przycisku przy tekście float nadaje się zresztą dobrze: tekst zostaje po lewej, przycisk dosuwa się do prawej krawędzi.

    Z tym krokiem komplet są tła MediumPurple i Thistle (R.4.2), biały tekst bloków i przycisków (R.4.3) oraz wysokości 610 / 50 / 450 / 30 px (R.4.4).

    styl.css CSS zmienione: 8 linii
    :root {
        --color-accent: MediumPurple;
        --color-main: Thistle;
        --color-even: #DDCADD;
        --color-hover: Lavender;
        --color-shadow: DimGray;
    }
    
    * {
        font-family: Verdana, sans-serif;
    }
    
    body {
        display: grid;
        grid-template-columns: 20% 80%;
        grid-template-areas:
            "aside header"
            "aside nav"
            "aside main"
            "aside footer";
    }
    
    aside {
        grid-area: aside;
        height: 610px;
    }
    
    img {
        width: 100%;
        height: 610px;
    }
    
    header,
    nav,
    footer {
        background-color: var(--color-accent);
        color: white;
        height: 50px;
        text-align: center;
        text-shadow: 4px 4px var(--color-shadow);
    }
    
    header {
        grid-area: header;
    }
    
    nav {
        grid-area: nav;
        padding-top: 10px;
    }
    
    footer {
        grid-area: footer;
    }
    
    main {
        grid-area: main;
        background-color: var(--color-main);
        height: 450px;
        overflow: auto;
    }
    
    li button {
        float: right;
        background-color: var(--color-accent);
        color: white;
        height: 30px;
        border: none;
    }
    Podgląd strony po tym kroku (notatki.html)
  23. Krok 23: Lista

    Kryteria: ,

    R.4.5 Ustawiona szerokość bloku bocznego 20%, obrazu 100%, pozostałych bloków 80% oraz dla listy punktowanej 70% (width lub grid-template-columns lub flex)

    R.4.9 Ustawione pozostałe cechy listy: rozmiar czcionki 25px, obramowanie 2px solid MediumPurple, brak znaków punktora, oraz dla przycisków: brak obramowania

    ok. 2 min
    Polecenie z arkusza

    Dla listy punktowanej: marginesy zewnętrzne górny i dolny 40 px, lewy i prawy automatycznie wyliczane przez przeglądarkę, szerokość 70%, rozmiar czcionki 25 px, obramowanie linią ciągłą o szerokości 2 px i kolorze MediumPurple, lista nie ma znaków punktora.

    margin: 40px auto - 40 px góra i dół, auto po bokach (przy ustalonej szerokości to wyśrodkowanie), szerokość 70% (ostatnia szerokość z R.4.5), czcionka 25 px, ramka i list-style-type: none - razem z przyciskami bez obramowania to R.4.9.

    Typowe błędy

    margin: 40px (jedna wartość) daje 40 px ze wszystkich stron i lista nie jest wyśrodkowana.

    styl.css CSS zmienione: 8 linii
    :root {
        --color-accent: MediumPurple;
        --color-main: Thistle;
        --color-even: #DDCADD;
        --color-hover: Lavender;
        --color-shadow: DimGray;
    }
    
    * {
        font-family: Verdana, sans-serif;
    }
    
    body {
        display: grid;
        grid-template-columns: 20% 80%;
        grid-template-areas:
            "aside header"
            "aside nav"
            "aside main"
            "aside footer";
    }
    
    aside {
        grid-area: aside;
        height: 610px;
    }
    
    img {
        width: 100%;
        height: 610px;
    }
    
    header,
    nav,
    footer {
        background-color: var(--color-accent);
        color: white;
        height: 50px;
        text-align: center;
        text-shadow: 4px 4px var(--color-shadow);
    }
    
    header {
        grid-area: header;
    }
    
    nav {
        grid-area: nav;
        padding-top: 10px;
    }
    
    footer {
        grid-area: footer;
    }
    
    main {
        grid-area: main;
        background-color: var(--color-main);
        height: 450px;
        overflow: auto;
    }
    
    li button {
        float: right;
        background-color: var(--color-accent);
        color: white;
        height: 30px;
        border: none;
    }
    
    ul {
        margin: 40px auto;
        width: 70%;
        font-size: 25px;
        border: 2px solid var(--color-accent);
        list-style-type: none;
    }
    Podgląd strony po tym kroku (notatki.html)
  24. Krok 24: Element listy

    Kryteria:

    R.4.7 Ustawione marginesy wewnętrzne (padding) dla elementu listy - 10px, dla bloku nawigacyjnego - jedynie górny 10px oraz marginesy zewnętrzne (margin) dla listy - górny i dolny 40px, lewy i prawy - auto

    ok. 1 min
    Polecenie z arkusza

    Dla elementu listy: marginesy wewnętrzne 10 px.

    padding: 10px - tekst nie klei się do ramki. Teraz komplet marginesów z R.4.7: 10 px w elementach listy, górne 10 px nawigacji i 40 px / auto listy.

    styl.css CSS zmienione: 4 linie
    :root {
        --color-accent: MediumPurple;
        --color-main: Thistle;
        --color-even: #DDCADD;
        --color-hover: Lavender;
        --color-shadow: DimGray;
    }
    
    * {
        font-family: Verdana, sans-serif;
    }
    
    body {
        display: grid;
        grid-template-columns: 20% 80%;
        grid-template-areas:
            "aside header"
            "aside nav"
            "aside main"
            "aside footer";
    }
    
    aside {
        grid-area: aside;
        height: 610px;
    }
    
    img {
        width: 100%;
        height: 610px;
    }
    
    header,
    nav,
    footer {
        background-color: var(--color-accent);
        color: white;
        height: 50px;
        text-align: center;
        text-shadow: 4px 4px var(--color-shadow);
    }
    
    header {
        grid-area: header;
    }
    
    nav {
        grid-area: nav;
        padding-top: 10px;
    }
    
    footer {
        grid-area: footer;
    }
    
    main {
        grid-area: main;
        background-color: var(--color-main);
        height: 450px;
        overflow: auto;
    }
    
    li button {
        float: right;
        background-color: var(--color-accent);
        color: white;
        height: 30px;
        border: none;
    }
    
    ul {
        margin: 40px auto;
        width: 70%;
        font-size: 25px;
        border: 2px solid var(--color-accent);
        list-style-type: none;
    }
    
    li {
        padding: 10px;
    }
    Podgląd strony po tym kroku (notatki.html)
  25. Krok 25: Parzyste elementy listy

    ok. 1 min
    Polecenie z arkusza

    Dla parzystych elementów listy: kolor tła #DDCADD.

    :nth-child(even) wybiera 2., 4., 6. … element - także te, które dopisze skrypt.

    Typowe błędy

    :nth-child(odd) to nieparzyste.

    styl.css CSS zmienione: 4 linie
    :root {
        --color-accent: MediumPurple;
        --color-main: Thistle;
        --color-even: #DDCADD;
        --color-hover: Lavender;
        --color-shadow: DimGray;
    }
    
    * {
        font-family: Verdana, sans-serif;
    }
    
    body {
        display: grid;
        grid-template-columns: 20% 80%;
        grid-template-areas:
            "aside header"
            "aside nav"
            "aside main"
            "aside footer";
    }
    
    aside {
        grid-area: aside;
        height: 610px;
    }
    
    img {
        width: 100%;
        height: 610px;
    }
    
    header,
    nav,
    footer {
        background-color: var(--color-accent);
        color: white;
        height: 50px;
        text-align: center;
        text-shadow: 4px 4px var(--color-shadow);
    }
    
    header {
        grid-area: header;
    }
    
    nav {
        grid-area: nav;
        padding-top: 10px;
    }
    
    footer {
        grid-area: footer;
    }
    
    main {
        grid-area: main;
        background-color: var(--color-main);
        height: 450px;
        overflow: auto;
    }
    
    li button {
        float: right;
        background-color: var(--color-accent);
        color: white;
        height: 30px;
        border: none;
    }
    
    ul {
        margin: 40px auto;
        width: 70%;
        font-size: 25px;
        border: 2px solid var(--color-accent);
        list-style-type: none;
    }
    
    li {
        padding: 10px;
    }
    
    li:nth-child(even) {
        background-color: var(--color-even);
    }
    Podgląd strony po tym kroku (notatki.html)
  26. Krok 26: Element listy pod kursorem

    Kryteria:

    R.4.10 Gdy kursor znajduje się na elemencie listy, jego kolor tła zmienia się na Lavender oraz jedynie parzyste elementy listy mają kolor tła #DDCADD

    ok. 1 min
    Polecenie z arkusza

    Gdy kursor znajduje się na elemencie listy, jego kolor tła zmienia się na Lavender.

    li:hover po regule parzystych - ta sama specyficzność, więc wygrywa późniejsza i pod kursorem zmieniają kolor także parzyste elementy (R.4.10 ocenia obie reguły razem).

    styl.css CSS zmienione: 4 linie
    :root {
        --color-accent: MediumPurple;
        --color-main: Thistle;
        --color-even: #DDCADD;
        --color-hover: Lavender;
        --color-shadow: DimGray;
    }
    
    * {
        font-family: Verdana, sans-serif;
    }
    
    body {
        display: grid;
        grid-template-columns: 20% 80%;
        grid-template-areas:
            "aside header"
            "aside nav"
            "aside main"
            "aside footer";
    }
    
    aside {
        grid-area: aside;
        height: 610px;
    }
    
    img {
        width: 100%;
        height: 610px;
    }
    
    header,
    nav,
    footer {
        background-color: var(--color-accent);
        color: white;
        height: 50px;
        text-align: center;
        text-shadow: 4px 4px var(--color-shadow);
    }
    
    header {
        grid-area: header;
    }
    
    nav {
        grid-area: nav;
        padding-top: 10px;
    }
    
    footer {
        grid-area: footer;
    }
    
    main {
        grid-area: main;
        background-color: var(--color-main);
        height: 450px;
        overflow: auto;
    }
    
    li button {
        float: right;
        background-color: var(--color-accent);
        color: white;
        height: 30px;
        border: none;
    }
    
    ul {
        margin: 40px auto;
        width: 70%;
        font-size: 25px;
        border: 2px solid var(--color-accent);
        list-style-type: none;
    }
    
    li {
        padding: 10px;
    }
    
    li:nth-child(even) {
        background-color: var(--color-even);
    }
    
    li:hover {
        background-color: var(--color-hover);
    }
    Podgląd strony po tym kroku (notatki.html)
  27. Etap 4: JavaScript

    Kroki 27-32 · po tym etapie masz 37 z 37 kryteriów (pkt)

    Krok 27: Plik script.js z defer i klasa .done

    Kryteria:

    R.5.1 Skrypt zapisany w języku JavaScript i zawiera on przynajmniej jedną instrukcję zapisaną zgodnie ze składnią

    ok. 3 min
    Polecenie z arkusza

    Napisany w języku JavaScript i działający na liście punktowanej.

    Trzy rzeczy na start:

    • <script src="script.js" defer> w <head> - atrybut defer każe pobrać skrypt od razu, a uruchomić po wczytaniu całego HTML. Lista już istnieje, gdy skrypt jej szuka, a skrypt nie wstrzymuje rysowania strony,
    • w styl.css klasa .done z text-decoration: line-through - skrypt nie będzie wpisywał stylu, tylko dokładał klasę. Wygląd zostaje w pliku stylów (arkusz: „styl CSS zdefiniowany w całości w zewnętrznym pliku”), a skrypt mówi tylko, że zadanie jest wykonane,
    • pierwsza linia skryptu: const taskList = document.querySelector('#task-list') - lista wyszukana raz i zapamiętana w stałej. querySelector (tabela 1 arkusza) przyjmuje dowolny selektor CSS.
    notatki.html HTML zmienione: 1 linia
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <title>Planer zadań</title>
        <link rel="stylesheet" href="styl.css">
        <script src="script.js" defer></script>
    </head>
    <body>
        <aside>
            <img src="obraz.jpg" alt="notatki">
        </aside>
        <header>
            <h2>Moje zadania</h2>
        </header>
        <nav>
            <form id="task-form">
                <input type="text" id="task-input" placeholder="Dodaj zadanie..." aria-label="Nowe zadanie" required>
                <button type="submit">Dodaj</button>
            </form>
        </nav>
        <main>
            <ul id="task-list">
                <li>Wyprowadzić psa <button type="button">Wykonane</button></li>
                <li>Gimnastyka <button type="button">Wykonane</button></li>
                <li>Zakupy <button type="button">Wykonane</button></li>
                <li>Spacer z kumplem <button type="button">Wykonane</button></li>
                <li>Odrabianie lekcji z młodszą siostrą <button type="button">Wykonane</button></li>
                <li>Projekt na geografię <button type="button">Wykonane</button></li>
            </ul>
        </main>
        <footer>
            <h3>Notatki: Bartosz Bryniarski</h3>
        </footer>
    </body>
    </html>
    script.js JavaScript nowy plik
    const taskList = document.querySelector('#task-list');
    styl.css CSS zmienione: 4 linie
    :root {
        --color-accent: MediumPurple;
        --color-main: Thistle;
        --color-even: #DDCADD;
        --color-hover: Lavender;
        --color-shadow: DimGray;
    }
    
    * {
        font-family: Verdana, sans-serif;
    }
    
    body {
        display: grid;
        grid-template-columns: 20% 80%;
        grid-template-areas:
            "aside header"
            "aside nav"
            "aside main"
            "aside footer";
    }
    
    aside {
        grid-area: aside;
        height: 610px;
    }
    
    img {
        width: 100%;
        height: 610px;
    }
    
    header,
    nav,
    footer {
        background-color: var(--color-accent);
        color: white;
        height: 50px;
        text-align: center;
        text-shadow: 4px 4px var(--color-shadow);
    }
    
    header {
        grid-area: header;
    }
    
    nav {
        grid-area: nav;
        padding-top: 10px;
    }
    
    footer {
        grid-area: footer;
    }
    
    main {
        grid-area: main;
        background-color: var(--color-main);
        height: 450px;
        overflow: auto;
    }
    
    li button {
        float: right;
        background-color: var(--color-accent);
        color: white;
        height: 30px;
        border: none;
    }
    
    ul {
        margin: 40px auto;
        width: 70%;
        font-size: 25px;
        border: 2px solid var(--color-accent);
        list-style-type: none;
    }
    
    li {
        padding: 10px;
    }
    
    li:nth-child(even) {
        background-color: var(--color-even);
    }
    
    li:hover {
        background-color: var(--color-hover);
    }
    
    .done {
        text-decoration: line-through;
    }
    Podgląd strony po tym kroku (notatki.html)
  28. Krok 28: Przekreślenie zadania: jeden nasłuchiwacz na całej liście

    Kryteria: ,

    R.3.5 Po wciśnięciu przynajmniej jednego przycisku Wykonane jest wywoływana funkcja skryptu

    R.5.5 Po wybraniu przycisku w liście tylko skojarzony z nim tekst jest przekreślany niezależnie od kolejności wciskanych przycisków. Sprawdzić jedynie dla wpisów ze stanu początkowego aplikacji

    ok. 4 min
    Polecenie z arkusza

    Działanie funkcji wywoływanej po wciśnięciu dowolnego z przycisków „Wykonane”: przekreśla treść elementu listy związanego z tym przyciskiem.

    Zamiast sześciu nasłuchiwaczy - jeden, na liście (delegacja zdarzeń). Kliknięcie w przycisk „wędruje” w górę drzewa dokumentu do <ul>, więc lista dowiaduje się o każdym kliknięciu w swoim wnętrzu:

    • event.target to element, w który kliknięto; .closest('button') szuka najbliższego przycisku (sam element albo jego przodek). Kliknięcie w tekst zadania, a nie w przycisk, daje null i funkcja kończy się na return,
    • button.closest('li') - element listy z tym przyciskiem, czyli „związany z tym przyciskiem”; classList.add('done') dokłada mu klasę przekreślenia.

    Działa niezależnie od kolejności klikania (R.5.5) i - co najważniejsze - także dla zadań, które dopiero zostaną dodane, bo nasłuchuje lista, a nie przyciski. Kliknij „Wykonane” w podglądzie. Sam przycisk nie jest przekreślony: jako element pływający (float) nie dziedziczy dekoracji tekstu.

    Typowe błędy

    event.target.parentElement bez sprawdzenia, czy kliknięto przycisk, przekreśli całą listę <ul>... albo <main> - zależnie od tego, gdzie trafi kursor. closest z warunkiem chroni przed tym.

    script.js JavaScript zmienione: 10 linii
    const taskList = document.querySelector('#task-list');
    
    function markDone(event) {
        const button = event.target.closest('button');
        if (!button) {
            return;
        }
        button.closest('li').classList.add('done');
    }
    
    taskList.addEventListener('click', markDone);
    Podgląd strony po tym kroku (notatki.html)
  29. Krok 29: Funkcja dla przycisku „Dodaj”: obsługa wysłania formularza

    Kryteria:

    R.3.4 Po wciśnięciu przycisku Dodaj jest wywoływana funkcja skryptu

    ok. 2 min
    Polecenie z arkusza

    Przycisk „Dodaj”, którego wciśnięcie powoduje wywołanie funkcji w skrypcie.

    Formularz i pole też trafiają do stałych. Nasłuchiwacz nasłuchuje zdarzenia submit formularza, a nie click przycisku - submit przychodzi i po kliknięciu „Dodaj”, i po Enterze w polu. Pierwsza instrukcja event.preventDefault() wyłącza domyślne zachowanie formularza (przeładowanie strony), bo zadanie ma się pojawić bez przeładowania.

    Klucz R.3.4 sprawdza, że wciśnięcie „Dodaj” wywołuje funkcję skryptu - wciśnięcie przycisku type="submit" wysyła formularz, a to wywołuje addTask.

    Typowe błędy

    Bez preventDefault() formularz bez action przeładuje stronę: nowe zadanie mignie i zniknie razem z przekreśleniami.

    script.js JavaScript zmienione: 8 linii
    const taskList = document.querySelector('#task-list');
    const taskForm = document.querySelector('#task-form');
    const taskInput = document.querySelector('#task-input');
    
    function markDone(event) {
        const button = event.target.closest('button');
        if (!button) {
            return;
        }
        button.closest('li').classList.add('done');
    }
    
    taskList.addEventListener('click', markDone);
    
    function addTask(event) {
        event.preventDefault();
    }
    
    taskForm.addEventListener('submit', addTask);
    Podgląd strony po tym kroku (notatki.html)
  30. Krok 30: Dodawanie: nowy element z treścią z pola

    Kryteria:

    R.5.7 W skryptach zastosowana przynajmniej jedna zmienna oraz nazwy wszystkich zmiennych są w języku polskim lub angielskim i odzwierciedlają cel zastosowania

    ok. 3 min
    Polecenie z arkusza

    Działanie funkcji wywoływanej po wciśnięciu przycisku „Dodaj”: tworzy nowy element listy na jej końcu. Element składa się z treści pobranej z pola edycyjnego.

    • taskInput.value.trim() - tekst z pola bez spacji na brzegach; pusty tekst kończy funkcję (required w HTML już to blokuje, ale skrypt nie powinien zależeć od atrybutu, który ktoś może usunąć),
    • document.createElement('li') - nowy element (tabela 3 arkusza),
    • item.append(\${text} `)-appenddokłada do elementu **tekst** (albo inne elementy). Tekst wstawiony w ten sposób zawsze jest tekstem: wpisane` pokaże się dosłownie, a nie jako pogrubienie. Szablon tekstowy w odwrotnych apostrofach skleja treść ze spacją przed przyciskiem.
    Typowe błędy

    item.innerHTML = text potraktowałby wpis użytkownika jak kod HTML - dla tekstu od użytkownika zawsze textContent albo append.

    script.js JavaScript zmienione: 6 linii
    const taskList = document.querySelector('#task-list');
    const taskForm = document.querySelector('#task-form');
    const taskInput = document.querySelector('#task-input');
    
    function markDone(event) {
        const button = event.target.closest('button');
        if (!button) {
            return;
        }
        button.closest('li').classList.add('done');
    }
    
    taskList.addEventListener('click', markDone);
    
    function addTask(event) {
        event.preventDefault();
        const text = taskInput.value.trim();
        if (text === '') {
            return;
        }
        const item = document.createElement('li');
        item.append(`${text} `);
    }
    
    taskForm.addEventListener('submit', addTask);
    Podgląd strony po tym kroku (notatki.html)
  31. Krok 31: Dodawanie: przycisk „Wykonane” w nowym elemencie

    Kryteria: ,

    R.5.3 Funkcja dodająca wyświetla w ostatnim elemencie listy przycisk

    R.5.4 Utworzony przycisk o nazwie Wykonane jest formatowany tak samo jak pozostałe przyciski w liście

    ok. 3 min
    Polecenie z arkusza

    Przycisku o treści „Wykonane”, który jest formatowany tak jak reszta przycisków w liście i jego kliknięcie powoduje wywołanie odpowiedniej funkcji.

    Nowy <button> z textContent = 'Wykonane' (textContent ustawia sam tekst i - inaczej niż innerText - nie każe przeglądarce przeliczać układu strony). item.append(tekst, button) wkłada tekst i przycisk do elementu w jednej instrukcji.

    Przycisk nie dostaje żadnego nasłuchiwacza: kliknięcie dotrze do listy, a tam czeka markDone z delegacji (R.5.6). Formatowanie „jak reszta przycisków” daje reguła li button (R.5.4).

    script.js JavaScript zmienione: 4 linie
    const taskList = document.querySelector('#task-list');
    const taskForm = document.querySelector('#task-form');
    const taskInput = document.querySelector('#task-input');
    
    function markDone(event) {
        const button = event.target.closest('button');
        if (!button) {
            return;
        }
        button.closest('li').classList.add('done');
    }
    
    taskList.addEventListener('click', markDone);
    
    function addTask(event) {
        event.preventDefault();
        const text = taskInput.value.trim();
        if (text === '') {
            return;
        }
        const item = document.createElement('li');
        const button = document.createElement('button');
        button.type = 'button';
        button.textContent = 'Wykonane';
        item.append(`${text} `, button);
    }
    
    taskForm.addEventListener('submit', addTask);
    Podgląd strony po tym kroku (notatki.html)
  32. Krok 32: Dodawanie: element na końcu listy

    Kryteria: ,

    R.5.2 Po wybraniu przycisku dodającego jest wyświetlany tekst, który został pobrany z pola edycyjnego, jako ostatni element listy

    R.5.6 Po dodaniu nowego elementu listy i wciśnięciu skojarzonego z nim przycisku, nowy element listy jest przekreślany

    ok. 2 min
    Polecenie z arkusza

    Tworzy nowy element listy na jej końcu.

    taskList.append(item) dokłada element jako ostatnie dziecko listy - na jej końcu (R.5.2). Potem taskForm.reset() czyści pole, a taskInput.focus() wraca do niego kursorem, żeby od razu wpisać kolejne zadanie.

    Podgląd działa jak na ilustracji 4: wpisz „Lekarz”, Enter albo „Dodaj”, potem „Wykonane” przy nowym zadaniu - przekreśli się, choć w chwili rejestrowania nasłuchiwacza tego przycisku nie było (R.5.6).

    script.js JavaScript zmienione: 3 linie
    const taskList = document.querySelector('#task-list');
    const taskForm = document.querySelector('#task-form');
    const taskInput = document.querySelector('#task-input');
    
    function markDone(event) {
        const button = event.target.closest('button');
        if (!button) {
            return;
        }
        button.closest('li').classList.add('done');
    }
    
    taskList.addEventListener('click', markDone);
    
    function addTask(event) {
        event.preventDefault();
        const text = taskInput.value.trim();
        if (text === '') {
            return;
        }
        const item = document.createElement('li');
        const button = document.createElement('button');
        button.type = 'button';
        button.textContent = 'Wykonane';
        item.append(`${text} `, button);
        taskList.append(item);
        taskForm.reset();
        taskInput.focus();
    }
    
    taskForm.addEventListener('submit', addTask);
    Podgląd strony po tym kroku (notatki.html)
  33. Etap 5: Porównanie z wersją podstawową

    Krok 33 · po tym etapie masz 37 z 37 kryteriów (pkt)

    Krok 33: Co jest inaczej i którą wersję wybrać

    ok. 3 min
    Polecenie z arkusza

    Dla uproszczenia należy założyć, że w stanie początkowym jest 6 elementów listy, elementy mogą być dodawane, ale nie są usuwane.

    wersja podstawowata wersja
    układfloat: left na pięciu blokachgrid-template-areas - mapa układu w kodzie
    szerokości 20% / 80%width w każdej regulegrid-template-columns
    przekreśleniestyle.textDecoration ze skryptuklasa .done w CSS
    przyciski „Wykonane”onclick="markDone(this)" i setAttributejeden nasłuchiwacz na liście
    „Dodaj”onclick przyciskuzdarzenie submit formularza (działa też Enter)

    Arkusz upraszcza: elementy nie są usuwane. Gdyby były, delegacja zdarzeń nie wymagałaby żadnych zmian, a w wersji z nasłuchiwaczem przy każdym przycisku trzeba by pamiętać o każdym nowym i usuniętym elemencie.

    Na egzaminie: wersja podstawowa trzyma się podpowiedzi z tabeli 1 (onclick, setAttribute) i jest szybsza do napisania bez dokumentacji. Ta wymaga pewności co do closest, classList i grid-template-areas - jeśli to pisze się z pamięci, kodu jest podobnie dużo, a strona lepiej się zachowuje (Enter, puste zadanie, zadania dodane później).

    Podgląd strony bez zmian - taki jak po kroku 32.

  34. Etap 6: Oddanie pracy

    Krok 34 · po tym etapie masz 37 z 37 kryteriów (pkt)

    Krok 34: Plik przeglądarka.txt i nagranie płyty

    ok. 5 min
    Polecenie z arkusza

    Po zakończeniu pracy utwórz plik tekstowy o nazwie przeglądarka.txt. Zapisz w nim nazwę przeglądarki internetowej, w której weryfikowana była poprawność działania witryny. Nagraj płytę z rezultatami pracy.

    Na płycie, w folderze z numerem zdającego: import.png, kw1.jpg - kw4.jpg, kwerendy.txt, notatki.html, obraz.jpg, przeglądarka.txt, styl.css i script.js. Po nagraniu sprawdź odczyt płyty.

    przeglądarka.txt Tekst nowy plik
    Google Chrome

    Podgląd strony bez zmian - taki jak po kroku 32.

Podgląd

To jedno z możliwych rozwiązań. Egzaminator ocenia spełnienie kryteriów z klucza odpowiedzi, a nie zgodność z tym kodem - inna, poprawna droga też daje punkty.

Autor rozwiązania: . Licencja CC BY-NC 4.0 - kopiowanie i przeróbki do celów niekomercyjnych, z podaniem autora i źródła (link do tej strony) (zasady wykorzystania). Treść arkusza i klucza oceniania pochodzi z CKE.