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

Rozwiązanie krok po kroku: Planer zadań z notatkami

Budujemy planer zadań: baza notatki z czterema kwerendami (MIN, LIKE, JOIN, COUNT z GROUP BY), grafika przeskalowana do 610 px wysokości, strona notatki.html z blokiem bocznym i czterema blokami obok niego, style w styl.css (cień tekstu, parzyste elementy listy, :hover) oraz skrypt JavaScript: przekreślanie wykonanych zadań i dodawanie nowych elementów listy z przyciskiem.

Kroki idą zdanie po zdaniu za treścią arkusza - każdy zaczyna się cytatem wymagania, a kod pokazuje dokładnie te linie, które je spełniają. JavaScript piszemy najpierw najprościej, tak jak podpowiada tabela 1 arkusza (atrybut onclick, setAttribute), a na końcu przerabiamy na wersję z prawdziwych projektów: nasłuchiwacze zdarzeń, walidacja pola i porządek po dodaniu zadania. W tym arkuszu nie ma PHP.

Obraz, baza i plik zadania.txt pochodzą z prawdziwego archiwum pliki2 (załącznik do pobrania na stronie arkusza, hasło w treści zadania). Zrzuty wyników zapytań są odtworzone z tej bazy - na egzaminie robisz je sam, z całego ekranu.

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 - można stracić 9 kryteriów i nadal zdać. Przy każdym kroku jest napisane, które kryteria spełnia; egzaminator ocenia spełnienie kryteriów z klucza, nie zgodność z tym kodem.

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.
  1. Etap 1: Baza danych

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

    Krok 1: Baza danych notatki

    Kryteria: R.1.1 ok. 2 min

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

    W przeglądarce otwieramy http://localhost/phpmyadmin, klikamy Nowa w lewym panelu, wpisujemy nazwę notatki i z listy metod porównywania wybieramy utf8_unicode_ci (arkusz podaje ją jako przykład; utf8_polish_ci też jest poprawne). Bez zestawu z polskimi znakami nazwy notatek z pliku SQL („Wyprowadzić psa”, „Projekt na geografię”) mogłyby stracić ogonki.

    Typowe błędy

    Nazwa bazy musi być dokładnie notatki - tak nazywa się też jedna z tabel, więc łatwo o pomyłkę „baza notatki, tabela notatki”. To dwie różne rzeczy: baza to pojemnik, tabela leży w środku.

  2. Krok 2: Import tabel z pliku baza.sql

    Kryteria: R.1.1 ok. 2 min

    Arkusz: Do bazy zaimportuj tabele z pliku baza.sql z rozpakowanego archiwum.

    Wchodzimy do bazy notatki (musi być zaznaczona w lewym panelu), karta Import, Wybierz plik → baza.sql z rozpakowanego archiwum, Wykonaj.

    Po imporcie są dwie tabele z ilustracji 1: osoby (id, imie, nazwisko - 4 wiersze) i notatki (id, Osoby_id, nazwa, tresc, priorytet - 13 wierszy). Kolumna Osoby_id w notatkach to klucz obcy wskazujący na id osoby - relacja jeden do wielu (jedna osoba ma wiele notatek). Uwaga na wielką literę w Osoby_id - w zapytaniach pisz ją tak, jak jest w tabeli.

    Typowe błędy

    Import bez wcześniejszego wejścia do bazy notatki tworzy tabele w innej bazie (np. test) - zapytania na zrzutach będą wtedy „działać na złej bazie”.

  3. Krok 3: Zrzut ekranu import.png

    Kryteria: R.1.1 ok. 2 min

    Arkusz: Wykonaj zrzut ekranu po imporcie. Zrzut zapisz w formacie PNG pod nazwą import. Nie kadruj zrzutu. Powinien on obejmować cały ekran monitora, z widocznym paskiem zadań.

    Klawisz Print Screen (cały ekran, nie Alt+PrtScr, który łapie tylko aktywne okno), wklejamy do edytora grafiki i zapisujemy jako import.png w folderze z numerem zdającego. Na zrzucie ma być widać komunikat phpMyAdmin o pomyślnym imporcie albo obie tabele w bazie.

    Formaty w tym arkuszu: zrzut importu w PNG, zrzuty kwerend w JPEG.

    Typowe błędy

    Wykadrowany zrzut (samo okno przeglądarki, bez paska zadań) nie jest oceniany - klucz mówi to wprost.

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

    Kryteria: R.1.2, R.1.3 ok. 3 min

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

    Zakładka SQL w phpMyAdmin (przy zaznaczonej bazie notatki). Klauzula po klauzuli:

    • SELECT MIN(priorytet) - funkcja grupująca MIN zwraca najmniejszą wartość z kolumny w wybranych wierszach. Wynik to jedna liczba, nie lista notatek. „Jedynie” oznacza, że nie dokładamy innych kolumn.
    • FROM notatki - priorytety są w tabeli notatek.
    • WHERE Osoby_id = 3 - tylko notatki osoby o id 3 (Ewelina). Filtrujemy po kluczu obcym, nie trzeba łączyć z tabelą osoby, bo arkusz podaje id, nie imię.

    Wynik: dokładnie jedna wartość - 2 (Ewelina ma notatki o priorytetach 5, 5, 3, 2, 5). Zapytanie kopiujemy do pliku kwerendy.txt i robimy zrzut całego ekranu: kw1.jpg.

    Typowe błędy

    Klucz przyjmuje też SELECT priorytet FROM notatki WHERE Osoby_id = 3 ORDER BY priorytet LIMIT 1; - sortowanie rosnąco i pierwszy wiersz. MIN jest krótszy i mówi wprost, o co chodzi.

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

    Kryteria: R.1.4 ok. 3 min

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

    • SELECT nazwa, priorytet FROM notatki - dwie kolumny z tabeli notatek.
    • WHERE nazwa LIKE "%na%" - LIKE porównuje napis ze wzorcem, a % zastępuje dowolny ciąg znaków (także pusty). "%na%" znaczy: cokolwiek, potem „na”, potem cokolwiek - czyli „na” gdziekolwiek w nazwie. Zwykłe = wymagałoby nazwy równej dokładnie „na”.

    Wynik: dokładnie 4 wiersze - Gimnastyka 1, Projekt na geografię 2, Wyjazd na wakacje 5, Weterynarz Brutus 5. Cząstka może być w środku wyrazu, nie tylko jako osobne słowo. Zrzut: kw2.jpg.

    Typowe błędy

    LIKE "na%" (bez pierwszego %) znajdzie tylko nazwy zaczynające się od „na” - zero wierszy. LIKE "% na %" ze spacjami pominie Gimnastykę i Weterynarza. Wzorzec musi mieć % z obu stron.

    kwerendy.txt Tekst zmienione: 3 linie
    -- Zapytanie 1
    SELECT MIN(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 5
  6. Krok 6: Zapytanie 3: imię i notatka dla priorytetu 5

    Kryteria: R.1.5 ok. 4 min

    Arkusz: 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ą.

    Imię jest w tabeli osoby, nazwa notatki w notatki - „posłużyć się relacją” znaczy złączyć obie tabele po kluczu.

    • SELECT imie, nazwa - po jednej kolumnie z każdej tabeli.
    • FROM notatki JOIN osoby ON osoby.id = notatki.Osoby_id - do każdej notatki dokładamy wiersz osoby, której id równa się Osoby_id notatki. ON mówi, po których kolumnach pasują wiersze. Nazwy poprzedzamy tabelą (osoby.id), bo id jest w obu tabelach i bez tego baza nie wie, o którą chodzi.
    • WHERE priorytet = 5 - tylko notatki o najwyższym priorytecie.

    Wynik: dokładnie 5 wierszy - Krzysiek: Wyprowadzić psa, Spacer z kumplem; Ewelina: Wyjazd na wakacje, Weterynarz Brutus, Lekarz. Zrzut: kw3.jpg.

    Typowe błędy

    JOIN bez ON daje iloczyn kartezjański: każda notatka z każdą osobą, 13 · 4 = 52 wiersze i przypadkowe imiona. Klucz przyjmuje też starszy zapis FROM notatki, osoby WHERE osoby.id = Osoby_id AND priorytet = 5.

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

    Kryteria: R.1.6 ok. 4 min

    Arkusz: 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ą.

    • SELECT imie, COUNT(*) - imię i liczba wierszy w grupie. COUNT(*) liczy wiersze; klucz przyjmuje też COUNT(Osoby_id) albo dowolną inną kolumnę.
    • FROM notatki JOIN osoby ON osoby.id = notatki.Osoby_id - to samo złączenie co w zapytaniu 3, bo imię jest w drugiej tabeli.
    • GROUP BY imie - wiersze o tym samym imieniu zlewają się w jedną grupę, a COUNT liczy osobno dla każdej. Bez GROUP BY wyszłaby jedna liczba (13) dla wszystkich.

    Wynik: dokładnie 4 wiersze - Ewelina 5, Grześ 1, Krzysiek 4, Paula 3. Zrzut: kw4.jpg. Plik kwerendy.txt ma komplet czterech zapytań.

    Typowe błędy

    Kolumna niegrupująca w SELECT obok funkcji grupującej (np. SELECT imie, nazwa, COUNT(*)) to w nowszym MySQL błąd, a w MariaDB przypadkowa nazwa - w SELECT z GROUP BY mogą być tylko kolumny z GROUP BY i funkcje grupujące.

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

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

    Krok 8: Przeskalowanie grafiki obraz.jpg

    Kryteria: R.2.1 ok. 3 min

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

    Plik z archiwum ma 480 × 640 px (pionowy). W edytorze grafiki rastrowej (np. GIMP: Obraz → Skaluj obraz) wpisujemy wysokość 610 px przy zamkniętym łańcuchu proporcji. Szerokość przeliczy się sama na 458 px (480 · 610 / 640 = 457,5) - i dokładnie to sprawdza egzaminator (458 ±5 px). Zapisujemy jako obraz.jpg w folderze zdającego.

    Wysokość 610 px nie jest przypadkowa: tyle samo dostanie w CSS blok boczny, w którym obraz będzie siedział.

    Typowe błędy

    Rozłączony łańcuch proporcji = obraz ściśnięty do 610 px wysokości przy starej szerokości 480 px. Wysokość się zgadza, punktu nie ma.

    Podgląd strony po tym kroku Podgląd strony po kroku 8
  9. Krok 9: Plik notatki.html w HTML5

    Kryteria: R.2.2 ok. 2 min

    Arkusz: Składa się ze strony o nazwie notatki.html. Zapisana w języku HTML5.

    W edytorze zaznaczającym składnię tworzymy plik notatki.html w folderze zdającego, obok obraz.jpg. Tym razem rozszerzenie .html, bo arkusz nie wymaga PHP - strona działa nawet otwarta prosto z dysku, bez XAMPP-a.

    Pierwsza linia <!DOCTYPE html> mówi przeglądarce, że dokument jest w HTML5. Dalej szkielet każdej strony: <html>, a w nim <head> (informacje o stronie) i <body> (to, co widać w oknie).

    notatki.html HTML nowy plik
    <!DOCTYPE html>
    <html>
    <head>
    </head>
    <body>
    </body>
    </html>
    Podgląd strony po tym kroku (notatki.html)
  10. Krok 10: Język strony

    Kryteria: R.2.2 ok. 1 min

    Arkusz: Zadeklarowany polski język zawartości witryny.

    Atrybut lang="pl" w znaczniku <html>. Czytnik ekranu czyta wtedy tekst polską wymową, a przeglądarka dobiera słownik. Na stronie nic się nie zmienia - to informacja dla programów.

    notatki.html HTML zmienione: 1 linia
    <!DOCTYPE html>
    <html lang="pl">
    <head>
    </head>
    <body>
    </body>
    </html>
    Podgląd strony po tym kroku (notatki.html)
  11. Krok 11: Kodowanie polskich znaków

    Kryteria: R.2.2 ok. 1 min

    Arkusz: Jawnie zastosowany właściwy standard kodowania polskich znaków.

    <meta charset="UTF-8"> jako pierwsza rzecz w <head>. „Jawnie” znaczy: wpisane w kodzie, nie zdane na domysł przeglądarki. Bez tej linii „Wyprowadzić psa” mogłoby wyświetlić się jako krzaki. Plik trzeba też zapisać w UTF-8.

    notatki.html HTML zmienione: 1 linia
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
    </head>
    <body>
    </body>
    </html>
    Podgląd strony po tym kroku (notatki.html)
  12. Krok 12: Tytuł strony

    Kryteria: R.2.3 ok. 1 min

    Arkusz: Tytuł strony widoczny na karcie przeglądarki: „Planer zadań”.

    <title> w <head> - napis na karcie przeglądarki, nie nagłówek na stronie (ten dojdzie w <header>). Klucz R.2.3 sprawdza dokładnie tekst „Planer zadań”.

    notatki.html HTML zmienione: 1 linia
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <title>Planer zadań</title>
    </head>
    <body>
    </body>
    </html>
    Podgląd strony po tym kroku (notatki.html)
  13. Krok 13: Podpięcie arkusza stylów

    Kryteria: R.3.2 ok. 1 min

    Arkusz: Arkusz stylów w pliku o nazwie styl.css prawidłowo połączony z kodem strony.

    <link rel="stylesheet" href="styl.css"> w <head>. Plik styl.css napiszemy po HTML - połączenie może już być, zadziała, gdy plik pojawi się w tym samym folderze.

    Typowe błędy

    <link> to znacznik pusty - w HTML5 bez </link> i bez ukośnika />. Bez rel="stylesheet" przeglądarka nie potraktuje pliku jako stylów.

    notatki.html HTML zmienione: 1 linia
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <title>Planer zadań</title>
        <link rel="stylesheet" href="styl.css">
    </head>
    <body>
    </body>
    </html>
    Podgląd strony po tym kroku (notatki.html)
  14. Krok 14: Bloki semantyczne

    Kryteria: R.2.4 ok. 2 min

    Arkusz: 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.

    Z ilustracji 3: po lewej wysoki blok boczny, a obok niego, jeden pod drugim, cztery bloki. Semantyczne odpowiedniki z tabeli 4 arkusza: blok boczny to <aside>, nagłówkowy <header>, „blok dodający zadanie” to blok nawigacyjny <nav> (tak nazywa go dalsza część arkusza i klucz R.2.4), główny <main>, stopka <footer>.

    Kolejność w kodzie ma znaczenie dla układu: <aside> jako pierwszy, bo w CSS popłynie w lewo, a reszta ustawi się obok niego. Na razie bloki są puste i stoją jeden pod drugim - układ zrobi CSS.

    Typowe błędy

    Pięć zwykłych <div> wygląda na stronie identycznie, ale kryterium R.2.4 wymaga znaczników semantycznych - sprawdzane w kodzie źródłowym. Klucz dopuszcza inny znacznik tylko zamiast <nav>.

    notatki.html HTML zmienione: 10 linii
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <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)
  15. Krok 15: Blok boczny: obraz

    Kryteria: R.2.6 ok. 1 min

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

    <img src="obraz.jpg" alt="notatki"> - plik przeskalowany w poprzednim etapie, leżący obok notatki.html. Tekst alternatywny przepisujemy dokładnie z arkusza: czyta go czytnik ekranu i pokazuje przeglądarka, gdy obraz się nie wczyta. W podglądzie obraz ma na razie swój naturalny rozmiar 458 × 610 px.

    Typowe błędy

    src="C:\Users\...\obraz.jpg" działa tylko na komputerze autora - na płycie egzaminatora obraz się nie wczyta. Ścieżka ma być względna.

    notatki.html HTML zmienione: 1 linia
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <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)
  16. Krok 16: Blok nagłówkowy: nagłówek „Moje zadania”

    Kryteria: R.2.5 ok. 1 min

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

    Drugiego stopnia to <h2> - nie <h1>, choć to pierwszy nagłówek na stronie. Egzaminator sprawdza dokładnie ten znacznik, który wskazał arkusz (klucz R.2.5).

    notatki.html HTML zmienione: 1 linia
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <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)
  17. Krok 17: Blok nawigacyjny: pole z podpowiedzią

    Kryteria: R.2.7, R.3.3 ok. 2 min

    Arkusz: Zawartość bloku nawigacyjnego: pole edycyjne z podpowiedzią o treści „Dodaj zadanie...”.

    Pole edycyjne to <input type="text">. „Podpowiedź” to atrybut placeholder - szary tekst widoczny w pustym polu, który znika, gdy użytkownik zacznie pisać (klucz R.3.3 wymienia ten atrybut wprost). Pole dostaje id="task-input", bo skrypt będzie z niego czytał wpisany tekst.

    Nazwy identyfikatorów, klas i zmiennych piszemy w tym rozwiązaniu po angielsku (arkusz dopuszcza polski lub angielski, wymaga konsekwencji); wyjątkiem są nazwy narzucone przez arkusz (notatki.html, styl.css, obraz.jpg).

    Typowe błędy

    value="Dodaj zadanie..." zamiast placeholder wpisuje ten tekst do pola na stałe - użytkownik musi go kasować, a skrypt „doda” zadanie o treści „Dodaj zadanie...”. R.3.3 przepada.

    notatki.html HTML zmienione: 1 linia
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <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>
            <input type="text" id="task-input" placeholder="Dodaj zadanie...">
        </nav>
        <main>
        </main>
        <footer>
        </footer>
    </body>
    </html>
    Podgląd strony po tym kroku (notatki.html)
  18. Krok 18: Blok nawigacyjny: przycisk „Dodaj”

    Kryteria: R.2.7 ok. 1 min

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

    <button>Dodaj</button> obok pola. Nie ma tu formularza, więc przycisk niczego nie wysyła - jego jedyną rolą będzie uruchomienie funkcji JavaScript. Wywołanie funkcji dopiszemy, gdy będzie gotowy skrypt; teraz budujemy strukturę.

    notatki.html HTML zmienione: 1 linia
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <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>
            <input type="text" id="task-input" placeholder="Dodaj zadanie...">
            <button>Dodaj</button>
        </nav>
        <main>
        </main>
        <footer>
        </footer>
    </body>
    </html>
    Podgląd strony po tym kroku (notatki.html)
  19. Krok 19: Blok główny: lista i pierwszy element z przyciskiem

    Kryteria: R.2.8, R.2.9 ok. 2 min

    Arkusz: 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.

    Lista nieuporządkowana <ul> z id="task-list" - skrypt będzie do niej dokładał nowe elementy. Pierwszy element <li> zawiera tekst zadania skopiowany z zadania.txt i wewnątrz tego samego <li> przycisk <button>Wykonane</button> - klucz R.2.9 sprawdza, że przycisk jest w środku elementu listy, nie obok.

    Wywołanie skryptu przez przycisk dopiszemy w etapie JavaScript.

    Typowe błędy

    Przycisk poza <li> (np. w osobnym <div> obok listy) nie spełnia R.2.9, a skrypt nie będzie wiedział, które zadanie przekreślić.

    notatki.html HTML zmienione: 3 linie
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <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>
            <input type="text" id="task-input" placeholder="Dodaj zadanie...">
            <button>Dodaj</button>
        </nav>
        <main>
            <ul id="task-list">
                <li>Wyprowadzić psa <button>Wykonane</button></li>
            </ul>
        </main>
        <footer>
        </footer>
    </body>
    </html>
    Podgląd strony po tym kroku (notatki.html)
  20. Krok 20: Blok główny: pozostałe pięć zadań

    Kryteria: R.2.8, R.2.9 ok. 2 min

    Arkusz: Lista punktowana (nieuporządkowana) zawierająca w stanie początkowym 6 elementów. Wpis (kolejne wpisy można skopiować do kodu HTML z pliku zadania.txt).

    Pięć kolejnych <li> według tego samego wzoru, z tekstami skopiowanymi z zadania.txt (Ctrl+C / Ctrl+V - nie przepisuj, żeby nie robić literówek). Lista ma teraz 6 elementów, każdy z własnym przyciskiem - to stan początkowy z ilustracji 2.

    notatki.html HTML zmienione: 5 linii
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <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>
            <input type="text" id="task-input" placeholder="Dodaj zadanie...">
            <button>Dodaj</button>
        </nav>
        <main>
            <ul id="task-list">
                <li>Wyprowadzić psa <button>Wykonane</button></li>
                <li>Gimnastyka <button>Wykonane</button></li>
                <li>Zakupy <button>Wykonane</button></li>
                <li>Spacer z kumplem <button>Wykonane</button></li>
                <li>Odrabianie lekcji z młodszą siostrą <button>Wykonane</button></li>
                <li>Projekt na geografię <button>Wykonane</button></li>
            </ul>
        </main>
        <footer>
        </footer>
    </body>
    </html>
    Podgląd strony po tym kroku (notatki.html)
  21. Krok 21: Stopka: nagłówek z numerem zdającego

    Kryteria: R.2.5 ok. 1 min

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

    <h3> w <footer>. W tym rozwiązaniu po dwukropku stoi nazwisko autora - na egzaminie wpisujesz w to miejsce swój numer zdającego (PESEL), bo tego sprawdza klucz R.2.5. Struktura HTML strony jest kompletna - dalej style.

    notatki.html HTML zmienione: 1 linia
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <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>
            <input type="text" id="task-input" placeholder="Dodaj zadanie...">
            <button>Dodaj</button>
        </nav>
        <main>
            <ul id="task-list">
                <li>Wyprowadzić psa <button>Wykonane</button></li>
                <li>Gimnastyka <button>Wykonane</button></li>
                <li>Zakupy <button>Wykonane</button></li>
                <li>Spacer z kumplem <button>Wykonane</button></li>
                <li>Odrabianie lekcji z młodszą siostrą <button>Wykonane</button></li>
                <li>Projekt na geografię <button>Wykonane</button></li>
            </ul>
        </main>
        <footer>
            <h3>Notatki: Bartosz Bryniarski</h3>
        </footer>
    </body>
    </html>
    Podgląd strony po tym kroku (notatki.html)
  22. Etap 3: Styl CSS

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

    Krok 22: Plik styl.css: krój czcionki

    Kryteria: R.4.1 ok. 1 min

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

    Tworzymy plik styl.css obok notatki.html. „Wszystkie selektory” to selektor uniwersalny * - reguła obejmuje każdy element strony, także przyciski i pole. font-family: Verdana ustawia krój. W podglądzie zmienia się czcionka - znak, że <link> z HTML zadziałał.

    styl.css CSS nowy plik
    * {
        font-family: Verdana;
    }
    Podgląd strony po tym kroku (notatki.html)
  23. Krok 23: Blok boczny: szerokość i wysokość

    Kryteria: R.4.4, R.4.5 ok. 1 min

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

    Selektor aside. Szerokość w procentach liczy się względem rodzica (<body>), wysokość jest stała - równa wysokości przeskalowanego obrazu. Obraz na razie wystaje poza wąski blok, bo ma jeszcze swój naturalny rozmiar.

    styl.css CSS zmienione: 4 linie
    * {
        font-family: Verdana;
    }
    aside {
        width: 20%;
        height: 610px;
    }
    Podgląd strony po tym kroku (notatki.html)
  24. Krok 24: Obraz: szerokość 100% i wysokość

    Kryteria: R.4.4, R.4.5 ok. 1 min

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

    Selektor znacznika img. width: 100% to cała szerokość rodzica, czyli bloku bocznego - obraz dopasuje się do 20% okna. Wysokość stała 610 px, tak jak blok. Przy innej proporcji okna obraz może się lekko zniekształcić - arkusz tego wymaga, a klucz to sprawdza (R.4.4, R.4.5).

    styl.css CSS zmienione: 4 linie
    * {
        font-family: Verdana;
    }
    aside {
        width: 20%;
        height: 610px;
    }
    img {
        width: 100%;
        height: 610px;
    }
    Podgląd strony po tym kroku (notatki.html)
  25. Krok 25: Nagłówek, stopka i nawigacja: wspólne style

    Kryteria: R.4.2, R.4.3, R.4.4, R.4.5, R.4.6, R.4.8 ok. 3 min

    Arkusz: 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.

    Trzy bloki mają te same cechy, więc jedna reguła z selektorami po przecinku: header, footer, nav. Kolory nazwane przepisujemy z arkusza. Cień tekstu to text-shadow z tabeli 2 arkusza: pierwsza wartość to przesunięcie w poziomie, druga w pionie, potem kolor - 4px 4px DimGray. Rozmycia arkusz nie wymaga, więc je pomijamy.

    Szerokość 80% dopełnia 20% bloku bocznego - obok siebie ustawimy je za dwa kroki.

    Typowe błędy

    box-shadow to cień elementu (całego prostokąta), text-shadow - cień liter. Arkusz mówi o cieniu tekstu; tabela 2 pokazuje oba, żeby sprawdzić, czy czytasz uważnie.

    styl.css CSS zmienione: 8 linii
    * {
        font-family: Verdana;
    }
    aside {
        width: 20%;
        height: 610px;
    }
    img {
        width: 100%;
        height: 610px;
    }
    header, footer, nav {
        background-color: MediumPurple;
        color: white;
        width: 80%;
        height: 50px;
        text-align: center;
        text-shadow: 4px 4px DimGray;
    }
    Podgląd strony po tym kroku (notatki.html)
  26. Krok 26: Nawigacja: górny margines wewnętrzny

    Kryteria: R.4.7 ok. 1 min

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

    Osobna reguła nav z padding-top: 10px - margines wewnętrzny tylko z góry, żeby pole i przycisk nie przylegały do krawędzi bloku. Druga reguła dla tego samego selektora dokłada właściwość, nie kasuje wspólnych stylów z poprzedniego kroku.

    Typowe błędy

    padding: 10px (ze wszystkich stron) zmienia też szerokość i wysokość bloku - arkusz mówi „jedynie górny” i klucz R.4.7 to sprawdza.

    styl.css CSS zmienione: 3 linie
    * {
        font-family: Verdana;
    }
    aside {
        width: 20%;
        height: 610px;
    }
    img {
        width: 100%;
        height: 610px;
    }
    header, footer, nav {
        background-color: MediumPurple;
        color: white;
        width: 80%;
        height: 50px;
        text-align: center;
        text-shadow: 4px 4px DimGray;
    }
    nav {
        padding-top: 10px;
    }
    Podgląd strony po tym kroku (notatki.html)
  27. Krok 27: Blok główny: tło, wymiary, paski przewijania

    Kryteria: R.4.2, R.4.4, R.4.5, R.4.8 ok. 2 min

    Arkusz: 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.

    Selektor main. „Paski tylko, gdy zawartość się nie mieści” to overflow: auto (klucz R.4.8). scroll pokazywałby je zawsze, hidden obcinałby listę bez możliwości przewinięcia. Po dodaniu wielu zadań lista przekroczy 450 px i wtedy pojawi się pasek.

    styl.css CSS zmienione: 6 linii
    * {
        font-family: Verdana;
    }
    aside {
        width: 20%;
        height: 610px;
    }
    img {
        width: 100%;
        height: 610px;
    }
    header, footer, nav {
        background-color: MediumPurple;
        color: white;
        width: 80%;
        height: 50px;
        text-align: center;
        text-shadow: 4px 4px DimGray;
    }
    nav {
        padding-top: 10px;
    }
    main {
        background-color: Thistle;
        width: 80%;
        height: 450px;
        overflow: auto;
    }
    Podgląd strony po tym kroku (notatki.html)
  28. Krok 28: Układ bloków: blok boczny i cztery bloki obok niego

    Kryteria: R.3.1 ok. 3 min

    Arkusz: 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.

    Wszystkie bloki dostają float: left - pływają w lewo i układają się jak słowa w wierszu: aside (20%) zajmuje lewą kolumnę, header (80%) mieści się obok niego, nav już się nie mieści w tym samym wierszu, więc idzie niżej - ale wciąż na prawo od wysokiego aside, bo ten sięga 610 px w dół. Tak samo main i footer. Suma wysokości prawej kolumny (50 + 60 + 450 + 50) mieści się w 610 px bloku bocznego, więc stopka stoi obok niego, jak na ilustracji 3.

    Dopisujemy float: left do trzech istniejących reguł (aside, header, footer, nav, main). Klucz przyjmuje też display: flex albo grid.

    Typowe błędy

    float: left tylko na aside daje inny efekt: pozostałe bloki (80%) zaczynają się od lewej krawędzi pod obrazem, a nie obok niego - ich tło chowa się za obrazem. Pływać muszą wszystkie bloki.

    styl.css CSS zmienione: 3 linie
    * {
        font-family: Verdana;
    }
    aside {
        width: 20%;
        height: 610px;
        float: left;
    }
    img {
        width: 100%;
        height: 610px;
    }
    header, footer, nav {
        float: left;
        background-color: MediumPurple;
        color: white;
        width: 80%;
        height: 50px;
        text-align: center;
        text-shadow: 4px 4px DimGray;
    }
    nav {
        padding-top: 10px;
    }
    main {
        float: left;
        background-color: Thistle;
        width: 80%;
        height: 450px;
        overflow: auto;
    }
    Podgląd strony po tym kroku (notatki.html)
  29. Krok 29: Przyciski w liście

    Kryteria: R.4.2, R.4.3, R.4.4, R.4.9 ok. 2 min

    Arkusz: 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 wewnątrz elementów listy; przycisk „Dodaj” w nawigacji zostaje domyślny. Klucz R.4.2 mówi wprost: id byłoby tu błędem, bo przycisków jest sześć (i przybędą nowe). „Opływanie do prawej” to float: right - przycisk odsuwa się na prawą krawędź, a tekst zadania zostaje po lewej, jak na ilustracji 2. border: none zdejmuje domyślną ramkę przeglądarki.

    Typowe błędy

    Selektor button bez li ostyluje też przycisk „Dodaj” - klucz R.4.2 wymaga tła MediumPurple jedynie dla przycisków w liście.

    styl.css CSS zmienione: 7 linii
    * {
        font-family: Verdana;
    }
    aside {
        width: 20%;
        height: 610px;
        float: left;
    }
    img {
        width: 100%;
        height: 610px;
    }
    header, footer, nav {
        float: left;
        background-color: MediumPurple;
        color: white;
        width: 80%;
        height: 50px;
        text-align: center;
        text-shadow: 4px 4px DimGray;
    }
    nav {
        padding-top: 10px;
    }
    main {
        float: left;
        background-color: Thistle;
        width: 80%;
        height: 450px;
        overflow: auto;
    }
    li button {
        float: right;
        background-color: MediumPurple;
        color: white;
        height: 30px;
        border: none;
    }
    Podgląd strony po tym kroku (notatki.html)
  30. Krok 30: Lista: marginesy, szerokość, czcionka, ramka, bez punktorów

    Kryteria: R.4.5, R.4.7, R.4.9 ok. 3 min

    Arkusz: 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.

    Selektor ul. Skrót margin: 40px auto to dwie wartości: pierwsza dla góry i dołu, druga dla lewej i prawej - auto przy ustalonej szerokości wyśrodkowuje listę w bloku głównym. border: 2px solid MediumPurple łączy grubość, styl (ciągła) i kolor. list-style-type: none zdejmuje kropki - lista dalej jest „punktowana” w sensie HTML (<ul>), tylko bez widocznych punktorów, jak na ilustracji 2.

    Typowe błędy

    margin: 40px (jedna wartość) ustawi 40 px z każdej strony i lista nie będzie wyśrodkowana; margin: auto 40px odwraca kolejność (auto dla góry i dołu nic nie robi).

    styl.css CSS zmienione: 7 linii
    * {
        font-family: Verdana;
    }
    aside {
        width: 20%;
        height: 610px;
        float: left;
    }
    img {
        width: 100%;
        height: 610px;
    }
    header, footer, nav {
        float: left;
        background-color: MediumPurple;
        color: white;
        width: 80%;
        height: 50px;
        text-align: center;
        text-shadow: 4px 4px DimGray;
    }
    nav {
        padding-top: 10px;
    }
    main {
        float: left;
        background-color: Thistle;
        width: 80%;
        height: 450px;
        overflow: auto;
    }
    li button {
        float: right;
        background-color: MediumPurple;
        color: white;
        height: 30px;
        border: none;
    }
    ul {
        margin: 40px auto;
        width: 70%;
        font-size: 25px;
        border: 2px solid MediumPurple;
        list-style-type: none;
    }
    Podgląd strony po tym kroku (notatki.html)
  31. Krok 31: Element listy: margines wewnętrzny

    Kryteria: R.4.7 ok. 1 min

    Arkusz: Dla elementu listy: marginesy wewnętrzne 10 px.

    Selektor li, padding: 10px - odstęp wewnątrz każdego elementu, dzięki czemu tekst nie klei się do ramki, a przycisk ma miejsce.

    styl.css CSS zmienione: 3 linie
    * {
        font-family: Verdana;
    }
    aside {
        width: 20%;
        height: 610px;
        float: left;
    }
    img {
        width: 100%;
        height: 610px;
    }
    header, footer, nav {
        float: left;
        background-color: MediumPurple;
        color: white;
        width: 80%;
        height: 50px;
        text-align: center;
        text-shadow: 4px 4px DimGray;
    }
    nav {
        padding-top: 10px;
    }
    main {
        float: left;
        background-color: Thistle;
        width: 80%;
        height: 450px;
        overflow: auto;
    }
    li button {
        float: right;
        background-color: MediumPurple;
        color: white;
        height: 30px;
        border: none;
    }
    ul {
        margin: 40px auto;
        width: 70%;
        font-size: 25px;
        border: 2px solid MediumPurple;
        list-style-type: none;
    }
    li {
        padding: 10px;
    }
    Podgląd strony po tym kroku (notatki.html)
  32. Krok 32: Parzyste elementy listy: tło

    Kryteria: R.4.10 ok. 1 min

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

    Pseudoklasa :nth-child(even) wybiera co drugi element (2., 4., 6. …) wśród rodzeństwa - dokładnie „parzyste elementy listy”. Nie trzeba dodawać klas do co drugiego <li> - i dobrze, bo nowe elementy dopisze skrypt, a reguła obejmie je automatycznie. Efekt: pasy jak na ilustracji 2.

    Typowe błędy

    :nth-child(odd) to nieparzyste (1., 3., 5.). :nth-child(2n) działa tak samo jak even i też jest poprawne.

    styl.css CSS zmienione: 3 linie
    * {
        font-family: Verdana;
    }
    aside {
        width: 20%;
        height: 610px;
        float: left;
    }
    img {
        width: 100%;
        height: 610px;
    }
    header, footer, nav {
        float: left;
        background-color: MediumPurple;
        color: white;
        width: 80%;
        height: 50px;
        text-align: center;
        text-shadow: 4px 4px DimGray;
    }
    nav {
        padding-top: 10px;
    }
    main {
        float: left;
        background-color: Thistle;
        width: 80%;
        height: 450px;
        overflow: auto;
    }
    li button {
        float: right;
        background-color: MediumPurple;
        color: white;
        height: 30px;
        border: none;
    }
    ul {
        margin: 40px auto;
        width: 70%;
        font-size: 25px;
        border: 2px solid MediumPurple;
        list-style-type: none;
    }
    li {
        padding: 10px;
    }
    li:nth-child(even) {
        background-color: #DDCADD;
    }
    Podgląd strony po tym kroku (notatki.html)
  33. Krok 33: Element listy pod kursorem: tło

    Kryteria: R.4.10 ok. 1 min

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

    Pseudoklasa :hover opisuje stan „kursor nad elementem”. Reguła stoi po li:nth-child(even) i ma tę samą wagę, więc przy najechaniu wygrywa - także dla parzystych elementów. Najedź w podglądzie na dowolne zadanie. Arkusz stylów jest kompletny.

    Typowe błędy

    li:hover zapisane przed regułą dla parzystych przegrywa z nią (ta sama specyficzność, późniejsza reguła wygrywa) - parzyste elementy nie zmienią koloru pod kursorem.

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

    Kroki 34-40 · po tym etapie masz 37 z 37 kryteriów (pkt)

    Krok 34: Plik script.js i funkcja dla przycisków „Wykonane”

    Kryteria: R.3.5, R.5.1, R.5.7 ok. 3 min

    Arkusz: Napisany w języku JavaScript i działający na liście punktowanej. Przycisk o treści „Wykonane”, którego wciśnięcie powoduje wywołanie skryptu dla danego elementu listy.

    Tworzymy plik script.js i podpinamy go na końcu <body> przez <script src="script.js"></script> - na końcu, żeby lista już istniała, gdy skrypt zacznie działać.

    Funkcja markDone(button) ma przekreślić zadanie przy tym konkretnym przycisku, więc musi wiedzieć, który przycisk kliknięto. Najprostszy sposób z tabeli 1 arkusza: atrybut onclick="markDone(this)" na każdym z sześciu przycisków - słowo this w atrybucie oznacza „ten element, który kliknięto”, i trafia do funkcji jako parametr button. Ciało funkcji wypełnimy w następnym kroku.

    Typowe błędy

    Skrypt podpięty w <head> wykona się, zanim powstanie lista - dla onclick to jeszcze nie problem (funkcje są tylko definiowane), ale przy nasłuchiwaczach z refaktoru getElementById zwróci null.

    notatki.html HTML zmienione: 7 linii
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <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>
            <input type="text" id="task-input" placeholder="Dodaj zadanie...">
            <button>Dodaj</button>
        </nav>
        <main>
            <ul id="task-list">
                <li>Wyprowadzić psa <button onclick="markDone(this)">Wykonane</button></li>
                <li>Gimnastyka <button onclick="markDone(this)">Wykonane</button></li>
                <li>Zakupy <button onclick="markDone(this)">Wykonane</button></li>
                <li>Spacer z kumplem <button onclick="markDone(this)">Wykonane</button></li>
                <li>Odrabianie lekcji z młodszą siostrą <button onclick="markDone(this)">Wykonane</button></li>
                <li>Projekt na geografię <button onclick="markDone(this)">Wykonane</button></li>
            </ul>
        </main>
        <footer>
            <h3>Notatki: Bartosz Bryniarski</h3>
        </footer>
        <script src="script.js"></script>
    </body>
    </html>
    script.js JavaScript nowy plik
    function markDone(button) {
    }
    Podgląd strony po tym kroku (notatki.html)
  35. Krok 35: Przekreślenie zadania

    Kryteria: R.5.5 ok. 3 min

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

    Jedna linia. button.parentElement to rodzic przycisku, czyli <li> z tekstem zadania - „element listy związany z tym przyciskiem”. Jego styl zmieniamy przez style.textDecoration = 'line-through' (nazwa CSS text-decoration w JavaScript traci myślnik: textDecoration).

    Sam przycisk nie zostaje przekreślony, bo jako element pływający (float: right) nie dziedziczy dekoracji tekstu. Podgląd działa - kliknij „Wykonane” przy dowolnym zadaniu; kolejność klikania nie ma znaczenia, bo każdy przycisk zna swojego rodzica (klucz R.5.5).

    Typowe błędy

    document.querySelector('li').style... przekreśli zawsze pierwsze zadanie, niezależnie od tego, który przycisk kliknięto - R.5.5 wymaga, żeby przekreślał się tylko skojarzony tekst.

    script.js JavaScript zmienione: 1 linia
    function markDone(button) {
        button.parentElement.style.textDecoration = 'line-through';
    }
    Podgląd strony po tym kroku (notatki.html)
  36. Krok 36: Funkcja dla przycisku „Dodaj”

    Kryteria: R.3.4 ok. 1 min

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

    Druga funkcja, addTask(), na razie pusta. Przycisk „Dodaj” dostaje onclick="addTask()" - tu nie trzeba przekazywać this, bo funkcja i tak wie, skąd wziąć dane: z pola task-input i listy task-list, które mają identyfikatory. To spełnia kryterium R.3.4.

    notatki.html HTML zmienione: 1 linia
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <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>
            <input type="text" id="task-input" placeholder="Dodaj zadanie...">
            <button onclick="addTask()">Dodaj</button>
        </nav>
        <main>
            <ul id="task-list">
                <li>Wyprowadzić psa <button onclick="markDone(this)">Wykonane</button></li>
                <li>Gimnastyka <button onclick="markDone(this)">Wykonane</button></li>
                <li>Zakupy <button onclick="markDone(this)">Wykonane</button></li>
                <li>Spacer z kumplem <button onclick="markDone(this)">Wykonane</button></li>
                <li>Odrabianie lekcji z młodszą siostrą <button onclick="markDone(this)">Wykonane</button></li>
                <li>Projekt na geografię <button onclick="markDone(this)">Wykonane</button></li>
            </ul>
        </main>
        <footer>
            <h3>Notatki: Bartosz Bryniarski</h3>
        </footer>
        <script src="script.js"></script>
    </body>
    </html>
    script.js JavaScript zmienione: 3 linie
    function markDone(button) {
        button.parentElement.style.textDecoration = 'line-through';
    }
    
    function addTask() {
    }
    Podgląd strony po tym kroku (notatki.html)
  37. Krok 37: Dodawanie: odczyt tekstu z pola

    Kryteria: R.5.2, R.5.7 ok. 2 min

    Arkusz: 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.

    document.getElementById('task-input') (tabela 1) znajduje pole po identyfikatorze, a .value to tekst, który użytkownik wpisał. Zapisujemy go w stałej text. const (jak w przykładach z tabeli 3 arkusza) deklaruje zmienną, której nie będziemy nadpisywać - nazwy po angielsku, znaczące (R.5.7).

    Typowe błędy

    input.innerText albo input.innerHTML dla pola <input> daje pusty napis - wpisany tekst jest w value.

    script.js JavaScript zmienione: 2 linie
    function markDone(button) {
        button.parentElement.style.textDecoration = 'line-through';
    }
    
    function addTask() {
        const input = document.getElementById('task-input');
        const text = input.value;
    }
    Podgląd strony po tym kroku (notatki.html)
  38. Krok 38: Dodawanie: nowy element listy z treścią

    Kryteria: R.5.2 ok. 2 min

    Arkusz: Tworzy nowy element listy na jej końcu. Element składa się z treści pobranej z pola edycyjnego.

    document.createElement('li') (tabela 3 arkusza) tworzy nowy, jeszcze nigdzie niewstawiony element <li>. item.innerText = text wpisuje do niego tekst z pola. Element istnieje tylko w pamięci - na stronie pojawi się dopiero po appendChild, za dwa kroki.

    Typowe błędy

    item.innerHTML = text też zadziała, ale wpisany przez użytkownika tekst zostałby potraktowany jak HTML - innerText wstawia go dosłownie.

    script.js JavaScript zmienione: 2 linie
    function markDone(button) {
        button.parentElement.style.textDecoration = 'line-through';
    }
    
    function addTask() {
        const input = document.getElementById('task-input');
        const text = input.value;
        const item = document.createElement('li');
        item.innerText = text;
    }
    Podgląd strony po tym kroku (notatki.html)
  39. Krok 39: Dodawanie: przycisk „Wykonane” w nowym elemencie

    Kryteria: R.5.3, R.5.4, R.5.6 ok. 3 min

    Arkusz: 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.

    Trzy rzeczy z tego zdania:

    • createElement('button') z innerText = 'Wykonane' - przycisk o wymaganej treści,
    • setAttribute('onclick', 'markDone(this)') (tabela 1) - dokładnie ten sam atrybut, który mają przyciski wpisane w HTML, więc kliknięcie wywoła markDone dla nowego elementu (R.5.6),
    • item.appendChild(button) - przycisk trafia do wnętrza nowego <li>.

    Formatowanie „jak reszta przycisków” (R.5.4) dostajemy za darmo: reguła CSS li button obejmuje każdy przycisk wewnątrz elementu listy, także dodany skryptem.

    Typowe błędy

    Przycisk dodany obok <li> (list.appendChild(button)) nie dostanie stylu li button i nie będzie wiedział, które zadanie przekreślić - R.5.3 i R.5.4 przepadają.

    script.js JavaScript zmienione: 4 linie
    function markDone(button) {
        button.parentElement.style.textDecoration = 'line-through';
    }
    
    function addTask() {
        const input = document.getElementById('task-input');
        const text = input.value;
        const item = document.createElement('li');
        item.innerText = text;
        const button = document.createElement('button');
        button.innerText = 'Wykonane';
        button.setAttribute('onclick', 'markDone(this)');
        item.appendChild(button);
    }
    Podgląd strony po tym kroku (notatki.html)
  40. Krok 40: Dodawanie: element na końcu listy

    Kryteria: R.5.2, R.5.6 ok. 2 min

    Arkusz: Tworzy nowy element listy na jej końcu.

    document.getElementById('task-list').appendChild(item) - gotowy <li> (tekst + przycisk) trafia na koniec listy; appendChild zawsze dokłada jako ostatnie dziecko (tabela 3 arkusza). Nowy element od razu dostaje style listy, a jeśli jest parzysty - także jasne tło.

    Podgląd działa jak na ilustracji 4: wpisz „Lekarz”, kliknij „Dodaj”, potem „Wykonane” przy nowym zadaniu. Skrypt spełnia wszystkie kryteria arkusza - dalej refaktor na wersję, jaką pisze się w prawdziwych projektach.

    Typowe błędy

    document.body.appendChild(item) (jak w pierwszym przykładzie tabeli 3) wstawi element na koniec strony, poza listą - bez stylów i poza blokiem głównym. Rodzicem ma być <ul>.

    script.js JavaScript zmienione: 1 linia
    function markDone(button) {
        button.parentElement.style.textDecoration = 'line-through';
    }
    
    function addTask() {
        const input = document.getElementById('task-input');
        const text = input.value;
        const item = document.createElement('li');
        item.innerText = text;
        const button = document.createElement('button');
        button.innerText = 'Wykonane';
        button.setAttribute('onclick', 'markDone(this)');
        item.appendChild(button);
        document.getElementById('task-list').appendChild(item);
    }
    Podgląd strony po tym kroku (notatki.html)
  41. Etap 5: Dobre praktyki (poza kluczem)

    Kroki 41-45 · po tym etapie masz 37 z 37 kryteriów (pkt)

    Krok 41: Refaktor: HTML bez atrybutów onclick

    Kryteria: R.5.1 ok. 2 min

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

    Rozwiązanie z onclick jest poprawne i na egzaminie wystarcza. W prawdziwych projektach zachowanie strony trzyma się w skrypcie, a HTML opisuje tylko strukturę - dzięki temu jeden plik JS można zmienić bez ruszania HTML, a kod nie jest rozsiany po atrybutach.

    Stare linie zostają w komentarzu HTML <!-- … --> do porównania, a pod nimi te same elementy bez onclick. Przycisk „Dodaj” dostaje id="add-button", żeby skrypt mógł go znaleźć. Po tym kroku kliknięcia przestają działać - podłączymy je w skrypcie.

    Typowe błędy

    Komentarz HTML nie może zawierać -- w środku ani być zagnieżdżony - jeden <!-- i jeden --> na całą grupę linii.

    notatki.html HTML zmienione: 10 linii
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <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>
            <input type="text" id="task-input" placeholder="Dodaj zadanie...">
            <!-- <button onclick="addTask()">Dodaj</button> -->
            <button id="add-button">Dodaj</button>
        </nav>
        <main>
            <ul id="task-list">
                <!-- wersja z atrybutem onclick:
                <li>Wyprowadzić psa <button onclick="markDone(this)">Wykonane</button></li>
                <li>Gimnastyka <button onclick="markDone(this)">Wykonane</button></li>
                <li>Zakupy <button onclick="markDone(this)">Wykonane</button></li>
                <li>Spacer z kumplem <button onclick="markDone(this)">Wykonane</button></li>
                <li>Odrabianie lekcji z młodszą siostrą <button onclick="markDone(this)">Wykonane</button></li>
                <li>Projekt na geografię <button onclick="markDone(this)">Wykonane</button></li>
                -->
                <li>Wyprowadzić psa <button>Wykonane</button></li>
                <li>Gimnastyka <button>Wykonane</button></li>
                <li>Zakupy <button>Wykonane</button></li>
                <li>Spacer z kumplem <button>Wykonane</button></li>
                <li>Odrabianie lekcji z młodszą siostrą <button>Wykonane</button></li>
                <li>Projekt na geografię <button>Wykonane</button></li>
            </ul>
        </main>
        <footer>
            <h3>Notatki: Bartosz Bryniarski</h3>
        </footer>
        <script src="script.js"></script>
    </body>
    </html>
    Podgląd strony po tym kroku (notatki.html)
  42. Krok 42: Refaktor: nasłuchiwacz dla przycisku „Dodaj”

    Kryteria: R.3.4 ok. 2 min

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

    addEventListener('click', addTask) mówi przeglądarce: „gdy ten element zostanie kliknięty, uruchom addTask”. Pierwszy argument to nazwa zdarzenia (click, bez „on”), drugi - funkcja, nie jej wywołanie: addTask bez nawiasów. Linia stoi poza funkcjami, na końcu pliku - wykona się raz, przy wczytaniu skryptu, a skrypt jest na końcu <body>, więc przycisk już istnieje. „Dodaj” w podglądzie znów działa.

    Typowe błędy

    addEventListener('click', addTask()) z nawiasami wywołuje funkcję od razu przy rejestracji (doda puste zadanie) i jako nasłuchiwacz przekazuje jej wynik (undefined) - kliknięcie nic nie zrobi.

    script.js JavaScript zmienione: 2 linie
    function markDone(button) {
        button.parentElement.style.textDecoration = 'line-through';
    }
    
    function addTask() {
        const input = document.getElementById('task-input');
        const text = input.value;
        const item = document.createElement('li');
        item.innerText = text;
        const button = document.createElement('button');
        button.innerText = 'Wykonane';
        button.setAttribute('onclick', 'markDone(this)');
        item.appendChild(button);
        document.getElementById('task-list').appendChild(item);
    }
    
    document.getElementById('add-button').addEventListener('click', addTask);
    Podgląd strony po tym kroku (notatki.html)
  43. Krok 43: Refaktor: nasłuchiwacze dla przycisków „Wykonane”

    Kryteria: R.3.5, R.5.5 ok. 3 min

    Arkusz: Przycisk o treści „Wykonane”, którego wciśnięcie powoduje wywołanie skryptu dla danego elementu listy.

    Sześć przycisków, więc pętla. document.querySelectorAll('#task-list button') (tabela 1) zwraca wszystkie przyciski wewnątrz listy - selektor jak w CSS. for (const button of ...) przechodzi po nich, a każdy dostaje własny nasłuchiwacz. Funkcja anonimowa function () { markDone(button); } jest potrzebna, bo markDone wymaga argumentu, a nasłuchiwacz wywołuje funkcję bez argumentów - const button z pętli pamięta w każdym obiegu „swój” przycisk.

    Przekreślanie w podglądzie działa dla sześciu zadań ze stanu początkowego. Nowe zadania dodawane przez addTask jeszcze używają setAttribute('onclick', …) - poprawimy to w następnym kroku.

    Typowe błędy

    var button zamiast const button w tej pętli to klasyczny błąd domknięć - wszystkie nasłuchiwacze widziałyby ostatni przycisk i każde kliknięcie przekreślałoby ostatnie zadanie.

    script.js JavaScript zmienione: 6 linii
    function markDone(button) {
        button.parentElement.style.textDecoration = 'line-through';
    }
    
    function addTask() {
        const input = document.getElementById('task-input');
        const text = input.value;
        const item = document.createElement('li');
        item.innerText = text;
        const button = document.createElement('button');
        button.innerText = 'Wykonane';
        button.setAttribute('onclick', 'markDone(this)');
        item.appendChild(button);
        document.getElementById('task-list').appendChild(item);
    }
    
    document.getElementById('add-button').addEventListener('click', addTask);
    
    for (const button of document.querySelectorAll('#task-list button')) {
        button.addEventListener('click', function () {
            markDone(button);
        });
    }
    Podgląd strony po tym kroku (notatki.html)
  44. Krok 44: Refaktor: nasłuchiwacz dla nowego przycisku

    Kryteria: R.5.6 ok. 2 min

    Arkusz: 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.

    W addTask zamiast setAttribute('onclick', …) rejestrujemy nasłuchiwacz tak samo jak w pętli - stara linia zostaje w komentarzu //. Teraz żaden przycisk nie ma atrybutu onclick, a cała logika jest w jednym miejscu. Wpisz zadanie, dodaj, kliknij „Wykonane” przy nowym - działa (R.5.6).

    script.js JavaScript zmienione: 4 linie
    function markDone(button) {
        button.parentElement.style.textDecoration = 'line-through';
    }
    
    function addTask() {
        const input = document.getElementById('task-input');
        const text = input.value;
        const item = document.createElement('li');
        item.innerText = text;
        const button = document.createElement('button');
        button.innerText = 'Wykonane';
        // button.setAttribute('onclick', 'markDone(this)');
        button.addEventListener('click', function () {
            markDone(button);
        });
        item.appendChild(button);
        document.getElementById('task-list').appendChild(item);
    }
    
    document.getElementById('add-button').addEventListener('click', addTask);
    
    for (const button of document.querySelectorAll('#task-list button')) {
        button.addEventListener('click', function () {
            markDone(button);
        });
    }
    Podgląd strony po tym kroku (notatki.html)
  45. Krok 45: Refaktor: walidacja pola i porządek po dodaniu

    ok. 3 min

    Arkusz: Tworzy nowy element listy na jej końcu. Element składa się z treści pobranej z pola edycyjnego.

    Dwie rzeczy, których arkusz nie wymaga, a użytkownik oczekuje:

    • walidacja: trim() obcina spacje z obu stron, a if (text === '') return; przerywa funkcję, gdy pole jest puste - bez tego kliknięcie „Dodaj” tworzyło pusty element listy z samym przyciskiem,
    • porządek: po dodaniu czyścimy pole (input.value = '') i wracamy do niego kursorem (input.focus()), żeby można było od razu wpisać kolejne zadanie.

    Sprawdź w podglądzie: pusty „Dodaj” nic nie robi, a po dodaniu zadania pole jest puste. To już wersja gotowa do prawdziwego użycia.

    Typowe błędy

    Walidacja to dodatek, nie wymaganie - klucz R.5.2 sprawdza tylko, czy tekst z pola trafia na koniec listy. Nie trać na to czasu, dopóki nie masz wszystkich kryteriów.

    script.js JavaScript zmienione: 6 linii
    function markDone(button) {
        button.parentElement.style.textDecoration = 'line-through';
    }
    
    function addTask() {
        const input = document.getElementById('task-input');
        const text = input.value.trim();
        if (text === '') {
            return;
        }
        const item = document.createElement('li');
        item.innerText = text;
        const button = document.createElement('button');
        button.innerText = 'Wykonane';
        // button.setAttribute('onclick', 'markDone(this)');
        button.addEventListener('click', function () {
            markDone(button);
        });
        item.appendChild(button);
        document.getElementById('task-list').appendChild(item);
        input.value = '';
        input.focus();
    }
    
    document.getElementById('add-button').addEventListener('click', addTask);
    
    for (const button of document.querySelectorAll('#task-list button')) {
        button.addEventListener('click', function () {
            markDone(button);
        });
    }
    Podgląd strony po tym kroku (notatki.html)
  46. Etap 6: Oddanie pracy

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

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

    ok. 5 min

    Arkusz: 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.

    W przeglądarka.txt wpisujemy nazwę przeglądarki, w której sprawdzaliśmy stronę. Na egzaminie nazwa pliku dokładnie jak w arkuszu, z „ą” - w tym rozwiązaniu plik nazywa się przegladarka.txt tylko dlatego, że podgląd nie przyjmuje polskich znaków w nazwach plików.

    W folderze z numerem zdającego muszą być: import.png, kw1.jpg, kw2.jpg, kw3.jpg, kw4.jpg, kwerendy.txt, notatki.html, obraz.jpg, przeglądarka.txt, styl.css oraz script.js (arkusz dopuszcza „inne przygotowane pliki”). Nagrywamy płytę, sprawdzamy jej odczyt i opisujemy numerem zdającego. Pusta lub nieczytelna płyta to zero punktów za wszystkie pięć rezultatów.

    przegladarka.txt Tekst nowy plik
    Google Chrome

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