Przejdź do głównej treści
INF.03-04-26.06-SGCzerwiec 2026 · 54 kroki · ok. 100 min · 39 kryteriów z klucza

Rozwiązanie krok po kroku: Strona rezerwacji biura podróży (Nowoczesny kod (grid, fieldset))

To drugi sposób rozwiązania tego samego arkusza. Wynik jest ten sam - baza wyprawy z czterema zapytaniami, grafika z przezroczystym tłem, strona rezerwacja.html z dwoma przyciskami przełączającymi sekcje i przewijaniem zdjęć - i tak samo spełnia każde kryterium klucza. Inna jest droga:

  • siatka CSS z nazwanymi obszarami w bloku głównym zamiast float i clear - szerokości 90%, 45% i 10% stoją w grid-template-columns, co klucz wprost dopuszcza, a blok boczny może stać w kodzie na końcu,
  • stan przycisków w atrybucie aria-pressed: skrypt tylko przełącza stan, a kolor DodgerBlue wynika z reguły CSS - całe formatowanie zostaje w styl.css,
  • grupy pól <fieldset disabled>: jedna właściwość wyłącza wszystkie kontrolki sekcji naraz,
  • skrypt w osobnym pliku skrypt.js z atrybutem defer: tablica obiektów z danymi wycieczek napędza i przewijanie, i dziewięć zdjęć w bloku bocznym, obsługa przez addEventListener, zawinięcie resztą z dzielenia, tekst przez textContent.

Na końcu krótkie porównanie z wersją podstawową (zakładka obok). Baza i grafiki pochodzą z prawdziwego archiwum pliki4, zapytania zostały wykonane na tej bazie w MariaDB, a skrypt sprawdzony w prawdziwej przeglądarce: kolory przycisków, aktywność kontrolek i kolejność zdjęć zgadzają się z ilustracjami 4 i 5 oraz z kolejnością podaną w arkuszu.

Jak liczą się punkty. Klucz ma 39 kryteriów (R.1.1 - R.5.7), każde za 1 punkt. Próg zdania to 75%, czyli 30 punktów. Przy każdym kroku jest napisane, które kryteria spełnia.

  1. Etap 1: Baza danych

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

    Krok 1: Baza danych wyprawy

    ok. 2 min
    Polecenie z arkusza

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

    Archiwum pliki4.7z z pulpitu rozpakuj hasłem RezErw@cjE i załóż folder z numerem zdającego. W phpMyAdmin Nowa → nazwa wyprawy, porównywanie utf8_polish_ci. To samo w SQL:

    CREATE DATABASE wyprawy CHARACTER SET utf8 COLLATE utf8_polish_ci;
    
  2. Krok 2: Import tabel z pliku wyprawy.sql

    ok. 2 min
    Polecenie z arkusza

    Do utworzonej bazy zaimportuj tabele z pliku wyprawy.sql

    Baza wyprawy → Import → wyprawy.sql → Wykonaj. Dwie tabele z ilustracji 1: wycieczki (3 miejsca) i rezerwacje (10 wierszy) z kluczem obcym Wycieczki_id.

  3. Krok 3: Zrzut ekranu import.png

    Kryteria:

    R.1.1 Wykonany import tabel do bazy danych wyprawy - plik import.png

    ok. 2 min
    Polecenie z arkusza

    Wykonaj zrzut ekranu po imporcie. Zapisz zrzut w formacie PNG pod nazwą import. Nie kadruj zrzutu. Powinien on obejmować cały ekran monitora, z widocznym paskiem zadań. Na zrzucie powinny być widoczne elementy wskazujące na poprawnie wykonany import tabel

    Cały ekran z paskiem zadań, zapis jako import.png. Wszystkie zrzuty w tym arkuszu są w PNG.

    Typowe błędy

    Wykadrowany zrzut albo zrzut bez paska zadań nie jest oceniany.

  4. Krok 4: Zapytanie 1: rezerwacje z all inclusive

    Kryteria: ,

    R.1.2 W pliku kwerendy.txt zapisane co najmniej jedno zapytanie SQL, wynikające z treści zadania

    R.1.3 Wykonane zapytanie 1 wybierające jedynie imiona i nazwiska osób rezerwujących wycieczki z wyżywieniem typu 3. W pliku z kwerendami lub na zrzucie istnieje zapis np.: SELECT imie, nazwisko FROM rezerwacje WHERE wyzywienie = 3; oraz na zrzucie są dokładnie 4 rekordy dla: Anny, Jana, Judyty i Jolanty

    ok. 2 min
    Polecenie z arkusza

    Zapytanie 1: wybierające jedynie imiona i nazwiska osób rezerwujących wycieczki z wyżywieniem typu 3 (all inclusive)

    Każda klauzula w osobnej linii - przy dłuższych zapytaniach od razu widać, co jest listą kolumn, co źródłem, a co warunkiem. Wynik: Anna, Jan, Judyta i Jolanta, jak w kluczu. Zapytanie do kwerendy.txt, zrzut kw1.png.

    kwerendy.txt Tekst nowy plik
    -- Zapytanie 1
    SELECT imie, nazwisko
    FROM rezerwacje
    WHERE wyzywienie = 3;
    Podgląd strony po tym kroku
    Podgląd strony po kroku 4
  5. Krok 5: Zapytanie 2: liczba rezerwacji do Rzymu z Warszawy

    Kryteria:

    R.1.4 Wykonane zapytanie 2 zliczające rezerwacje na wycieczkę o id równym 2 i wylocie z Warszawy. W pliku z kwerendami lub na zrzucie istnieje zapis np.: SELECT COUNT(*) FROM rezerwacje WHERE Wycieczki_id = 2 AND wylot = "Warszawa"; oraz na zrzucie jest widoczna wartość 2

    ok. 2 min
    Polecenie z arkusza

    Zapytanie 2: zliczające rezerwacje na wycieczkę o id równym 2 i wylocie z Warszawy

    COUNT(*) liczy wiersze spełniające oba warunki połączone AND. Alias liczba_rezerwacji nadaje kolumnie wyniku czytelną nazwę zamiast COUNT(*) - arkusz go nie wymaga, ale zrzut od razu mówi, co oznacza liczba. Napis w apostrofach - to standard SQL (cudzysłowy też zadziałają w MySQL, ale nie w każdej bazie). Wynik: 2, zrzut kw2.png.

    kwerendy.txt Tekst zmienione: 6 linii
    -- Zapytanie 1
    SELECT imie, nazwisko
    FROM rezerwacje
    WHERE wyzywienie = 3;
    
    -- Zapytanie 2
    SELECT COUNT(*) AS liczba_rezerwacji
    FROM rezerwacje
    WHERE Wycieczki_id = 2
      AND wylot = 'Warszawa';
    Podgląd strony po tym kroku
    Podgląd strony po kroku 5
  6. Krok 6: Zapytanie 3: miejsca wycieczek z rezerwującymi

    Kryteria:

    R.1.5 Wykonane zapytanie 3 wybierające jedynie miejsca wycieczek i odpowiadające im imiona i nazwiska rezerwujących. W pliku z kwerendami lub na zrzucie istnieje zapis np.: SELECT miejsce, imie, nazwisko FROM wycieczki JOIN rezerwacje ON wycieczki.id = Wycieczki_id; (lub porównanie kluczy po WHERE ) oraz na zrzucie jest dokładnie 10 rekordów

    ok. 3 min
    Polecenie z arkusza

    Zapytanie 3: wybierające jedynie miejsca wycieczek i odpowiadające im imiona i nazwiska rezerwujących. Należy posłużyć się relacją

    • FROM wycieczki AS w i JOIN rezerwacje AS r - krótkie aliasy tabel,
    • ON r.Wycieczki_id = w.id - klucz obcy rezerwacji równa się kluczowi głównemu wycieczki,
    • w.miejsce, r.imie - przedrostek mówi, z której tabeli jest kolumna; przy dwóch tabelach z kolumną id bez przedrostków łatwo o niejednoznaczność.

    Warunek złączenia w ON (a nie w WHERE z przecinkiem między tabelami) oddziela „jak połączyć tabele” od „które wiersze wybrać”. Wynik: 10 wierszy, zrzut kw3.png.

    kwerendy.txt Tekst zmienione: 5 linii
    -- Zapytanie 1
    SELECT imie, nazwisko
    FROM rezerwacje
    WHERE wyzywienie = 3;
    
    -- Zapytanie 2
    SELECT COUNT(*) AS liczba_rezerwacji
    FROM rezerwacje
    WHERE Wycieczki_id = 2
      AND wylot = 'Warszawa';
    
    -- Zapytanie 3
    SELECT w.miejsce, r.imie, r.nazwisko
    FROM wycieczki AS w
    JOIN rezerwacje AS r ON r.Wycieczki_id = w.id;
    Podgląd strony po tym kroku
    Podgląd strony po kroku 6
  7. Krok 7: Zapytanie 4: liczba osób na każdej wycieczce

    Kryteria:

    R.1.6 Wykonane zapytanie 4 wybierające jedynie miejsca wycieczek i sumujące liczbę osób, dla których została zarezerwowana wycieczka. Jedna osoba (jeden rekord) może rezerwować wycieczkę dla kilku osób. W pliku z kwerendami lub na zrzucie istnieje zapis np.: SELECT miejsce, SUM(osob) FROM wycieczki JOIN rezerwacje ON wycieczki.id = Wycieczki_id GROUP BY miejsce; (lub porównanie kluczy po WHERE ) oraz na zrzucie są dane: Barcelona 5, Londyn 4, Rzym 10

    ok. 3 min
    Polecenie z arkusza

    Zapytanie 4: wybierające jedynie miejsca wycieczek i sumujące liczbę osób, dla których została zarezerwowana wycieczka. Jedna osoba (jeden rekord) może rezerwować wycieczkę dla kilku osób. Należy posłużyć się relacją i grupowaniem

    To samo złączenie, GROUP BY w.miejsce zlewa wiersze jednego miejsca w jeden, a SUM(r.osob) dodaje liczby osób z rezerwacji grupy (alias liczba_osob). Wynik: Barcelona 5, Londyn 4, Rzym 10 - jak w kluczu. Zrzut kw4.png.

    Typowe błędy

    COUNT(*) zamiast SUM(r.osob) liczy rezerwacje, a nie osoby - arkusz uprzedza, że jedna rezerwacja może być dla kilku osób.

    kwerendy.txt Tekst zmienione: 6 linii
    -- Zapytanie 1
    SELECT imie, nazwisko
    FROM rezerwacje
    WHERE wyzywienie = 3;
    
    -- Zapytanie 2
    SELECT COUNT(*) AS liczba_rezerwacji
    FROM rezerwacje
    WHERE Wycieczki_id = 2
      AND wylot = 'Warszawa';
    
    -- Zapytanie 3
    SELECT w.miejsce, r.imie, r.nazwisko
    FROM wycieczki AS w
    JOIN rezerwacje AS r ON r.Wycieczki_id = w.id;
    
    -- Zapytanie 4
    SELECT w.miejsce, SUM(r.osob) AS liczba_osob
    FROM wycieczki AS w
    JOIN rezerwacje AS r ON r.Wycieczki_id = w.id
    GROUP BY w.miejsce;
    Podgląd strony po tym kroku
    Podgląd strony po kroku 7
  8. Etap 2: Grafika i strona HTML

    Kroki 8-35 · po tym etapie masz 20 z 39 kryteriów (pkt)

    Krok 8: Grafika osoba.png: przezroczyste tło

    ok. 3 min
    Polecenie z arkusza

    Tło przezroczyste

    W GIMP-ie Warstwa → Przezroczystość → Dodaj kanał alfa, Zaznaczenie rozmyte (próg ok. 15) i klik w jasnoszare tło, Edycja → Wyczyść. Tło usuwa się na pełnej rozdzielczości - granica sylwetki wychodzi dokładniej niż na obrazie 100 px.

    Podgląd strony po tym kroku
    Podgląd strony po kroku 8
  9. Krok 9: Grafika osoba.png: szerokość 100 px

    Kryteria:

    R.2.1 Grafika osoba.png ma wymiary 100 px na 100 px i tło przezroczyste

    ok. 2 min
    Polecenie z arkusza

    Cechy grafiki osoba.png: Przeskalowana z zachowaniem proporcji do szerokości 100 px

    Obraz → Skaluj obraz, szerokość 100 px przy zamkniętym łańcuchu - obraz jest kwadratowy, więc wychodzi 100 × 100 px. Eksport do PNG zachowuje kanał alfa.

    Podgląd strony po tym kroku
    Podgląd strony po kroku 9
  10. Krok 10: Plik rezerwacja.html w HTML5

    ok. 1 min
    Polecenie z arkusza

    Cechy strony rezerwacja: Zapisana w języku HTML5

    Arkusz dopuszcza PHP albo HTML - bez kodu po stronie serwera wystarczy rezerwacja.html. <!DOCTYPE html> włącza tryb standardów HTML5.

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

    Kryteria:

    R.2.2 W pliku rezerwacja znajduje się deklaracja HTML5 <!DOCTYPE HTML> i deklaracja języka zawartości: polski np. <html lang="pl">

    ok. 1 min
    Polecenie z arkusza

    Zadeklarowany polski język zawartości witryny

    lang="pl" - czytnik ekranu czyta polską wymową, przeglądarka dobiera słownik i dzielenie wyrazów.

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

    ok. 1 min
    Polecenie z arkusza

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

    <meta charset="UTF-8"> jako pierwszy element <head>; plik też zapisz w UTF-8.

    rezerwacja.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 (rezerwacja.html)
  13. Krok 13: Tytuł strony

    Kryteria:

    R.2.3 W sekcji head znajduje się jawnie podany standard kodowania polskich znaków i tytuł strony "Biuro turystyczne"

    ok. 1 min
    Polecenie z arkusza

    Tytuł strony widoczny na karcie przeglądarki: „Biuro turystyczne”

    <title> w <head>. Obok <meta name="viewport"> - bez niego telefon pokazałby stronę pomniejszoną jak na monitorze. Arkusz tego nie wymaga, a w każdym współczesnym szablonie ta linia jest.

    rezerwacja.html HTML zmienione: 2 linie
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <title>Biuro turystyczne</title>
    </head>
    <body>
    </body>
    </html>
    Podgląd strony po tym kroku (rezerwacja.html)
  14. Krok 14: Podpięcie arkusza stylów

    Kryteria:

    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

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

    <link rel="stylesheet" href="styl.css">. W tej wersji w HTML nie ma ani jednego atrybutu style, a skrypt nie ustawia kolorów - całe formatowanie siedzi w styl.css.

    Typowe błędy

    Zła nazwa w href to brak stylów, a wtedy rezultat R.4 przepada.

    rezerwacja.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>Biuro turystyczne</title>
        <link rel="stylesheet" href="styl.css">
    </head>
    <body>
    </body>
    </html>
    Podgląd strony po tym kroku (rezerwacja.html)
  15. Krok 15: Strona wycieczki.html w budowie

    ok. 2 min
    Polecenie z arkusza

    Witryna składa się z podstron o nazwach rezerwacja (format PHP lub HTML), wycieczki.html. W pliku wycieczki.html należy zapisać „Strona w trakcie budowy”

    Szkielet HTML5 z jednym akapitem. Odnośnik „Do strony głównej” będzie miał dokąd prowadzić.

    wycieczki.html HTML nowy plik
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <title>Wycieczki</title>
    </head>
    <body>
        <p>Strona w trakcie budowy</p>
    </body>
    </html>
    Podgląd strony po tym kroku (wycieczki.html)
  16. Krok 16: Bloki semantyczne w kolejności treści

    Kryteria:

    R.2.4 W sekcji body zdefiniowany jest podział na bloki za pomocą semantycznych znaczników HTML5: header, main -> nav + section + section + aside, footer (dopuszcza się stosowanie bloków pomocniczych)

    ok. 3 min
    Polecenie z arkusza

    Podział strony na bloki: nagłówkowy, główny i stopki. Blok główny zawiera bloki: nawigacyjny, dwa bloki sekcji i blok boczny. Podział zrealizowany za pomocą semantycznych znaczników języka HTML5 tak, aby po uruchomieniu w przeglądarce układ bloków był zgodny z ilustracją 3

    header, w main kolejno nav, dwie section i aside, na końcu footer - dokładnie lista z klucza R.2.4. Blok boczny stoi w kodzie na końcu bloku głównego, bo to treść poboczna: czytnik ekranu przeczyta najpierw nawigację i formularz. Przy float musiałby stać pierwszy, żeby zaczynał się od góry; siatka CSS ustawia bloki niezależnie od kolejności w kodzie. Sekcje dostają identyfikatory client i trip - po nich siatka przypisze im miejsca.

    rezerwacja.html HTML zmienione: 14 linii
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <title>Biuro turystyczne</title>
        <link rel="stylesheet" href="styl.css">
    </head>
    <body>
        <header>
        </header>
        <main>
            <nav>
            </nav>
            <section id="client">
            </section>
            <section id="trip">
            </section>
            <aside>
            </aside>
        </main>
        <footer>
        </footer>
    </body>
    </html>
    Podgląd strony po tym kroku (rezerwacja.html)
  17. Krok 17: Nagłówek strony

    ok. 1 min
    Polecenie z arkusza

    Zawartość bloku nagłówkowego: nagłówek pierwszego stopnia o treści „Rezerwacja wycieczek”

    <h1> w <header>.

    rezerwacja.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>Biuro turystyczne</title>
        <link rel="stylesheet" href="styl.css">
    </head>
    <body>
        <header>
            <h1>Rezerwacja wycieczek</h1>
        </header>
        <main>
            <nav>
            </nav>
            <section id="client">
            </section>
            <section id="trip">
            </section>
            <aside>
            </aside>
        </main>
        <footer>
        </footer>
    </body>
    </html>
    Podgląd strony po tym kroku (rezerwacja.html)
  18. Krok 18: Blok boczny: 9 zdjęć z danych wycieczek

    ok. 4 min
    Polecenie z arkusza

    Zawartość bloku bocznego: 9 obrazów z tekstem alternatywnym „wycieczki”. Obrazy są zapętlone: 1.jpg, 2.jpg, 3.jpg, 1.jpg itd. Sposób realizacji dowolny

    „Sposób realizacji dowolny”, więc zdjęcia tworzy skrypt z danych. W pliku skrypt.js stoi tablica obiektów trips: każda wycieczka to para zdjęcie - miejsce. Ta sama tablica posłuży później przewijaniu w drugiej sekcji, więc zdjęcia i nazwy są zapisane w jednym miejscu.

    Pętla for tworzy dziewięć obrazów (document.createElement('img')), plik bierze z indeksu i % trips.length - reszta z dzielenia przez 3 daje 0, 1, 2, 0, 1, 2... - i dokleja je do bloku bocznego metodą append. const dla wartości, których się nie przypisuje ponownie, let dla licznika pętli.

    Skrypt jest podpięty w <head> atrybutem defer: przeglądarka pobiera go równolegle, ale wykonuje dopiero po wczytaniu całego HTML-u - querySelector('aside') znajdzie blok, choć znacznik <script> stoi wyżej. Klucz uznaje obrazy dodane skryptem (R.2.7: „w HTML lub skrypcie”).

    Typowe błędy

    <script src="skrypt.js"> w <head> bez defer wykona się przed powstaniem <aside> - querySelector zwróci null i skrypt przerwie się błędem.

    rezerwacja.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>Biuro turystyczne</title>
        <link rel="stylesheet" href="styl.css">
        <script src="skrypt.js" defer></script>
    </head>
    <body>
        <header>
            <h1>Rezerwacja wycieczek</h1>
        </header>
        <main>
            <nav>
            </nav>
            <section id="client">
            </section>
            <section id="trip">
            </section>
            <aside>
            </aside>
        </main>
        <footer>
        </footer>
    </body>
    </html>
    skrypt.js JavaScript nowy plik
    const trips = [
        { image: '1.jpg', place: 'Barcelona' },
        { image: '2.jpg', place: 'Rzym' },
        { image: '3.jpg', place: 'Londyn' },
    ];
    
    const gallery = document.querySelector('aside');
    for (let i = 0; i < 9; i++) {
        const photo = document.createElement('img');
        photo.src = trips[i % trips.length].image;
        photo.alt = 'wycieczki';
        gallery.append(photo);
    }
    Podgląd strony po tym kroku (rezerwacja.html)
  19. Krok 19: Przycisk „Uczestnik”

    ok. 2 min
    Polecenie z arkusza

    Zawartość bloku nawigacyjnego: Przycisk o treści „Uczestnik”, którego kliknięcie powoduje wywołanie funkcji uczestnika

    type="button" mówi wprost, że przycisk nie wysyła formularza. Atrybut aria-pressed="true" opisuje stan przycisku przełączającego: ten jest „wciśnięty”, bo na starcie aktywna jest sekcja klienta. Czytnik ekranu odczyta go jako „przycisk przełączający, wciśnięty”, a CSS pokoloruje przycisk właśnie po tym atrybucie. Obsługę kliknięcia dopisze skrypt.

    rezerwacja.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>Biuro turystyczne</title>
        <link rel="stylesheet" href="styl.css">
        <script src="skrypt.js" defer></script>
    </head>
    <body>
        <header>
            <h1>Rezerwacja wycieczek</h1>
        </header>
        <main>
            <nav>
                <button type="button" id="participant" aria-pressed="true">Uczestnik</button>
            </nav>
            <section id="client">
            </section>
            <section id="trip">
            </section>
            <aside>
            </aside>
        </main>
        <footer>
        </footer>
    </body>
    </html>
    Podgląd strony po tym kroku (rezerwacja.html)
  20. Krok 20: Przycisk „Rezerwacja”

    Kryteria:

    R.2.6 W bloku nawigacyjnym znajdują się dwa przyciski "Uczestnik" i "Rezerwacja"

    ok. 1 min
    Polecenie z arkusza

    Przycisk o treści „Rezerwacja”, którego kliknięcie powoduje wywołanie funkcji rezerwacji

    Drugi przycisk przełączający, na starcie aria-pressed="false".

    rezerwacja.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>Biuro turystyczne</title>
        <link rel="stylesheet" href="styl.css">
        <script src="skrypt.js" defer></script>
    </head>
    <body>
        <header>
            <h1>Rezerwacja wycieczek</h1>
        </header>
        <main>
            <nav>
                <button type="button" id="participant" aria-pressed="true">Uczestnik</button>
                <button type="button" id="booking" aria-pressed="false">Rezerwacja</button>
            </nav>
            <section id="client">
            </section>
            <section id="trip">
            </section>
            <aside>
            </aside>
        </main>
        <footer>
        </footer>
    </body>
    </html>
    Podgląd strony po tym kroku (rezerwacja.html)
  21. Krok 21: Pierwsza sekcja: nagłówek

    ok. 1 min
    Polecenie z arkusza

    Zawartość pierwszego bloku sekcji: Nagłówek drugiego stopnia o treści „Dane Klienta”

    <h2> w sekcji client.

    rezerwacja.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>Biuro turystyczne</title>
        <link rel="stylesheet" href="styl.css">
        <script src="skrypt.js" defer></script>
    </head>
    <body>
        <header>
            <h1>Rezerwacja wycieczek</h1>
        </header>
        <main>
            <nav>
                <button type="button" id="participant" aria-pressed="true">Uczestnik</button>
                <button type="button" id="booking" aria-pressed="false">Rezerwacja</button>
            </nav>
            <section id="client">
                <h2>Dane Klienta</h2>
            </section>
            <section id="trip">
            </section>
            <aside>
            </aside>
        </main>
        <footer>
        </footer>
    </body>
    </html>
    Podgląd strony po tym kroku (rezerwacja.html)
  22. Krok 22: Obraz osoba.png

    ok. 1 min
    Polecenie z arkusza

    Obraz osoba.png z tekstem alternatywnym „Klient”

    Grafika 100 × 100 px z przezroczystym tłem.

    rezerwacja.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>Biuro turystyczne</title>
        <link rel="stylesheet" href="styl.css">
        <script src="skrypt.js" defer></script>
    </head>
    <body>
        <header>
            <h1>Rezerwacja wycieczek</h1>
        </header>
        <main>
            <nav>
                <button type="button" id="participant" aria-pressed="true">Uczestnik</button>
                <button type="button" id="booking" aria-pressed="false">Rezerwacja</button>
            </nav>
            <section id="client">
                <h2>Dane Klienta</h2>
                <img src="osoba.png" alt="Klient">
            </section>
            <section id="trip">
            </section>
            <aside>
            </aside>
        </main>
        <footer>
        </footer>
    </body>
    </html>
    Podgląd strony po tym kroku (rezerwacja.html)
  23. Krok 23: Grupa pól klienta i pole „Imię”

    ok. 3 min
    Polecenie z arkusza

    Poniżej etykieta „Imię” oraz pole edycyjne z nią powiązane

    Kontrolki sekcji trafiają do <fieldset> - znacznika grupującego pola formularza. Jego atrybut disabled wyłącza naraz wszystkie kontrolki w środku, więc skrypt przełączy całą sekcję jedną właściwością.

    Para etykieta - pole stoi w akapicie <p>: akapit jest blokiem, więc para zaczyna się w nowej linii pod obrazem bez <br>. <label for="firstName"> wiąże etykietę z polem po id - kliknięcie w napis „Imię” ustawia kursor w polu. name przyda się, gdy formularz kiedyś zacznie wysyłać dane.

    rezerwacja.html HTML zmienione: 6 linii
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <title>Biuro turystyczne</title>
        <link rel="stylesheet" href="styl.css">
        <script src="skrypt.js" defer></script>
    </head>
    <body>
        <header>
            <h1>Rezerwacja wycieczek</h1>
        </header>
        <main>
            <nav>
                <button type="button" id="participant" aria-pressed="true">Uczestnik</button>
                <button type="button" id="booking" aria-pressed="false">Rezerwacja</button>
            </nav>
            <section id="client">
                <h2>Dane Klienta</h2>
                <img src="osoba.png" alt="Klient">
                <fieldset id="clientFields">
                    <p>
                        <label for="firstName">Imię</label>
                        <input type="text" id="firstName" name="firstName">
                    </p>
                </fieldset>
            </section>
            <section id="trip">
            </section>
            <aside>
            </aside>
        </main>
        <footer>
        </footer>
    </body>
    </html>
    Podgląd strony po tym kroku (rezerwacja.html)
  24. Krok 24: Etykieta i pole „Nazwisko”

    ok. 1 min
    Polecenie z arkusza

    Poniżej etykieta „Nazwisko” oraz pole edycyjne z nią powiązane

    Druga para w tej samej grupie clientFields.

    rezerwacja.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>Biuro turystyczne</title>
        <link rel="stylesheet" href="styl.css">
        <script src="skrypt.js" defer></script>
    </head>
    <body>
        <header>
            <h1>Rezerwacja wycieczek</h1>
        </header>
        <main>
            <nav>
                <button type="button" id="participant" aria-pressed="true">Uczestnik</button>
                <button type="button" id="booking" aria-pressed="false">Rezerwacja</button>
            </nav>
            <section id="client">
                <h2>Dane Klienta</h2>
                <img src="osoba.png" alt="Klient">
                <fieldset id="clientFields">
                    <p>
                        <label for="firstName">Imię</label>
                        <input type="text" id="firstName" name="firstName">
                    </p>
                    <p>
                        <label for="lastName">Nazwisko</label>
                        <input type="text" id="lastName" name="lastName">
                    </p>
                </fieldset>
            </section>
            <section id="trip">
            </section>
            <aside>
            </aside>
        </main>
        <footer>
        </footer>
    </body>
    </html>
    Podgląd strony po tym kroku (rezerwacja.html)
  25. Krok 25: Odnośnik do wycieczki.html

    Kryteria:

    R.3.4 Kliknięcie odnośnika "Do strony głównej" powoduje przejście do strony wycieczki.html

    ok. 1 min
    Polecenie z arkusza

    Paragraf z odnośnikiem o treści „Do strony głównej”, który prowadzi do strony wycieczki.html

    Akapit poza grupą pól - odnośnik nie jest kontrolką formularza i ma działać zawsze. Ścieżka względna, obie strony leżą w tym samym folderze.

    rezerwacja.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>Biuro turystyczne</title>
        <link rel="stylesheet" href="styl.css">
        <script src="skrypt.js" defer></script>
    </head>
    <body>
        <header>
            <h1>Rezerwacja wycieczek</h1>
        </header>
        <main>
            <nav>
                <button type="button" id="participant" aria-pressed="true">Uczestnik</button>
                <button type="button" id="booking" aria-pressed="false">Rezerwacja</button>
            </nav>
            <section id="client">
                <h2>Dane Klienta</h2>
                <img src="osoba.png" alt="Klient">
                <fieldset id="clientFields">
                    <p>
                        <label for="firstName">Imię</label>
                        <input type="text" id="firstName" name="firstName">
                    </p>
                    <p>
                        <label for="lastName">Nazwisko</label>
                        <input type="text" id="lastName" name="lastName">
                    </p>
                </fieldset>
                <p><a href="wycieczki.html">Do strony głównej</a></p>
            </section>
            <section id="trip">
            </section>
            <aside>
            </aside>
        </main>
        <footer>
        </footer>
    </body>
    </html>
    Podgląd strony po tym kroku (rezerwacja.html)
  26. Krok 26: Druga sekcja: nagłówek

    ok. 1 min
    Polecenie z arkusza

    Zawartość drugiego bloku sekcji: Nagłówek drugiego stopnia o treści „Dane rezerwacji”

    <h2> w sekcji trip.

    rezerwacja.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>Biuro turystyczne</title>
        <link rel="stylesheet" href="styl.css">
        <script src="skrypt.js" defer></script>
    </head>
    <body>
        <header>
            <h1>Rezerwacja wycieczek</h1>
        </header>
        <main>
            <nav>
                <button type="button" id="participant" aria-pressed="true">Uczestnik</button>
                <button type="button" id="booking" aria-pressed="false">Rezerwacja</button>
            </nav>
            <section id="client">
                <h2>Dane Klienta</h2>
                <img src="osoba.png" alt="Klient">
                <fieldset id="clientFields">
                    <p>
                        <label for="firstName">Imię</label>
                        <input type="text" id="firstName" name="firstName">
                    </p>
                    <p>
                        <label for="lastName">Nazwisko</label>
                        <input type="text" id="lastName" name="lastName">
                    </p>
                </fieldset>
                <p><a href="wycieczki.html">Do strony głównej</a></p>
            </section>
            <section id="trip">
                <h2>Dane rezerwacji</h2>
            </section>
            <aside>
            </aside>
        </main>
        <footer>
        </footer>
    </body>
    </html>
    Podgląd strony po tym kroku (rezerwacja.html)
  27. Krok 27: Znak « przed zdjęciem

    ok. 2 min
    Polecenie z arkusza

    Ogólny znacznik liniowy <span> ze znakiem «, jego kliknięcie powoduje zmianę zdjęcia w bloku sekcji drugiej

    Arkusz wymaga <span> - zostaje, ale z trzema atrybutami, dzięki którym da się go obsłużyć bez myszy:

    • role="button" - czytnik ekranu ogłasza go jako przycisk,
    • tabindex="0" - klawisz Tab zatrzymuje się na nim jak na przycisku,
    • aria-label="Poprzednia wycieczka" - zamiast „cudzysłów” czytnik powie, co robi.

    Znak zapisany encją &laquo;. Identyfikator previous posłuży skryptowi.

    Typowe błędy

    Zwykły <button> byłby tu nawet lepszy, ale klucz R.2.9 sprawdza dwa znaczniki <span> - formę z arkusza trzeba zostawić.

    rezerwacja.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>Biuro turystyczne</title>
        <link rel="stylesheet" href="styl.css">
        <script src="skrypt.js" defer></script>
    </head>
    <body>
        <header>
            <h1>Rezerwacja wycieczek</h1>
        </header>
        <main>
            <nav>
                <button type="button" id="participant" aria-pressed="true">Uczestnik</button>
                <button type="button" id="booking" aria-pressed="false">Rezerwacja</button>
            </nav>
            <section id="client">
                <h2>Dane Klienta</h2>
                <img src="osoba.png" alt="Klient">
                <fieldset id="clientFields">
                    <p>
                        <label for="firstName">Imię</label>
                        <input type="text" id="firstName" name="firstName">
                    </p>
                    <p>
                        <label for="lastName">Nazwisko</label>
                        <input type="text" id="lastName" name="lastName">
                    </p>
                </fieldset>
                <p><a href="wycieczki.html">Do strony głównej</a></p>
            </section>
            <section id="trip">
                <h2>Dane rezerwacji</h2>
                <span id="previous" role="button" tabindex="0" aria-label="Poprzednia wycieczka">&laquo;</span>
            </section>
            <aside>
            </aside>
        </main>
        <footer>
        </footer>
    </body>
    </html>
    Podgląd strony po tym kroku (rezerwacja.html)
  28. Krok 28: Zdjęcie wycieczki

    Kryteria:

    R.2.7 W bloku sekcji pierwszej znajduje się obraz osoba.png z tekstem alternatywnym "Klient" w bloku sekcji drugiej - 1.jpg z tekstem alternatywnym "Wycieczka" w bloku bocznym znajduje się 9 obrazów (w HTML lub skrypcie, część z nich może być ukryta)

    ok. 1 min
    Polecenie z arkusza

    Obraz 1.jpg z tekstem alternatywnym „wycieczka”

    Identyfikator tripImage - skrypt podmieni src. Z tym krokiem są wszystkie obrazy z klucza: osoba.png, 1.jpg i dziewięć zdjęć z pętli w bloku bocznym.

    rezerwacja.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>Biuro turystyczne</title>
        <link rel="stylesheet" href="styl.css">
        <script src="skrypt.js" defer></script>
    </head>
    <body>
        <header>
            <h1>Rezerwacja wycieczek</h1>
        </header>
        <main>
            <nav>
                <button type="button" id="participant" aria-pressed="true">Uczestnik</button>
                <button type="button" id="booking" aria-pressed="false">Rezerwacja</button>
            </nav>
            <section id="client">
                <h2>Dane Klienta</h2>
                <img src="osoba.png" alt="Klient">
                <fieldset id="clientFields">
                    <p>
                        <label for="firstName">Imię</label>
                        <input type="text" id="firstName" name="firstName">
                    </p>
                    <p>
                        <label for="lastName">Nazwisko</label>
                        <input type="text" id="lastName" name="lastName">
                    </p>
                </fieldset>
                <p><a href="wycieczki.html">Do strony głównej</a></p>
            </section>
            <section id="trip">
                <h2>Dane rezerwacji</h2>
                <span id="previous" role="button" tabindex="0" aria-label="Poprzednia wycieczka">&laquo;</span>
                <img src="1.jpg" alt="wycieczka" id="tripImage">
            </section>
            <aside>
            </aside>
        </main>
        <footer>
        </footer>
    </body>
    </html>
    Podgląd strony po tym kroku (rezerwacja.html)
  29. Krok 29: Znak » za zdjęciem

    Kryteria:

    R.2.9 Na stronie znajdują się dwa znaczniki <span> ze znakami &laquo; i &raquo;

    ok. 1 min
    Polecenie z arkusza

    Ogólny znacznik liniowy <span> ze znakiem », jego kliknięcie powoduje zmianę zdjęcia w bloku sekcji drugiej

    Lustrzany <span> z encją &raquo; i etykietą „Następna wycieczka”. Oba znaki i obraz są liniowe, więc stoją w jednym wierszu - jak na ilustracji 2.

    rezerwacja.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>Biuro turystyczne</title>
        <link rel="stylesheet" href="styl.css">
        <script src="skrypt.js" defer></script>
    </head>
    <body>
        <header>
            <h1>Rezerwacja wycieczek</h1>
        </header>
        <main>
            <nav>
                <button type="button" id="participant" aria-pressed="true">Uczestnik</button>
                <button type="button" id="booking" aria-pressed="false">Rezerwacja</button>
            </nav>
            <section id="client">
                <h2>Dane Klienta</h2>
                <img src="osoba.png" alt="Klient">
                <fieldset id="clientFields">
                    <p>
                        <label for="firstName">Imię</label>
                        <input type="text" id="firstName" name="firstName">
                    </p>
                    <p>
                        <label for="lastName">Nazwisko</label>
                        <input type="text" id="lastName" name="lastName">
                    </p>
                </fieldset>
                <p><a href="wycieczki.html">Do strony głównej</a></p>
            </section>
            <section id="trip">
                <h2>Dane rezerwacji</h2>
                <span id="previous" role="button" tabindex="0" aria-label="Poprzednia wycieczka">&laquo;</span>
                <img src="1.jpg" alt="wycieczka" id="tripImage">
                <span id="next" role="button" tabindex="0" aria-label="Następna wycieczka">&raquo;</span>
            </section>
            <aside>
            </aside>
        </main>
        <footer>
        </footer>
    </body>
    </html>
    Podgląd strony po tym kroku (rezerwacja.html)
  30. Krok 30: Nazwa miejsca wycieczki

    ok. 1 min
    Polecenie z arkusza

    Nagłówek czwartego stopnia o treści „Barcelona”

    <h4> z identyfikatorem tripName - skrypt zmieni jego tekst razem ze zdjęciem.

    rezerwacja.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>Biuro turystyczne</title>
        <link rel="stylesheet" href="styl.css">
        <script src="skrypt.js" defer></script>
    </head>
    <body>
        <header>
            <h1>Rezerwacja wycieczek</h1>
        </header>
        <main>
            <nav>
                <button type="button" id="participant" aria-pressed="true">Uczestnik</button>
                <button type="button" id="booking" aria-pressed="false">Rezerwacja</button>
            </nav>
            <section id="client">
                <h2>Dane Klienta</h2>
                <img src="osoba.png" alt="Klient">
                <fieldset id="clientFields">
                    <p>
                        <label for="firstName">Imię</label>
                        <input type="text" id="firstName" name="firstName">
                    </p>
                    <p>
                        <label for="lastName">Nazwisko</label>
                        <input type="text" id="lastName" name="lastName">
                    </p>
                </fieldset>
                <p><a href="wycieczki.html">Do strony głównej</a></p>
            </section>
            <section id="trip">
                <h2>Dane rezerwacji</h2>
                <span id="previous" role="button" tabindex="0" aria-label="Poprzednia wycieczka">&laquo;</span>
                <img src="1.jpg" alt="wycieczka" id="tripImage">
                <span id="next" role="button" tabindex="0" aria-label="Następna wycieczka">&raquo;</span>
                <h4 id="tripName">Barcelona</h4>
            </section>
            <aside>
            </aside>
        </main>
        <footer>
        </footer>
    </body>
    </html>
    Podgląd strony po tym kroku (rezerwacja.html)
  31. Krok 31: Pole liczby osób: 1 - 4, wartość 2, nieaktywne

    ok. 2 min
    Polecenie z arkusza

    Etykieta „Liczba osób:” oraz pole edycyjne z nią powiązane. Pole jest typu numerycznego, przyjmuje wartości od 1 do 4, wyświetla wartość 2. Pole jest nieaktywne

    type="number" z min="1", max="4" i value="2". Pole stoi w drugiej grupie, <fieldset id="bookingFields" disabled> - wyłączona grupa wyłącza wszystkie kontrolki w środku, więc samo pole nie potrzebuje własnego disabled. Działa to dokładnie tak samo: pole jest wyszarzone i nie da się go zmienić (selektor :disabled je obejmuje), a kolejne kontrolki tej sekcji trafią do tej samej grupy.

    Typowe błędy

    Egzaminator sprawdza działanie: pole ma być nieaktywne na stronie (R.3.5). Spróbuj w nie kliknąć w przeglądarce - jeśli da się zmienić wartość, grupa nie ma disabled.

    rezerwacja.html HTML zmienione: 6 linii
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <title>Biuro turystyczne</title>
        <link rel="stylesheet" href="styl.css">
        <script src="skrypt.js" defer></script>
    </head>
    <body>
        <header>
            <h1>Rezerwacja wycieczek</h1>
        </header>
        <main>
            <nav>
                <button type="button" id="participant" aria-pressed="true">Uczestnik</button>
                <button type="button" id="booking" aria-pressed="false">Rezerwacja</button>
            </nav>
            <section id="client">
                <h2>Dane Klienta</h2>
                <img src="osoba.png" alt="Klient">
                <fieldset id="clientFields">
                    <p>
                        <label for="firstName">Imię</label>
                        <input type="text" id="firstName" name="firstName">
                    </p>
                    <p>
                        <label for="lastName">Nazwisko</label>
                        <input type="text" id="lastName" name="lastName">
                    </p>
                </fieldset>
                <p><a href="wycieczki.html">Do strony głównej</a></p>
            </section>
            <section id="trip">
                <h2>Dane rezerwacji</h2>
                <span id="previous" role="button" tabindex="0" aria-label="Poprzednia wycieczka">&laquo;</span>
                <img src="1.jpg" alt="wycieczka" id="tripImage">
                <span id="next" role="button" tabindex="0" aria-label="Następna wycieczka">&raquo;</span>
                <h4 id="tripName">Barcelona</h4>
                <fieldset id="bookingFields" disabled>
                    <p>
                        <label for="people">Liczba osób:</label>
                        <input type="number" id="people" name="people" min="1" max="4" value="2">
                    </p>
                </fieldset>
            </section>
            <aside>
            </aside>
        </main>
        <footer>
        </footer>
    </body>
    </html>
    Podgląd strony po tym kroku (rezerwacja.html)
  32. Krok 32: Lista miejsc wylotu, nieaktywna

    ok. 1 min
    Polecenie z arkusza

    Poniżej etykieta „Miejsce wylotu:” oraz pole listy rozwijanej z nią powiązane. Opcje listy: „Warszawa”, „Kraków”, „Wrocław”. Pole listy jest nieaktywne

    <select> z trzema opcjami w tej samej wyłączonej grupie - jest nieaktywny bez własnego atrybutu.

    rezerwacja.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>Biuro turystyczne</title>
        <link rel="stylesheet" href="styl.css">
        <script src="skrypt.js" defer></script>
    </head>
    <body>
        <header>
            <h1>Rezerwacja wycieczek</h1>
        </header>
        <main>
            <nav>
                <button type="button" id="participant" aria-pressed="true">Uczestnik</button>
                <button type="button" id="booking" aria-pressed="false">Rezerwacja</button>
            </nav>
            <section id="client">
                <h2>Dane Klienta</h2>
                <img src="osoba.png" alt="Klient">
                <fieldset id="clientFields">
                    <p>
                        <label for="firstName">Imię</label>
                        <input type="text" id="firstName" name="firstName">
                    </p>
                    <p>
                        <label for="lastName">Nazwisko</label>
                        <input type="text" id="lastName" name="lastName">
                    </p>
                </fieldset>
                <p><a href="wycieczki.html">Do strony głównej</a></p>
            </section>
            <section id="trip">
                <h2>Dane rezerwacji</h2>
                <span id="previous" role="button" tabindex="0" aria-label="Poprzednia wycieczka">&laquo;</span>
                <img src="1.jpg" alt="wycieczka" id="tripImage">
                <span id="next" role="button" tabindex="0" aria-label="Następna wycieczka">&raquo;</span>
                <h4 id="tripName">Barcelona</h4>
                <fieldset id="bookingFields" disabled>
                    <p>
                        <label for="people">Liczba osób:</label>
                        <input type="number" id="people" name="people" min="1" max="4" value="2">
                    </p>
                    <p>
                        <label for="departure">Miejsce wylotu:</label>
                        <select id="departure" name="departure">
                            <option>Warszawa</option>
                            <option>Kraków</option>
                            <option>Wrocław</option>
                        </select>
                    </p>
                </fieldset>
            </section>
            <aside>
            </aside>
        </main>
        <footer>
        </footer>
    </body>
    </html>
    Podgląd strony po tym kroku (rezerwacja.html)
  33. Krok 33: Trzy pola opcji wyżywienia

    Kryteria: ,

    R.3.5 Pola z drugiego bloku sekcji są nieaktywne

    R.3.6 Pole z liczbą osób jest typu number, przyjmuje wartości od 1 do 4, wyświetla wartość 2 (value lub placeholder) W danym momencie może być wybrane tylko jedno pole opcji, domyślnie pierwsze pole opcji jest zaznaczone

    ok. 2 min
    Polecenie z arkusza

    Poniżej zgrupowane trzy pola opcji (w jednym momencie możliwe jest wybranie tylko jednego pola), powiązane z etykietami „Śniadania”, „Śniadania i obiadokolacje”, „All inclusive”. Wszystkie pola są nieaktywne, pierwsze jest zaznaczone

    Wspólne name="food" łączy pola opcji w grupę - zaznaczenie jednego odznacza pozostałe. value to numer wyżywienia, taki sam jak w polu wyzywienie tabeli rezerwacje (1, 2, 3) - gdy formularz zacznie wysyłać dane, wartość pasuje od razu do bazy. Pierwsze pole ma checked. Wszystkie są nieaktywne, bo stoją w wyłączonej grupie.

    Typowe błędy

    Różne name przy polach opcji pozwalają zaznaczyć wszystkie trzy - klucz wymaga, by można było wybrać tylko jedno (R.3.6).

    rezerwacja.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>Biuro turystyczne</title>
        <link rel="stylesheet" href="styl.css">
        <script src="skrypt.js" defer></script>
    </head>
    <body>
        <header>
            <h1>Rezerwacja wycieczek</h1>
        </header>
        <main>
            <nav>
                <button type="button" id="participant" aria-pressed="true">Uczestnik</button>
                <button type="button" id="booking" aria-pressed="false">Rezerwacja</button>
            </nav>
            <section id="client">
                <h2>Dane Klienta</h2>
                <img src="osoba.png" alt="Klient">
                <fieldset id="clientFields">
                    <p>
                        <label for="firstName">Imię</label>
                        <input type="text" id="firstName" name="firstName">
                    </p>
                    <p>
                        <label for="lastName">Nazwisko</label>
                        <input type="text" id="lastName" name="lastName">
                    </p>
                </fieldset>
                <p><a href="wycieczki.html">Do strony głównej</a></p>
            </section>
            <section id="trip">
                <h2>Dane rezerwacji</h2>
                <span id="previous" role="button" tabindex="0" aria-label="Poprzednia wycieczka">&laquo;</span>
                <img src="1.jpg" alt="wycieczka" id="tripImage">
                <span id="next" role="button" tabindex="0" aria-label="Następna wycieczka">&raquo;</span>
                <h4 id="tripName">Barcelona</h4>
                <fieldset id="bookingFields" disabled>
                    <p>
                        <label for="people">Liczba osób:</label>
                        <input type="number" id="people" name="people" min="1" max="4" value="2">
                    </p>
                    <p>
                        <label for="departure">Miejsce wylotu:</label>
                        <select id="departure" name="departure">
                            <option>Warszawa</option>
                            <option>Kraków</option>
                            <option>Wrocław</option>
                        </select>
                    </p>
                    <p>
                        <input type="radio" name="food" id="food1" value="1" checked>
                        <label for="food1">Śniadania</label>
                        <input type="radio" name="food" id="food2" value="2">
                        <label for="food2">Śniadania i obiadokolacje</label>
                        <input type="radio" name="food" id="food3" value="3">
                        <label for="food3">All inclusive</label>
                    </p>
                </fieldset>
            </section>
            <aside>
            </aside>
        </main>
        <footer>
        </footer>
    </body>
    </html>
    Podgląd strony po tym kroku (rezerwacja.html)
  34. Krok 34: Przycisk „Sprawdź cenę”

    Kryteria:

    R.2.8 W bloku sekcji pierwszej znajdują się dwa pola edycyjne W bloku sekcji drugiej - pole edycyjne, lista rozwijana, trzy pola opcji i przycisk. Co najmniej jedno pole ma powiązaną etykietę (label z for i id lub zagnieżdżenie kontrolki w label)

    ok. 1 min
    Polecenie z arkusza

    Poniżej przycisk „Sprawdź cenę”. Przycisk nie ma funkcjonalności

    Przycisk stoi za grupą pól: arkusz nie każe go wyłączać, a w wyłączonym fieldset też stałby się nieaktywny. type="button" - nic nie wysyła.

    rezerwacja.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>Biuro turystyczne</title>
        <link rel="stylesheet" href="styl.css">
        <script src="skrypt.js" defer></script>
    </head>
    <body>
        <header>
            <h1>Rezerwacja wycieczek</h1>
        </header>
        <main>
            <nav>
                <button type="button" id="participant" aria-pressed="true">Uczestnik</button>
                <button type="button" id="booking" aria-pressed="false">Rezerwacja</button>
            </nav>
            <section id="client">
                <h2>Dane Klienta</h2>
                <img src="osoba.png" alt="Klient">
                <fieldset id="clientFields">
                    <p>
                        <label for="firstName">Imię</label>
                        <input type="text" id="firstName" name="firstName">
                    </p>
                    <p>
                        <label for="lastName">Nazwisko</label>
                        <input type="text" id="lastName" name="lastName">
                    </p>
                </fieldset>
                <p><a href="wycieczki.html">Do strony głównej</a></p>
            </section>
            <section id="trip">
                <h2>Dane rezerwacji</h2>
                <span id="previous" role="button" tabindex="0" aria-label="Poprzednia wycieczka">&laquo;</span>
                <img src="1.jpg" alt="wycieczka" id="tripImage">
                <span id="next" role="button" tabindex="0" aria-label="Następna wycieczka">&raquo;</span>
                <h4 id="tripName">Barcelona</h4>
                <fieldset id="bookingFields" disabled>
                    <p>
                        <label for="people">Liczba osób:</label>
                        <input type="number" id="people" name="people" min="1" max="4" value="2">
                    </p>
                    <p>
                        <label for="departure">Miejsce wylotu:</label>
                        <select id="departure" name="departure">
                            <option>Warszawa</option>
                            <option>Kraków</option>
                            <option>Wrocław</option>
                        </select>
                    </p>
                    <p>
                        <input type="radio" name="food" id="food1" value="1" checked>
                        <label for="food1">Śniadania</label>
                        <input type="radio" name="food" id="food2" value="2">
                        <label for="food2">Śniadania i obiadokolacje</label>
                        <input type="radio" name="food" id="food3" value="3">
                        <label for="food3">All inclusive</label>
                    </p>
                </fieldset>
                <button type="button">Sprawdź cenę</button>
            </section>
            <aside>
            </aside>
        </main>
        <footer>
        </footer>
    </body>
    </html>
    Podgląd strony po tym kroku (rezerwacja.html)
  35. Krok 35: Stopka z numerem zdającego w em

    Kryteria: ,

    R.2.5 W bloku nagłówkowym znajduje się <h1>: "Rezerwacja wycieczek" w blokach sekcji <h2>: "Dane Klienta" i "Dane rezerwacji" oraz <h4>: "Barcelona", w stopce <p> z numerem zdającego

    R.2.10 Numer zdającego jest w znaczniku <em>

    ok. 1 min
    Polecenie z arkusza

    Zawartość bloku stopki: paragraf o treści „Autor strony: ”, dalej wstawiony numer zdającego. Numer zdającego jest zapisany w semantycznym znaczniku oznaczającym tekst uwypuklony, formatowany domyślnie jako pochylony

    Tekst uwypuklony to <em>. Na egzaminie wpisujesz w nim swój numer zdającego (PESEL) - tutaj stoi imię i nazwisko autora rozwiązania.

    rezerwacja.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>Biuro turystyczne</title>
        <link rel="stylesheet" href="styl.css">
        <script src="skrypt.js" defer></script>
    </head>
    <body>
        <header>
            <h1>Rezerwacja wycieczek</h1>
        </header>
        <main>
            <nav>
                <button type="button" id="participant" aria-pressed="true">Uczestnik</button>
                <button type="button" id="booking" aria-pressed="false">Rezerwacja</button>
            </nav>
            <section id="client">
                <h2>Dane Klienta</h2>
                <img src="osoba.png" alt="Klient">
                <fieldset id="clientFields">
                    <p>
                        <label for="firstName">Imię</label>
                        <input type="text" id="firstName" name="firstName">
                    </p>
                    <p>
                        <label for="lastName">Nazwisko</label>
                        <input type="text" id="lastName" name="lastName">
                    </p>
                </fieldset>
                <p><a href="wycieczki.html">Do strony głównej</a></p>
            </section>
            <section id="trip">
                <h2>Dane rezerwacji</h2>
                <span id="previous" role="button" tabindex="0" aria-label="Poprzednia wycieczka">&laquo;</span>
                <img src="1.jpg" alt="wycieczka" id="tripImage">
                <span id="next" role="button" tabindex="0" aria-label="Następna wycieczka">&raquo;</span>
                <h4 id="tripName">Barcelona</h4>
                <fieldset id="bookingFields" disabled>
                    <p>
                        <label for="people">Liczba osób:</label>
                        <input type="number" id="people" name="people" min="1" max="4" value="2">
                    </p>
                    <p>
                        <label for="departure">Miejsce wylotu:</label>
                        <select id="departure" name="departure">
                            <option>Warszawa</option>
                            <option>Kraków</option>
                            <option>Wrocław</option>
                        </select>
                    </p>
                    <p>
                        <input type="radio" name="food" id="food1" value="1" checked>
                        <label for="food1">Śniadania</label>
                        <input type="radio" name="food" id="food2" value="2">
                        <label for="food2">Śniadania i obiadokolacje</label>
                        <input type="radio" name="food" id="food3" value="3">
                        <label for="food3">All inclusive</label>
                    </p>
                </fieldset>
                <button type="button">Sprawdź cenę</button>
            </section>
            <aside>
            </aside>
        </main>
        <footer>
            <p>Autor strony: <em>Bartosz Bryniarski</em></p>
        </footer>
    </body>
    </html>
    Podgląd strony po tym kroku (rezerwacja.html)
  36. Etap 3: Styl CSS

    Kroki 36-45 · po tym etapie masz 31 z 39 kryteriów (pkt)

    Krok 36: Zmienne z kolorami i krój czcionki

    Kryteria:

    R.4.1 domyślne formatowanie selektorów (*): krój czcionki "Bookman Old Style"

    ok. 2 min
    Polecenie z arkusza

    Domyślnie, dla wszystkich selektorów: krój czcionki „Bookman Old Style”

    Na początku zmienne CSS (custom properties) w :root: kolor SkyBlue występuje w arkuszu trzy razy, a zaokrąglenie 15 px dwa razy - zmiana w jednym miejscu zmieni wszystkie. Odwołanie to var(--sky). Potem selektor * z krojem; nazwa ze spacjami w cudzysłowie.

    styl.css CSS nowy plik
    :root {
        --sky: SkyBlue;
        --dodger: DodgerBlue;
        --radius: 15px;
    }
    
    * {
        font-family: "Bookman Old Style";
    }
    Podgląd strony po tym kroku (rezerwacja.html)
  37. Krok 37: Ciało strony

    ok. 1 min
    Polecenie z arkusza

    Dla ciała strony: kolor tła AliceBlue

    Selektor body.

    styl.css CSS zmienione: 4 linie
    :root {
        --sky: SkyBlue;
        --dodger: DodgerBlue;
        --radius: 15px;
    }
    
    * {
        font-family: "Bookman Old Style";
    }
    
    body {
        background-color: AliceBlue;
    }
    Podgląd strony po tym kroku (rezerwacja.html)
  38. Krok 38: Nagłówek i stopka

    ok. 1 min
    Polecenie z arkusza

    Dla bloku nagłówkowego i stopki: kolor tła SkyBlue, wyrównanie tekstu do środka, marginesy wewnętrzne 1 px, zaokrąglenie rogów 15 px

    Jedna reguła dla dwóch selektorów, kolor i promień ze zmiennych.

    styl.css CSS zmienione: 8 linii
    :root {
        --sky: SkyBlue;
        --dodger: DodgerBlue;
        --radius: 15px;
    }
    
    * {
        font-family: "Bookman Old Style";
    }
    
    body {
        background-color: AliceBlue;
    }
    
    header,
    footer {
        background-color: var(--sky);
        text-align: center;
        padding: 1px;
        border-radius: var(--radius);
    }
    Podgląd strony po tym kroku (rezerwacja.html)
  39. Krok 39: Siatka bloku głównego zgodna z ilustracją 3

    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 albo position; żaden blok nie zachodzi na inne bloki - sprawdzić w inspektorze)

    R.4.5 wyrównanie tekstu: blok nagłówkowy, nawigacyjny, stopka - do środka

    ok. 4 min
    Polecenie z arkusza

    Dla bloku nawigacyjnego: szerokość 90%, wyrównanie tekstu do środka

    Blok główny dostaje display: grid z trzema kolumnami 45% 45% 10% - to szerokości z arkusza: sekcja, sekcja, blok boczny. grid-template-areas rysuje układ z ilustracji 3 słowami: w pierwszym wierszu nawigacja zajmuje dwie kolumny (45% + 45% = 90%), w drugim stoją obie sekcje, a blok boczny zajmuje trzecią kolumnę w obu wierszach. Każdy blok dostaje swój obszar przez grid-area.

    Klucz R.4.3 dopuszcza szerokości w grid-template-columns, a R.3.1 - układ na grid. Bloki nie mają ustawianych szerokości, więc nic nie może się nie zmieścić, a stopka stoi pod blokiem głównym bez clear. text-align: center w nawigacji wyśrodkowuje przyciski.

    Typowe błędy

    Nazwy w grid-template-areas muszą tworzyć prostokąty - obszar aside w obu wierszach w tej samej kolumnie jest poprawny, ale „rozjechany” (np. nav w dwóch nieprzylegających miejscach) unieważnia całą właściwość i bloki wracają jeden pod drugi.

    styl.css CSS zmienione: 25 linii
    :root {
        --sky: SkyBlue;
        --dodger: DodgerBlue;
        --radius: 15px;
    }
    
    * {
        font-family: "Bookman Old Style";
    }
    
    body {
        background-color: AliceBlue;
    }
    
    header,
    footer {
        background-color: var(--sky);
        text-align: center;
        padding: 1px;
        border-radius: var(--radius);
    }
    
    main {
        display: grid;
        grid-template-columns: 45% 45% 10%;
        grid-template-areas:
            "nav    nav  aside"
            "client trip aside";
    }
    
    nav {
        grid-area: nav;
        text-align: center;
    }
    
    #client {
        grid-area: client;
    }
    
    #trip {
        grid-area: trip;
    }
    
    aside {
        grid-area: aside;
    }
    Podgląd strony po tym kroku (rezerwacja.html)
  40. Krok 40: Bloki sekcji: wysokość

    ok. 1 min
    Polecenie z arkusza

    Dla obu bloków sekcji: szerokość 45%, wysokość 640 px

    Szerokość 45% wynika już z kolumn siatki, więc zostaje sama wysokość. Selektor section działa na obie sekcje.

    styl.css CSS zmienione: 4 linie
    :root {
        --sky: SkyBlue;
        --dodger: DodgerBlue;
        --radius: 15px;
    }
    
    * {
        font-family: "Bookman Old Style";
    }
    
    body {
        background-color: AliceBlue;
    }
    
    header,
    footer {
        background-color: var(--sky);
        text-align: center;
        padding: 1px;
        border-radius: var(--radius);
    }
    
    main {
        display: grid;
        grid-template-columns: 45% 45% 10%;
        grid-template-areas:
            "nav    nav  aside"
            "client trip aside";
    }
    
    nav {
        grid-area: nav;
        text-align: center;
    }
    
    #client {
        grid-area: client;
    }
    
    #trip {
        grid-area: trip;
    }
    
    aside {
        grid-area: aside;
    }
    
    section {
        height: 640px;
    }
    Podgląd strony po tym kroku (rezerwacja.html)
  41. Krok 41: Blok boczny: wysokość i ukryta zawartość wystająca

    Kryteria: ,

    R.4.4 wysokość: oba bloki sekcji - 640px, blok boczny - 700px

    R.4.10 w przypadku przepełnienia (overflow) bloku bocznego zawartość wystająca poza blok jest ukryta

    ok. 2 min
    Polecenie z arkusza

    Dla bloku bocznego: szerokość 10%, wysokość 700 px, w przypadku przepełnienia bloku zawartość wystająca poza blok jest ukryta

    Szerokość 10% to trzecia kolumna siatki. Dopisane do istniejącej reguły aside: wysokość i overflow: hidden - dziewięć zdjęć jest wyższych niż 700 px, nadmiar zostaje obcięty bez paska przewijania.

    styl.css CSS zmienione: 2 linie
    :root {
        --sky: SkyBlue;
        --dodger: DodgerBlue;
        --radius: 15px;
    }
    
    * {
        font-family: "Bookman Old Style";
    }
    
    body {
        background-color: AliceBlue;
    }
    
    header,
    footer {
        background-color: var(--sky);
        text-align: center;
        padding: 1px;
        border-radius: var(--radius);
    }
    
    main {
        display: grid;
        grid-template-columns: 45% 45% 10%;
        grid-template-areas:
            "nav    nav  aside"
            "client trip aside";
    }
    
    nav {
        grid-area: nav;
        text-align: center;
    }
    
    #client {
        grid-area: client;
    }
    
    #trip {
        grid-area: trip;
    }
    
    aside {
        grid-area: aside;
        height: 700px;
        overflow: hidden;
    }
    
    section {
        height: 640px;
    }
    Podgląd strony po tym kroku (rezerwacja.html)
  42. Krok 42: Zdjęcia w bloku bocznym

    ok. 1 min
    Polecenie z arkusza

    Jedynie dla obrazów w bloku bocznym: szerokość 100%

    Selektor potomka aside img - osoba.png i zdjęcie wycieczki zostają w swoim rozmiarze.

    styl.css CSS zmienione: 4 linie
    :root {
        --sky: SkyBlue;
        --dodger: DodgerBlue;
        --radius: 15px;
    }
    
    * {
        font-family: "Bookman Old Style";
    }
    
    body {
        background-color: AliceBlue;
    }
    
    header,
    footer {
        background-color: var(--sky);
        text-align: center;
        padding: 1px;
        border-radius: var(--radius);
    }
    
    main {
        display: grid;
        grid-template-columns: 45% 45% 10%;
        grid-template-areas:
            "nav    nav  aside"
            "client trip aside";
    }
    
    nav {
        grid-area: nav;
        text-align: center;
    }
    
    #client {
        grid-area: client;
    }
    
    #trip {
        grid-area: trip;
    }
    
    aside {
        grid-area: aside;
        height: 700px;
        overflow: hidden;
    }
    
    section {
        height: 640px;
    }
    
    aside img {
        width: 100%;
    }
    Podgląd strony po tym kroku (rezerwacja.html)
  43. Krok 43: Znaki « i »

    Kryteria:

    R.4.9 styl elementu span: rozmiar czcionki - 200%, kursor pointer (np. cursor:pointer )

    ok. 1 min
    Polecenie z arkusza

    Dla znacznika span: rozmiar czcionki 200%, kursor pointer

    cursor: pointer pokazuje rączkę - podpowiedź, że da się kliknąć.

    styl.css CSS zmienione: 5 linii
    :root {
        --sky: SkyBlue;
        --dodger: DodgerBlue;
        --radius: 15px;
    }
    
    * {
        font-family: "Bookman Old Style";
    }
    
    body {
        background-color: AliceBlue;
    }
    
    header,
    footer {
        background-color: var(--sky);
        text-align: center;
        padding: 1px;
        border-radius: var(--radius);
    }
    
    main {
        display: grid;
        grid-template-columns: 45% 45% 10%;
        grid-template-areas:
            "nav    nav  aside"
            "client trip aside";
    }
    
    nav {
        grid-area: nav;
        text-align: center;
    }
    
    #client {
        grid-area: client;
    }
    
    #trip {
        grid-area: trip;
    }
    
    aside {
        grid-area: aside;
        height: 700px;
        overflow: hidden;
    }
    
    section {
        height: 640px;
    }
    
    aside img {
        width: 100%;
    }
    
    span {
        font-size: 200%;
        cursor: pointer;
    }
    Podgląd strony po tym kroku (rezerwacja.html)
  44. Krok 44: Przyciski i przycisk wciśnięty

    Kryteria: , , ,

    R.4.2 kolor tła: ciało strony - AliceBlue, blok nagłówkowy, stopki, przycisków - SkyBlue

    R.4.3 szerokość: blok nawigacyjny - 90%, oba bloki sekcji - 45%, blok boczny - 10% (width lub grid-template- columns lub flex ), obraz jedynie w bloku bocznym - 100%, przyciski 45%

    R.4.6 marginesy wewnętrzne (padding ): blok nagłówkowy, stopka - 1px, przyciski - górny i dolny - 15px, pozostałe - 40px

    R.4.8 zaokrąglenie rogów (border-radius) : blok nagłówkowy, stopka, przyciski - 15px

    ok. 3 min
    Polecenie z arkusza

    Dla przycisku: kolor tła SkyBlue, szerokość 45%, zaokrąglenie rogów 15 px, marginesy wewnętrzne górny i dolny 15 px, pozostałe 40 px, marginesy zewnętrzne górny i dolny 3 px, pozostałe 2%

    Skróty z dwiema wartościami (góra i dół, potem boki): padding: 15px 40px, margin: 3px 2%. Druga reguła to selektor atrybutu button[aria-pressed="true"]: wciśnięty przycisk ma tło DodgerBlue. Dzięki temu „Uczestnik” jest ciemnoniebieski od pierwszego wczytania (stan początkowy z arkusza), a skrypt będzie tylko przełączał aria-pressed - kolor zawsze wynika ze stanu i nie da się ich rozkleić.

    styl.css CSS zmienione: 12 linii
    :root {
        --sky: SkyBlue;
        --dodger: DodgerBlue;
        --radius: 15px;
    }
    
    * {
        font-family: "Bookman Old Style";
    }
    
    body {
        background-color: AliceBlue;
    }
    
    header,
    footer {
        background-color: var(--sky);
        text-align: center;
        padding: 1px;
        border-radius: var(--radius);
    }
    
    main {
        display: grid;
        grid-template-columns: 45% 45% 10%;
        grid-template-areas:
            "nav    nav  aside"
            "client trip aside";
    }
    
    nav {
        grid-area: nav;
        text-align: center;
    }
    
    #client {
        grid-area: client;
    }
    
    #trip {
        grid-area: trip;
    }
    
    aside {
        grid-area: aside;
        height: 700px;
        overflow: hidden;
    }
    
    section {
        height: 640px;
    }
    
    aside img {
        width: 100%;
    }
    
    span {
        font-size: 200%;
        cursor: pointer;
    }
    
    button {
        background-color: var(--sky);
        width: 45%;
        border-radius: var(--radius);
        padding: 15px 40px;
        margin: 3px 2%;
    }
    
    button[aria-pressed="true"] {
        background-color: var(--dodger);
    }
    Podgląd strony po tym kroku (rezerwacja.html)
  45. Krok 45: Kontrolki formularza i grupy pól

    Kryteria:

    R.4.7 marginesy zewnętrzne (margin) : kontrolki na stronie - 3px, przyciski - górny i dolny - 3px, pozostałe - 2%

    ok. 1 min
    Polecenie z arkusza

    Dla kontrolek (pola edycyjne, lista rozwijana, pola opcji): marginesy zewnętrzne 3 px

    Pola i pola opcji to <input>, lista to <select>. Druga reguła zdejmuje ramkę i odstępy, które przeglądarka domyślnie rysuje wokół fieldset - grupy mają działać, a nie być widoczne.

    styl.css CSS zmienione: 11 linii
    :root {
        --sky: SkyBlue;
        --dodger: DodgerBlue;
        --radius: 15px;
    }
    
    * {
        font-family: "Bookman Old Style";
    }
    
    body {
        background-color: AliceBlue;
    }
    
    header,
    footer {
        background-color: var(--sky);
        text-align: center;
        padding: 1px;
        border-radius: var(--radius);
    }
    
    main {
        display: grid;
        grid-template-columns: 45% 45% 10%;
        grid-template-areas:
            "nav    nav  aside"
            "client trip aside";
    }
    
    nav {
        grid-area: nav;
        text-align: center;
    }
    
    #client {
        grid-area: client;
    }
    
    #trip {
        grid-area: trip;
    }
    
    aside {
        grid-area: aside;
        height: 700px;
        overflow: hidden;
    }
    
    section {
        height: 640px;
    }
    
    aside img {
        width: 100%;
    }
    
    span {
        font-size: 200%;
        cursor: pointer;
    }
    
    button {
        background-color: var(--sky);
        width: 45%;
        border-radius: var(--radius);
        padding: 15px 40px;
        margin: 3px 2%;
    }
    
    button[aria-pressed="true"] {
        background-color: var(--dodger);
    }
    
    input,
    select {
        margin: 3px;
    }
    
    fieldset {
        border: none;
        margin: 0;
        padding: 0;
    }
    Podgląd strony po tym kroku (rezerwacja.html)
  46. Etap 4: JavaScript

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

    Krok 46: Stan początkowy i funkcja przełączająca: kolory przycisków

    Kryteria: , ,

    R.5.1 Skrypt w języku JavaScript zawiera przynajmniej jedną poprawną składniowo instrukcję wynikającą z treści zadania

    R.5.2 Zastosowana przynajmniej jedna zmienna lub funkcja oraz nazewnictwo jest w języku polskim lub angielskim i odzwierciedla cel zastosowania

    R.5.3 Odwołania do istniejących elementów DOM (np. document.getElementById) są zapisane zgodnie ze składnią

    ok. 4 min
    Polecenie z arkusza

    Funkcja uczestnika wywoływana po wciśnięciu przycisku „Uczestnik” (ilustracja 4): Zmieniane są kolory tła przycisków: „Uczestnik” na DodgerBlue, „Rezerwacja” na SkyBlue

    Stan początkowy z arkusza („Uczestnik” na DodgerBlue) jest już spełniony: aria-pressed="true" w HTML i reguła w CSS.

    Funkcje uczestnika i rezerwacji różnią się tylko tym, która strona jest aktywna, więc w skrypcie jest jedna funkcja showPanel(isParticipant) z parametrem logicznym. Na górze cztery stałe z elementami strony (document.querySelector z selektorem CSS) - wyszukane raz, używane przy każdym kliknięciu. setAttribute('aria-pressed', isParticipant) zapisuje "true" przy przycisku aktywnym i "false" przy drugim (! odwraca wartość); kolor dobiera CSS.

    skrypt.js JavaScript zmienione: 10 linii
    const trips = [
        { image: '1.jpg', place: 'Barcelona' },
        { image: '2.jpg', place: 'Rzym' },
        { image: '3.jpg', place: 'Londyn' },
    ];
    
    const gallery = document.querySelector('aside');
    for (let i = 0; i < 9; i++) {
        const photo = document.createElement('img');
        photo.src = trips[i % trips.length].image;
        photo.alt = 'wycieczki';
        gallery.append(photo);
    }
    
    const participantButton = document.querySelector('#participant');
    const bookingButton = document.querySelector('#booking');
    const clientFields = document.querySelector('#clientFields');
    const bookingFields = document.querySelector('#bookingFields');
    
    function showPanel(isParticipant) {
        participantButton.setAttribute('aria-pressed', isParticipant);
        bookingButton.setAttribute('aria-pressed', !isParticipant);
    }
    Podgląd strony po tym kroku (rezerwacja.html)
  47. Krok 47: Funkcja: pola klienta aktywne

    ok. 1 min
    Polecenie z arkusza

    Kontrolki pierwszego bloku sekcji są aktywowane

    clientFields.disabled = !isParticipant - dla uczestnika false, czyli grupa i wszystkie jej pola są aktywne.

    skrypt.js JavaScript zmienione: 1 linia
    const trips = [
        { image: '1.jpg', place: 'Barcelona' },
        { image: '2.jpg', place: 'Rzym' },
        { image: '3.jpg', place: 'Londyn' },
    ];
    
    const gallery = document.querySelector('aside');
    for (let i = 0; i < 9; i++) {
        const photo = document.createElement('img');
        photo.src = trips[i % trips.length].image;
        photo.alt = 'wycieczki';
        gallery.append(photo);
    }
    
    const participantButton = document.querySelector('#participant');
    const bookingButton = document.querySelector('#booking');
    const clientFields = document.querySelector('#clientFields');
    const bookingFields = document.querySelector('#bookingFields');
    
    function showPanel(isParticipant) {
        participantButton.setAttribute('aria-pressed', isParticipant);
        bookingButton.setAttribute('aria-pressed', !isParticipant);
        clientFields.disabled = !isParticipant;
    }
    Podgląd strony po tym kroku (rezerwacja.html)
  48. Krok 48: Funkcja: pola rezerwacji nieaktywne

    ok. 1 min
    Polecenie z arkusza

    Kontrolki drugiego bloku sekcji są nieaktywne

    bookingFields.disabled = isParticipant - jedna linia zamiast wyłączania pięciu kontrolek po kolei.

    skrypt.js JavaScript zmienione: 1 linia
    const trips = [
        { image: '1.jpg', place: 'Barcelona' },
        { image: '2.jpg', place: 'Rzym' },
        { image: '3.jpg', place: 'Londyn' },
    ];
    
    const gallery = document.querySelector('aside');
    for (let i = 0; i < 9; i++) {
        const photo = document.createElement('img');
        photo.src = trips[i % trips.length].image;
        photo.alt = 'wycieczki';
        gallery.append(photo);
    }
    
    const participantButton = document.querySelector('#participant');
    const bookingButton = document.querySelector('#booking');
    const clientFields = document.querySelector('#clientFields');
    const bookingFields = document.querySelector('#bookingFields');
    
    function showPanel(isParticipant) {
        participantButton.setAttribute('aria-pressed', isParticipant);
        bookingButton.setAttribute('aria-pressed', !isParticipant);
        clientFields.disabled = !isParticipant;
        bookingFields.disabled = isParticipant;
    }
    Podgląd strony po tym kroku (rezerwacja.html)
  49. Krok 49: Przycisk „Uczestnik” wywołuje funkcję

    Kryteria:

    R.3.3 Dla przynajmniej jednego przycisku zdefiniowane jest zdarzenie kliknięcia, które uruchamia funkcję skryptu

    ok. 2 min
    Polecenie z arkusza

    Przycisk o treści „Uczestnik”, którego kliknięcie powoduje wywołanie funkcji uczestnika

    addEventListener('click', ...) przypina obsługę kliknięcia w skrypcie - HTML zostaje bez atrybutów onclick. Funkcja strzałkowa () => showPanel(true) jest „funkcją uczestnika”: wywołuje wspólną funkcję z parametrem true. Samo showPanel(true) (bez strzałki) wywołałoby funkcję od razu przy wczytaniu.

    skrypt.js JavaScript zmienione: 2 linie
    const trips = [
        { image: '1.jpg', place: 'Barcelona' },
        { image: '2.jpg', place: 'Rzym' },
        { image: '3.jpg', place: 'Londyn' },
    ];
    
    const gallery = document.querySelector('aside');
    for (let i = 0; i < 9; i++) {
        const photo = document.createElement('img');
        photo.src = trips[i % trips.length].image;
        photo.alt = 'wycieczki';
        gallery.append(photo);
    }
    
    const participantButton = document.querySelector('#participant');
    const bookingButton = document.querySelector('#booking');
    const clientFields = document.querySelector('#clientFields');
    const bookingFields = document.querySelector('#bookingFields');
    
    function showPanel(isParticipant) {
        participantButton.setAttribute('aria-pressed', isParticipant);
        bookingButton.setAttribute('aria-pressed', !isParticipant);
        clientFields.disabled = !isParticipant;
        bookingFields.disabled = isParticipant;
    }
    
    participantButton.addEventListener('click', () => showPanel(true));
    Podgląd strony po tym kroku (rezerwacja.html)
  50. Krok 50: Przycisk „Rezerwacja”: ta sama funkcja z false

    Kryteria: ,

    R.5.4 Po kliknięciu w przycisk jego kolor tła jest ustawiany na DodgerBlue, drugiego przycisku na SkyBlue (sprawdzić dla obu przycisków )

    R.5.5 Po kliknięciu w każdy przycisk, kontrolki odpowiedniej sekcji są aktywne, w sąsiedniej sekcji są nieaktywne

    ok. 2 min
    Polecenie z arkusza

    Funkcja rezerwacji wywoływana po wciśnięciu przycisku „Rezerwacja” (ilustracja 5): Zmieniane są kolory tła przycisków: „Uczestnik” na SkyBlue, „Rezerwacja” na DodgerBlue. Kontrolki pierwszego bloku sekcji są nieaktywne. Kontrolki drugiego bloku sekcji są aktywowane

    Funkcja rezerwacji to () => showPanel(false): przy false każda z czterech linii daje ustawienie odwrotne - „Rezerwacja” wciśnięta, pola klienta wyłączone, pola rezerwacji włączone. Kliknij „Rezerwacja” w podglądzie: stan z ilustracji 5; „Uczestnik” przywraca stan z ilustracji 4. (W arkuszu stoi „na SkyBlue na” - drugie „na” to literówka.)

    skrypt.js JavaScript zmienione: 1 linia
    const trips = [
        { image: '1.jpg', place: 'Barcelona' },
        { image: '2.jpg', place: 'Rzym' },
        { image: '3.jpg', place: 'Londyn' },
    ];
    
    const gallery = document.querySelector('aside');
    for (let i = 0; i < 9; i++) {
        const photo = document.createElement('img');
        photo.src = trips[i % trips.length].image;
        photo.alt = 'wycieczki';
        gallery.append(photo);
    }
    
    const participantButton = document.querySelector('#participant');
    const bookingButton = document.querySelector('#booking');
    const clientFields = document.querySelector('#clientFields');
    const bookingFields = document.querySelector('#bookingFields');
    
    function showPanel(isParticipant) {
        participantButton.setAttribute('aria-pressed', isParticipant);
        bookingButton.setAttribute('aria-pressed', !isParticipant);
        clientFields.disabled = !isParticipant;
        bookingFields.disabled = isParticipant;
    }
    
    participantButton.addEventListener('click', () => showPanel(true));
    bookingButton.addEventListener('click', () => showPanel(false));
    Podgląd strony po tym kroku (rezerwacja.html)
  51. Krok 51: Przewijanie do tyłu z zawinięciem

    Kryteria:

    R.5.6 Po kliknięciu w znak przewijania w lewo obraz zmienia się na poprzedni, z zawinięciem: Barcelona -> Londyn - > Rzym -> Barcelona

    ok. 4 min
    Polecenie z arkusza

    Funkcja wywoływana po wciśnięciu znaku «: Zmienia się obraz oraz nazwa miejsca wycieczki na poprzedni, w przypadku pierwszego – na ostatni (obrazy są przewijane do tyłu). Kolejność: 1.jpg „Barcelona”, 3.jpg „Londyn”, 2.jpg „Rzym”, 1.jpg „Barcelona, ...

    Funkcja showTrip(step) przesuwa numer wycieczki o -1 albo 1. Zawinięcie robi reszta z dzielenia: dodane trips.length sprawia, że krok w tył z zera daje (0 - 1 + 3) % 3 = 2, czyli ostatnią wycieczkę, a nie liczbę ujemną. Zdjęcie i nazwę bierze z tej samej tablicy trips, która zbudowała blok boczny - pary zdjęcie - miejsce nie da się pomylić.

    Nazwę wpisuje textContent, nie innerHTML: to zwykły tekst, więc przeglądarka nie czyta go jako HTML. Kliknij « w podglądzie: Londyn, Rzym, Barcelona.

    skrypt.js JavaScript zmienione: 12 linii
    const trips = [
        { image: '1.jpg', place: 'Barcelona' },
        { image: '2.jpg', place: 'Rzym' },
        { image: '3.jpg', place: 'Londyn' },
    ];
    
    const gallery = document.querySelector('aside');
    for (let i = 0; i < 9; i++) {
        const photo = document.createElement('img');
        photo.src = trips[i % trips.length].image;
        photo.alt = 'wycieczki';
        gallery.append(photo);
    }
    
    const participantButton = document.querySelector('#participant');
    const bookingButton = document.querySelector('#booking');
    const clientFields = document.querySelector('#clientFields');
    const bookingFields = document.querySelector('#bookingFields');
    
    function showPanel(isParticipant) {
        participantButton.setAttribute('aria-pressed', isParticipant);
        bookingButton.setAttribute('aria-pressed', !isParticipant);
        clientFields.disabled = !isParticipant;
        bookingFields.disabled = isParticipant;
    }
    
    participantButton.addEventListener('click', () => showPanel(true));
    bookingButton.addEventListener('click', () => showPanel(false));
    
    const tripImage = document.querySelector('#tripImage');
    const tripName = document.querySelector('#tripName');
    let currentTrip = 0;
    
    function showTrip(step) {
        currentTrip = (currentTrip + step + trips.length) % trips.length;
        tripImage.src = trips[currentTrip].image;
        tripName.textContent = trips[currentTrip].place;
    }
    
    document.querySelector('#previous').addEventListener('click', () => showTrip(-1));
    Podgląd strony po tym kroku (rezerwacja.html)
  52. Krok 52: Przewijanie do przodu i obsługa klawiatury

    Kryteria:

    R.5.7 Po kliknięciu w znak przewijania w prawo obraz zmienia się na następny, z zawinięciem: Barcelona -> Rzym - > Londyn -> Barcelona

    ok. 3 min
    Polecenie z arkusza

    Funkcja wywoływana po wciśnięciu znaku »: Zmienia się obraz oraz nazwa miejsca wycieczki na następny, w przypadku ostatniego – na pierwszy. Kolejność: 1.jpg „Barcelona”, 2.jpg „Rzym”, 3.jpg „Londyn”, 1.jpg „Barcelona”, ...

    Ta sama funkcja z krokiem 1: Rzym, Londyn, Barcelona. Do tego klawiatura: znaki z role="button" obiecują zachowanie przycisku, a prawdziwy przycisk reaguje na Enter i spację. Pętla for...of po obu znakach dopina zdarzenie keydown, które wywołuje ich kliknięcie (arrow.click()); preventDefault() blokuje przewinięcie strony spacją. Klucz tego nie sprawdza - to dopełnienie atrybutów z kroku ze znakiem «.

    skrypt.js JavaScript zmienione: 10 linii
    const trips = [
        { image: '1.jpg', place: 'Barcelona' },
        { image: '2.jpg', place: 'Rzym' },
        { image: '3.jpg', place: 'Londyn' },
    ];
    
    const gallery = document.querySelector('aside');
    for (let i = 0; i < 9; i++) {
        const photo = document.createElement('img');
        photo.src = trips[i % trips.length].image;
        photo.alt = 'wycieczki';
        gallery.append(photo);
    }
    
    const participantButton = document.querySelector('#participant');
    const bookingButton = document.querySelector('#booking');
    const clientFields = document.querySelector('#clientFields');
    const bookingFields = document.querySelector('#bookingFields');
    
    function showPanel(isParticipant) {
        participantButton.setAttribute('aria-pressed', isParticipant);
        bookingButton.setAttribute('aria-pressed', !isParticipant);
        clientFields.disabled = !isParticipant;
        bookingFields.disabled = isParticipant;
    }
    
    participantButton.addEventListener('click', () => showPanel(true));
    bookingButton.addEventListener('click', () => showPanel(false));
    
    const tripImage = document.querySelector('#tripImage');
    const tripName = document.querySelector('#tripName');
    let currentTrip = 0;
    
    function showTrip(step) {
        currentTrip = (currentTrip + step + trips.length) % trips.length;
        tripImage.src = trips[currentTrip].image;
        tripName.textContent = trips[currentTrip].place;
    }
    
    document.querySelector('#previous').addEventListener('click', () => showTrip(-1));
    document.querySelector('#next').addEventListener('click', () => showTrip(1));
    
    for (const arrow of document.querySelectorAll('[role="button"]')) {
        arrow.addEventListener('keydown', (event) => {
            if (event.key === 'Enter' || event.key === ' ') {
                event.preventDefault();
                arrow.click();
            }
        });
    }
    Podgląd strony po tym kroku (rezerwacja.html)
  53. Etap 5: Porównanie z wersją podstawową

    Krok 53 · po tym etapie masz 39 z 39 kryteriów (pkt)

    Krok 53: Co jest inaczej i którą wersję wybrać na egzaminie

    ok. 2 min

    Obie wersje dają komplet 39 punktów. Różnice:

    wersja podstawowawersja nowoczesna
    układfloat + clear, blok boczny pierwszy w kodziesiatka z grid-template-areas, blok boczny na końcu
    szerokości blokówwidth każdego blokugrid-template-columns: 45% 45% 10%
    kolor przyciskustyle.backgroundColor ze skryptuaria-pressed + selektor atrybutu w CSS
    wyłączanie pól7 kontrolek osobno w każdej funkcjifieldset.disabled - jedna linia na sekcję
    skrypt<script> w HTML, onclick, dwie funkcjeskrypt.js z defer, addEventListener, jedna funkcja z parametrem
    przewijaniedwie tablice, if przy granicytablica obiektów, reszta z dzielenia
    zdjęcia bloku bocznego9 znaczników <img>pętla po tej samej tablicy

    Na egzaminie wersja podstawowa jest bezpieczniejsza, gdy brakuje czasu: każdy wymóg arkusza widać w kodzie dosłownie (disabled przy każdym polu, kolor w skrypcie), więc łatwo go sprawdzić wzrokiem. Wersja nowoczesna jest krótsza i trudniej w niej o niespójność - kolor i stan przycisku nie mogą się rozjechać, a nowe pole formularza nie wymaga zmian w skrypcie. Tu warto sprawdzić w przeglądarce, że pola drugiej sekcji naprawdę są wyszarzone, bo atrybutu disabled przy nich nie widać.

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

  54. Etap 6: Oddanie pracy

    Krok 54 · po tym etapie masz 39 z 39 kryteriów (pkt)

    Krok 54: 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. Umieść go w folderze z numerem zdającego. Nagraj płytę z rezultatami pracy.

    W folderze z numerem zdającego: 1.jpg, 2.jpg, 3.jpg, import.png, kw1.png … kw4.png, kwerendy.txt, osoba.png, przeglądarka.txt, rezerwacja.html, skrypt.js, styl.css, wycieczki.html. Bez skrypt.js nie działają przyciski ani przewijanie. Płytę sprawdź po nagraniu.

    przeglądarka.txt Tekst nowy plik
    Google Chrome

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

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.