Przejdź do głównej treści
INF.03-05-25.06-SGCzerwiec 2025 · 33 kroki · ok. 83 min · 41 kryteriów z klucza

Rozwiązanie krok po kroku: Salon fotograficzny online (Nowoczesny kod (grid))

To drugi sposób rozwiązania tego samego arkusza: salon fotograficzny z bazą fotografia, stroną zamowienie.html z galerią, formularzem zamówienia i koszykiem oraz podstronami regulamin.html i uslugi.html. Obie wersje spełniają wszystkie 41 kryteriów klucza - różni je sposób pisania kodu.

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

  • blok główny na siatce CSS z nazwanymi obszarami: sekcja zamówienia po lewej, nawigacja i koszyk po prawej, szerokości 35% / 50% w grid-template-columns - bez float i clear w układzie,
  • galeria z display: flow-root - blok sam obejmuje pływające obrazy, nie trzeba clear w kolejnym bloku; kolory z arkusza w zmiennych CSS,
  • skrypt od pierwszej linijki z nasłuchiwaczem (addEventListener), podpięty w <head> z defer,
  • nazwa pliku z File API (files[0].name) zamiast cięcia ścieżki C:\fakepath\…, ceny w obiekcie zamiast drabinki if, sprawdzenie, czy wybrano plik i podano liczbę kopii,
  • każde zamówienie w koszyku to osobny blok <article>, a tekst ustawiany przez textContent i szablony tekstowe.

Kroki nadal idą zdanie po zdaniu za treścią arkusza, a podgląd rośnie z każdym krokiem i działa jak strona na egzaminie. Zdjęcia i baza pochodzą z prawdziwego archiwum pliki6; zrzuty to wyniki zapytań wykonanych na tej bazie w MariaDB.

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

  1. Etap 1: Baza danych

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

    Krok 1: Baza fotografia, import i zrzut import.png

    Kryteria:

    R.1.1 Wykonany import bazy danych fotografia, czynność udokumentowana w pliku import.png

    ok. 4 min
    Polecenie z arkusza

    Utwórz bazę danych o nazwie fotografia, z zestawem polskich znaków (np. utf8_unicode_ci). Z rozpakowanego archiwum zaimportuj tabele z pliku fotografia.sql. Wykonaj zrzut ekranu po imporcie. Zapisz zrzut w formacie PNG pod nazwą import. Nie kadruj zrzutu.

    1. CREATE DATABASE fotografia CHARACTER SET utf8 COLLATE utf8_unicode_ci; (albo Nowa w phpMyAdmin).
    2. Import → fotografia.sql. Plik sam zawiera CREATE DATABASE IF NOT EXISTS fotografia i USE fotografia - baza już istnieje, więc pierwsza linia nic nie zmienia, a druga kieruje import do właściwej bazy. Powstają zamowienia (81 wierszy) i zdjecia (2 rodzaje papieru z ceną). Kluczem relacji jest tekstowe pole rodzaj.
    3. Zrzut całego ekranu z paskiem zadań jako import.png (w tym arkuszu wszystkie zrzuty w PNG).
    Typowe błędy

    Wartości rodzaj mają polskie znaki („błyszczący”) - przy złym zestawie znaków porównanie w zapytaniu 3 nie znajdzie żadnego wiersza.

  2. Krok 2: Zapytanie 1: duże zamówienia na papierze matowym

    Kryteria: ,

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

    R.1.3 Wykonane zapytanie 1 wybierające jedynie pola plik, nr_klienta, liczba_odbitek dla zamówień na papierze matowym, z liczbą odbitek większą od 100. W pliku z kwerendami lub na zrzucie istnieje zapis np. SELECT plik, nr_klienta, liczba_odbitek FROM zamowienia WHERE liczba_odbitek > 100 AND rodzaj = "matowy"; oraz wynik działania zapytania udokumentowany zrzutem, na którym widocznych jest dokładnie 6 wierszy z numerami klienta: 2, 6, 5, 8, 2, 3

    ok. 3 min
    Polecenie z arkusza

    Zapytanie 1: wybierające jedynie pola plik, nr_klienta, liczba_odbitek dla zamówień na papierze matowym, z liczbą odbitek większą od 100.

    Dwa warunki połączone AND, każdy w osobnej linii - czytelne i łatwe do zmiany. Napis 'matowy' w apostrofach (standard SQL), liczba bez. Wynik: 6 wierszy, klienci 2, 6, 5, 8, 2, 3. Zapytania w kwerendy.txt z komentarzem -- nad każdym; zrzut: kw1.png.

    Typowe błędy

    OR zamiast AND daje kilkadziesiąt wierszy zamiast sześciu.

    kwerendy.txt Tekst nowy plik
    -- Zapytanie 1
    SELECT plik, nr_klienta, liczba_odbitek
    FROM zamowienia
    WHERE rodzaj = 'matowy'
      AND liczba_odbitek > 100;
    Podgląd strony po tym kroku
    Podgląd strony po kroku 2
  3. Krok 3: Zapytanie 2: wartość zamówień klienta 3

    Kryteria:

    R.1.4 Wykonane zapytanie 2 wybierające dla klienta o identyfikatorze 3 wartości jego zamówień obliczane na podstawie liczby odbitek i ceny, alias „Do zapłaty”. W pliku z kwerendami lub na zrzucie istnieje zapis np. SELECT (liczba_odbitek*cena) AS "Do zapłaty" FROM zamowienia JOIN zdjecia USING(rodzaj) WHERE nr_klienta = 3; (możliwe też porównanie kluczy po ON lub WHERE; dodatkowo AS opcjonalne, dozwolone dowolne kolumny po SELECT) oraz wynik działania zapytania udokumentowany zrzutem, na którym widocznych jest dokładnie 7 wierszy: 157, 157, 16.50, 157, 7, 15.40, 219.8

    ok. 4 min
    Polecenie z arkusza

    Zapytanie 2: wybierające dla klienta o identyfikatorze 3 wartości jego zamówień, obliczane na podstawie liczby odbitek i ceny, nazwa kolumny (alias) „Do zapłaty”. Należy posłużyć się relacją.

    • FROM zamowienia AS z JOIN zdjecia AS f ON f.rodzaj = z.rodzaj - aliasy tabel i złączenie po polu rodzaj,
    • z.liczba_odbitek * f.cena - iloczyn kolumn z dwóch tabel,
    • AS `Do zapłaty` - alias ze spacją i polskimi literami. W MariaDB i MySQL nazwy (kolumn, aliasów) ujmuje się w odwrotne apostrofy; cudzysłów z klucza też zadziała, ale w tych bazach oznacza zwykle napis, a nie nazwę.

    Wynik: 7 wierszy - 157, 157, 16,50, 157, 7, 15,40, 219,80. Zrzut: kw2.png.

    Typowe błędy

    JOIN bez ON daje każdemu zamówieniu obie ceny - 14 wierszy zamiast 7.

    kwerendy.txt Tekst zmienione: 6 linii
    -- Zapytanie 1
    SELECT plik, nr_klienta, liczba_odbitek
    FROM zamowienia
    WHERE rodzaj = 'matowy'
      AND liczba_odbitek > 100;
    
    -- Zapytanie 2
    SELECT z.liczba_odbitek * f.cena AS `Do zapłaty`
    FROM zamowienia AS z
    JOIN zdjecia AS f ON f.rodzaj = z.rodzaj
    WHERE z.nr_klienta = 3;
    Podgląd strony po tym kroku
    Podgląd strony po kroku 3
  4. Krok 4: Zapytanie 3: suma odbitek na papierze błyszczącym

    Kryteria:

    R.1.5 Wykonane zapytanie 3 zliczające sumę wszystkich odbitek na papierze błyszczącym. W pliku z kwerendami lub na zrzucie istnieje zapis np. SELECT SUM(liczba_odbitek) FROM zamowienia WHERE rodzaj = "błyszczący"; oraz wynik działania zapytania udokumentowany zrzutem, na którym jest widoczna wartość 4291

    ok. 2 min
    Polecenie z arkusza

    Zapytanie 3: zliczające sumę wszystkich odbitek na papierze błyszczącym.

    SUM(liczba_odbitek) z aliasem odbitki_blyszczace - wynik ma nazwę zamiast SUM(liczba_odbitek). Wynik: 4291. Zrzut: kw3.png.

    Typowe błędy

    COUNT liczy wiersze (zamówienia), a nie sumę odbitek.

    kwerendy.txt Tekst zmienione: 5 linii
    -- Zapytanie 1
    SELECT plik, nr_klienta, liczba_odbitek
    FROM zamowienia
    WHERE rodzaj = 'matowy'
      AND liczba_odbitek > 100;
    
    -- Zapytanie 2
    SELECT z.liczba_odbitek * f.cena AS `Do zapłaty`
    FROM zamowienia AS z
    JOIN zdjecia AS f ON f.rodzaj = z.rodzaj
    WHERE z.nr_klienta = 3;
    
    -- Zapytanie 3
    SELECT SUM(liczba_odbitek) AS odbitki_blyszczace
    FROM zamowienia
    WHERE rodzaj = 'błyszczący';
    Podgląd strony po tym kroku
    Podgląd strony po kroku 4
  5. Krok 5: Zapytanie 4: konto użytkownika Anna

    Kryteria:

    R.1.6 Wykonane zapytanie 4 tworzące konto użytkownika Anna na localhost z hasłem @NNa. W pliku z kwerendami lub na zrzucie istnieje zapis np. CREATE USER 'Anna'@'localhost' IDENTIFIED BY '@NNa'; oraz wynik działania zapytania udokumentowany zrzutem, na którym jest widoczny komunikat o poprawnie wykonanym zapytaniu (dopuszczalny jest błąd informujący o niewybraniu bazy)

    ok. 2 min
    Polecenie z arkusza

    Zapytanie 4: tworzące konto użytkownika Anna na localhost z hasłem @NNa.

    CREATE USER 'Anna'@'localhost' IDENTIFIED BY '@NNa'; - konto, które może logować się tylko z tego komputera. Nowe konto nie ma jeszcze żadnych uprawnień; w prawdziwej pracy następnym krokiem byłoby GRANT tylko na potrzebne tabele (zasada najmniejszych uprawnień). Arkusz tego nie wymaga. Zrzut kw4.png: komunikat o wykonaniu.

    Typowe błędy

    Bez @'localhost' konto dostaje host % - może logować się z dowolnego komputera.

    kwerendy.txt Tekst zmienione: 3 linie
    -- Zapytanie 1
    SELECT plik, nr_klienta, liczba_odbitek
    FROM zamowienia
    WHERE rodzaj = 'matowy'
      AND liczba_odbitek > 100;
    
    -- Zapytanie 2
    SELECT z.liczba_odbitek * f.cena AS `Do zapłaty`
    FROM zamowienia AS z
    JOIN zdjecia AS f ON f.rodzaj = z.rodzaj
    WHERE z.nr_klienta = 3;
    
    -- Zapytanie 3
    SELECT SUM(liczba_odbitek) AS odbitki_blyszczace
    FROM zamowienia
    WHERE rodzaj = 'błyszczący';
    
    -- Zapytanie 4
    CREATE USER 'Anna'@'localhost' IDENTIFIED BY '@NNa';
    Podgląd strony po tym kroku
    Podgląd strony po kroku 5
  6. Etap 2: Grafika i strona HTML

    Kroki 6-16 · po tym etapie masz 20 z 41 kryteriów (pkt)

    Krok 6: Przeskalowanie grafiki 10.jpg

    Kryteria:

    R.2.1 Grafika 10.jpg została przeskalowana z zachowaniem proporcji do wysokości 200 px, odpowiadająca temu szerokość to 267 px (±5 px)

    ok. 3 min
    Polecenie z arkusza

    Cechy grafiki 10.jpg: grafikę należy przeskalować z zachowaniem proporcji do wysokości 200 px.

    10.jpg ma 560 × 420 px; GIMP Obraz → Skaluj obraz, wysokość 200 px z zamkniętym łańcuchem - szerokość wyjdzie 267 px (klucz: ±5). Pozostałe zdjęcia już mają 267 × 200 px.

    Podgląd strony po tym kroku
    Podgląd strony po kroku 6
  7. Krok 7: Podstrony regulamin.html i uslugi.html

    ok. 1 min
    Polecenie z arkusza

    Składa się z podstron o nazwach zamowienie.html, regulamin.html, uslugi.html. W pliku regulamin.html należy zapisać jedynie: „Regulamin”. W pliku uslugi.html należy zapisać jedynie: „Usługi”.

    Arkusz mówi „jedynie”, więc w każdym pliku tylko jedno słowo, bez szkieletu HTML. Przeglądarka i tak je wyświetli (sama rozpozna kodowanie UTF-8 przy „ł”). Nazwa pliku uslugi.html bez polskich liter - tak jak w odnośniku.

    regulamin.html HTML nowy plik
    Regulamin
    uslugi.html HTML nowy plik
    Usługi
    Podgląd strony po tym kroku (regulamin.html)
  8. Krok 8: Plik zamowienie.html: HTML5, język i kodowanie

    Kryteria:

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

    ok. 2 min
    Polecenie z arkusza

    Cechy strony zamowienie.html: zapisana w języku HTML5. Zadeklarowany polski język zawartości witryny. Jawnie zastosowany właściwy standard kodowania polskich znaków.

    <!DOCTYPE html>, <html lang="pl">, <meta charset="UTF-8"> jako pierwszy element <head>.

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

    Kryteria: ,

    R.2.3 Nadany tytuł stronie: "Fotografia"

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

    ok. 1 min
    Polecenie z arkusza

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

    <title>Fotografia</title> (R.2.3) i <link rel="stylesheet" href="style.css"> (R.3.2) - w tym arkuszu plik nazywa się style.css. Meta viewport dla telefonów.

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

    Kryteria:

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

    ok. 2 min
    Polecenie z arkusza

    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. Zawartość bloku głównego: bloki sekcji lewej, nawigacyjny i sekcji prawej.

    Układ z klucza R.2.4: <header>, <section> galerii, <main> z <section> + <nav> + <section>, <footer>. Identyfikatory mówią o treści: gallery, order (zamówienie), cart (koszyk) - nie o położeniu. Na wąskim ekranie koszyk mógłby trafić pod zamówienie i nazwa right byłaby myląca.

    Typowe błędy

    Nawigacja poza <main> nie spełnia R.2.4 - klucz wymaga jej między dwiema sekcjami bloku głównego.

    zamowienie.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>Fotografia</title>
        <link rel="stylesheet" href="style.css">
    </head>
    <body>
        <header>
        </header>
        <section id="gallery">
        </section>
        <main>
            <section id="order">
            </section>
            <nav>
            </nav>
            <section id="cart">
            </section>
        </main>
        <footer>
        </footer>
    </body>
    </html>
    Podgląd strony po tym kroku (zamowienie.html)
  11. Krok 11: Nagłówek i galeria dziesięciu obrazów

    Kryteria:

    R.2.6 Blok sekcji obrazów zawiera 10 obrazów od 1.jpg do 10.jpg. Wszystkie obrazy mają tekst alternatywny „obrazy do sprzedaży” oraz atrybut title, taki sam jak nazwa pliku, np. 1.jpg

    ok. 3 min
    Polecenie z arkusza

    Zawartość bloku nagłówkowego: nagłówek pierwszego stopnia o treści „Fotografia artystyczna”. Zawartość bloku sekcji obrazów: 10 obrazów od 1.jpg do 10.jpg. Wszystkie obrazy mają tekst alternatywny „obrazy do sprzedaży” oraz tekst dymka podpowiedzi taki sam jak nazwa pliku, np. 1.jpg.

    <h1> w nagłówku i dziesięć <img> z alt z arkusza i title równym nazwie pliku - title to dymek z ilustracji 2 (R.2.6). Galeria jest statyczna, jak każe arkusz: obrazy nie pochodzą z bazy.

    Typowe błędy

    Zamiana alt z title kosztuje R.2.6.

    zamowienie.html HTML zmienione: 11 linii
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <title>Fotografia</title>
        <link rel="stylesheet" href="style.css">
    </head>
    <body>
        <header>
            <h1>Fotografia artystyczna</h1>
        </header>
        <section id="gallery">
            <img src="1.jpg" alt="obrazy do sprzedaży" title="1.jpg">
            <img src="2.jpg" alt="obrazy do sprzedaży" title="2.jpg">
            <img src="3.jpg" alt="obrazy do sprzedaży" title="3.jpg">
            <img src="4.jpg" alt="obrazy do sprzedaży" title="4.jpg">
            <img src="5.jpg" alt="obrazy do sprzedaży" title="5.jpg">
            <img src="6.jpg" alt="obrazy do sprzedaży" title="6.jpg">
            <img src="7.jpg" alt="obrazy do sprzedaży" title="7.jpg">
            <img src="8.jpg" alt="obrazy do sprzedaży" title="8.jpg">
            <img src="9.jpg" alt="obrazy do sprzedaży" title="9.jpg">
            <img src="10.jpg" alt="obrazy do sprzedaży" title="10.jpg">
        </section>
        <main>
            <section id="order">
            </section>
            <nav>
            </nav>
            <section id="cart">
            </section>
        </main>
        <footer>
        </footer>
    </body>
    </html>
    Podgląd strony po tym kroku (zamowienie.html)
  12. Krok 12: Sekcja zamówienia: pole pliku i liczba kopii

    Kryteria:

    R.3.6 Pierwsze pole edycyjne jest typu file z atrybutem np. accept="image/jpeg" lub accept =".jpg, .jpeg", drugie pole edycyjne jest typu number

    ok. 3 min
    Polecenie z arkusza

    Zawartość sekcji lewej: nagłówek trzeciego stopnia o treści „Zamówienie”. Etykieta „Wybierz obraz: ”, powiązana z polem edycyjnym znajdującym się obok. Pole edycyjne przeznaczone do wyboru pliku. Akceptuje tylko obrazy w formacie JPEG. Poniżej etykieta „Liczba Kopii: ”, powiązana z polem edycyjnym znajdującym się obok. Pole edycyjne przeznaczone do wpisywania jedynie liczb.

    <h3> i dwie pary <label for> + pole: type="file" z accept="image/jpeg" (okno wyboru pokaże tylko JPEG-i) i type="number" (R.3.6). Pole liczby dostaje min="1" i step="1" - strzałki nie zejdą poniżej jednej kopii i nie podadzą ułamka. To podpowiedź dla przeglądarki; skrypt i tak sprawdzi liczbę.

    Etykieta i pole stoją w jednej linii, a <br> kończy linię formularza - tu znacznik łamania jest na miejscu, bo arkusz każe ustawić kolejne kontrolki poniżej.

    Typowe błędy

    <label> bez for nie jest powiązana z polem - klucz R.2.7 wymaga powiązanych etykiet.

    zamowienie.html HTML zmienione: 5 linii
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <title>Fotografia</title>
        <link rel="stylesheet" href="style.css">
    </head>
    <body>
        <header>
            <h1>Fotografia artystyczna</h1>
        </header>
        <section id="gallery">
            <img src="1.jpg" alt="obrazy do sprzedaży" title="1.jpg">
            <img src="2.jpg" alt="obrazy do sprzedaży" title="2.jpg">
            <img src="3.jpg" alt="obrazy do sprzedaży" title="3.jpg">
            <img src="4.jpg" alt="obrazy do sprzedaży" title="4.jpg">
            <img src="5.jpg" alt="obrazy do sprzedaży" title="5.jpg">
            <img src="6.jpg" alt="obrazy do sprzedaży" title="6.jpg">
            <img src="7.jpg" alt="obrazy do sprzedaży" title="7.jpg">
            <img src="8.jpg" alt="obrazy do sprzedaży" title="8.jpg">
            <img src="9.jpg" alt="obrazy do sprzedaży" title="9.jpg">
            <img src="10.jpg" alt="obrazy do sprzedaży" title="10.jpg">
        </section>
        <main>
            <section id="order">
                <h3>Zamówienie</h3>
                <label for="image">Wybierz obraz: </label>
                <input type="file" id="image" accept="image/jpeg"><br>
                <label for="copies">Liczba Kopii: </label>
                <input type="number" id="copies" min="1" step="1"><br>
            </section>
            <nav>
            </nav>
            <section id="cart">
            </section>
        </main>
        <footer>
        </footer>
    </body>
    </html>
    Podgląd strony po tym kroku (zamowienie.html)
  13. Krok 13: Pola opcji papieru i przycisk

    Kryteria: ,

    R.2.7 W sekcji lewej umieszczone dwa pola edycyjne z powiązanymi etykietami, kolejno: "Wybierz obraz:" i "Liczba kopii:", dwa pola opcji: "Papier błyszczący" i "Papier matowy" oraz przycisk "Dodaj do koszyka"

    R.3.5 Pierwsze pole opcji jest domyślnie zaznaczone. W jednym momencie można zaznaczyć tylko jedno pole opcji

    ok. 3 min
    Polecenie z arkusza

    Poniżej pole opcji „Papier błyszczący”, domyślnie zaznaczone. Poniżej pole opcji „Papier matowy”. W jednym momencie można zaznaczyć tylko jedno pole opcji. Przycisk „Dodaj do koszyka”, którego kliknięcie wywołuje skrypt.

    Dwa pola type="radio" ze wspólnym name="paper" - wspólna nazwa robi z nich grupę, w której zaznaczone jest zawsze jedno; checked przy błyszczącym (R.3.5). Każde pole ma value (glossy, matte) - skrypt odczyta wartość zaznaczonego pola zamiast sprawdzać po kolei, które jest zaznaczone.

    Przycisk ma id, po którym znajdzie go skrypt, i nie ma onclick - obsługę kliknięcia dopisze skrypt. type="button" mówi wprost, że przycisk niczego nie wysyła.

    Typowe błędy

    Różne name w polach radio pozwalają zaznaczyć oba naraz - R.3.5 przepada.

    zamowienie.html HTML zmienione: 5 linii
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <title>Fotografia</title>
        <link rel="stylesheet" href="style.css">
    </head>
    <body>
        <header>
            <h1>Fotografia artystyczna</h1>
        </header>
        <section id="gallery">
            <img src="1.jpg" alt="obrazy do sprzedaży" title="1.jpg">
            <img src="2.jpg" alt="obrazy do sprzedaży" title="2.jpg">
            <img src="3.jpg" alt="obrazy do sprzedaży" title="3.jpg">
            <img src="4.jpg" alt="obrazy do sprzedaży" title="4.jpg">
            <img src="5.jpg" alt="obrazy do sprzedaży" title="5.jpg">
            <img src="6.jpg" alt="obrazy do sprzedaży" title="6.jpg">
            <img src="7.jpg" alt="obrazy do sprzedaży" title="7.jpg">
            <img src="8.jpg" alt="obrazy do sprzedaży" title="8.jpg">
            <img src="9.jpg" alt="obrazy do sprzedaży" title="9.jpg">
            <img src="10.jpg" alt="obrazy do sprzedaży" title="10.jpg">
        </section>
        <main>
            <section id="order">
                <h3>Zamówienie</h3>
                <label for="image">Wybierz obraz: </label>
                <input type="file" id="image" accept="image/jpeg"><br>
                <label for="copies">Liczba Kopii: </label>
                <input type="number" id="copies" min="1" step="1"><br>
                <input type="radio" id="glossy" name="paper" value="glossy" checked>
                <label for="glossy">Papier błyszczący</label><br>
                <input type="radio" id="matte" name="paper" value="matte">
                <label for="matte">Papier matowy</label><br>
                <button type="button" id="add-button">Dodaj do koszyka</button>
            </section>
            <nav>
            </nav>
            <section id="cart">
            </section>
        </main>
        <footer>
        </footer>
    </body>
    </html>
    Podgląd strony po tym kroku (zamowienie.html)
  14. Krok 14: Blok nawigacyjny: lista odnośników

    Kryteria: ,

    R.2.8 W bloku nawigacyjnym umieszczona jedna lista punktowana z trzema elementami, zastosowane znaczniki <ul> i <li>

    R.3.3 Wybranie odnośnika "Regulamin" otwiera stronę regulamin.html, "Usługi" otwiera uslugi.html , „Zdjęcia” inicjuje otwarcie strony "https://pixabay.com"

    ok. 2 min
    Polecenie z arkusza

    Zawartość bloku nawigacyjnego: lista punktowana (nieuporządkowana) z elementami: odnośnik prowadzący do podstrony regulamin.html o treści „Regulamin”, odnośnik prowadzący do podstrony uslugi.html o treści „Usługi”, odnośnik prowadzący do strony internetowej https://pixabay.com o treści „Zdjęcia”.

    <ul> z trzema <li> (R.2.8): dwa odnośniki względne do podstron i jeden pełny adres https://pixabay.com (R.3.3). Lista w <nav> to standardowy zapis menu - czytnik ekranu ogłasza „nawigacja, lista, 3 elementy”.

    Typowe błędy

    href="pixabay.com" bez https:// szuka pliku w folderze strony.

    zamowienie.html HTML zmienione: 5 linii
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <title>Fotografia</title>
        <link rel="stylesheet" href="style.css">
    </head>
    <body>
        <header>
            <h1>Fotografia artystyczna</h1>
        </header>
        <section id="gallery">
            <img src="1.jpg" alt="obrazy do sprzedaży" title="1.jpg">
            <img src="2.jpg" alt="obrazy do sprzedaży" title="2.jpg">
            <img src="3.jpg" alt="obrazy do sprzedaży" title="3.jpg">
            <img src="4.jpg" alt="obrazy do sprzedaży" title="4.jpg">
            <img src="5.jpg" alt="obrazy do sprzedaży" title="5.jpg">
            <img src="6.jpg" alt="obrazy do sprzedaży" title="6.jpg">
            <img src="7.jpg" alt="obrazy do sprzedaży" title="7.jpg">
            <img src="8.jpg" alt="obrazy do sprzedaży" title="8.jpg">
            <img src="9.jpg" alt="obrazy do sprzedaży" title="9.jpg">
            <img src="10.jpg" alt="obrazy do sprzedaży" title="10.jpg">
        </section>
        <main>
            <section id="order">
                <h3>Zamówienie</h3>
                <label for="image">Wybierz obraz: </label>
                <input type="file" id="image" accept="image/jpeg"><br>
                <label for="copies">Liczba Kopii: </label>
                <input type="number" id="copies" min="1" step="1"><br>
                <input type="radio" id="glossy" name="paper" value="glossy" checked>
                <label for="glossy">Papier błyszczący</label><br>
                <input type="radio" id="matte" name="paper" value="matte">
                <label for="matte">Papier matowy</label><br>
                <button type="button" id="add-button">Dodaj do koszyka</button>
            </section>
            <nav>
                <ul>
                    <li><a href="regulamin.html">Regulamin</a></li>
                    <li><a href="uslugi.html">Usługi</a></li>
                    <li><a href="https://pixabay.com">Zdjęcia</a></li>
                </ul>
            </nav>
            <section id="cart">
            </section>
        </main>
        <footer>
        </footer>
    </body>
    </html>
    Podgląd strony po tym kroku (zamowienie.html)
  15. Krok 15: Sekcja koszyka i blok na zamówienia

    Kryteria:

    R.2.9 W sekcji prawej umieszczony element blokowy (np. <div> lub <section>)

    ok. 2 min
    Polecenie z arkusza

    Zawartość sekcji prawej: nagłówek trzeciego stopnia o treści „Koszyk”. Blok, w którym zostanie umieszczony efekt działania skryptu.

    <h3> i pusty <div id="cart-items"> (R.2.9) - do niego skrypt będzie dokładał zamówienia. Atrybut aria-live="polite" każe czytnikowi ekranu przeczytać to, co skrypt dopisze, gdy użytkownik skończy bieżącą czynność - bez niego niewidomy klient nie dowiedziałby się, że coś trafiło do koszyka.

    zamowienie.html HTML zmienione: 3 linie
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <title>Fotografia</title>
        <link rel="stylesheet" href="style.css">
    </head>
    <body>
        <header>
            <h1>Fotografia artystyczna</h1>
        </header>
        <section id="gallery">
            <img src="1.jpg" alt="obrazy do sprzedaży" title="1.jpg">
            <img src="2.jpg" alt="obrazy do sprzedaży" title="2.jpg">
            <img src="3.jpg" alt="obrazy do sprzedaży" title="3.jpg">
            <img src="4.jpg" alt="obrazy do sprzedaży" title="4.jpg">
            <img src="5.jpg" alt="obrazy do sprzedaży" title="5.jpg">
            <img src="6.jpg" alt="obrazy do sprzedaży" title="6.jpg">
            <img src="7.jpg" alt="obrazy do sprzedaży" title="7.jpg">
            <img src="8.jpg" alt="obrazy do sprzedaży" title="8.jpg">
            <img src="9.jpg" alt="obrazy do sprzedaży" title="9.jpg">
            <img src="10.jpg" alt="obrazy do sprzedaży" title="10.jpg">
        </section>
        <main>
            <section id="order">
                <h3>Zamówienie</h3>
                <label for="image">Wybierz obraz: </label>
                <input type="file" id="image" accept="image/jpeg"><br>
                <label for="copies">Liczba Kopii: </label>
                <input type="number" id="copies" min="1" step="1"><br>
                <input type="radio" id="glossy" name="paper" value="glossy" checked>
                <label for="glossy">Papier błyszczący</label><br>
                <input type="radio" id="matte" name="paper" value="matte">
                <label for="matte">Papier matowy</label><br>
                <button type="button" id="add-button">Dodaj do koszyka</button>
            </section>
            <nav>
                <ul>
                    <li><a href="regulamin.html">Regulamin</a></li>
                    <li><a href="uslugi.html">Usługi</a></li>
                    <li><a href="https://pixabay.com">Zdjęcia</a></li>
                </ul>
            </nav>
            <section id="cart">
                <h3>Koszyk</h3>
                <div id="cart-items" aria-live="polite">
                </div>
            </section>
        </main>
        <footer>
        </footer>
    </body>
    </html>
    Podgląd strony po tym kroku (zamowienie.html)
  16. Krok 16: Stopka z numerem zdającego

    Kryteria: ,

    R.2.5 W bloku nagłówkowym umieszczony <h1> "Fotografia artystyczna", w sekcji lewej <h3> "Zamówienie", w prawej <h3> "Koszyk", w stopce paragraf z numerem zdającego

    R.2.10 W stopce zastosowany dla numeru zdającego znacznik <em>

    ok. 1 min
    Polecenie z arkusza

    Zawartość stopki: paragraf o treści „Autor strony: ”, dalej wstawiony numer zdającego. Numer zdającego jest zapisany za pomocą znacznika semantycznego oznaczającego tekst uwypuklony, formatowany domyślnie jako pochylony.

    <em> - znacznik semantyczny uwypuklenia, domyślnie pochylony (R.2.10); <i> pochyla bez znaczenia. Tu stoi nazwisko autora rozwiązania - na egzaminie wpisujesz swój numer zdającego (PESEL). Z nagłówkami <h1>, dwoma <h3> i tym paragrafem to komplet R.2.5.

    zamowienie.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>Fotografia</title>
        <link rel="stylesheet" href="style.css">
    </head>
    <body>
        <header>
            <h1>Fotografia artystyczna</h1>
        </header>
        <section id="gallery">
            <img src="1.jpg" alt="obrazy do sprzedaży" title="1.jpg">
            <img src="2.jpg" alt="obrazy do sprzedaży" title="2.jpg">
            <img src="3.jpg" alt="obrazy do sprzedaży" title="3.jpg">
            <img src="4.jpg" alt="obrazy do sprzedaży" title="4.jpg">
            <img src="5.jpg" alt="obrazy do sprzedaży" title="5.jpg">
            <img src="6.jpg" alt="obrazy do sprzedaży" title="6.jpg">
            <img src="7.jpg" alt="obrazy do sprzedaży" title="7.jpg">
            <img src="8.jpg" alt="obrazy do sprzedaży" title="8.jpg">
            <img src="9.jpg" alt="obrazy do sprzedaży" title="9.jpg">
            <img src="10.jpg" alt="obrazy do sprzedaży" title="10.jpg">
        </section>
        <main>
            <section id="order">
                <h3>Zamówienie</h3>
                <label for="image">Wybierz obraz: </label>
                <input type="file" id="image" accept="image/jpeg"><br>
                <label for="copies">Liczba Kopii: </label>
                <input type="number" id="copies" min="1" step="1"><br>
                <input type="radio" id="glossy" name="paper" value="glossy" checked>
                <label for="glossy">Papier błyszczący</label><br>
                <input type="radio" id="matte" name="paper" value="matte">
                <label for="matte">Papier matowy</label><br>
                <button type="button" id="add-button">Dodaj do koszyka</button>
            </section>
            <nav>
                <ul>
                    <li><a href="regulamin.html">Regulamin</a></li>
                    <li><a href="uslugi.html">Usługi</a></li>
                    <li><a href="https://pixabay.com">Zdjęcia</a></li>
                </ul>
            </nav>
            <section id="cart">
                <h3>Koszyk</h3>
                <div id="cart-items" aria-live="polite">
                </div>
            </section>
        </main>
        <footer>
            <p>Autor strony: <em>Bartosz Bryniarski</em></p>
        </footer>
    </body>
    </html>
    Podgląd strony po tym kroku (zamowienie.html)
  17. Etap 3: Styl CSS

    Kroki 17-23 · po tym etapie masz 31 z 41 kryteriów (pkt)

    Krok 17: Zmienne z kolorami, krój i tło strony

    Kryteria:

    R.4.1 Ustawiony styl domyślny dla wszystkich selektorów (*): krój czcionki "Century Gothic", Arial (tylko taka kolejność)

    ok. 2 min
    Polecenie z arkusza

    Domyślnie, dla wszystkich selektorów: krój czcionki Century Gothic, w przypadku braku Arial. Dla ciała strony: kolor tła BlanchedAlmond.

    Kolory arkusza w zmiennych :root - Sienna występuje w nagłówku, stopce i odnośnikach, więc wpisana raz. * z krojem "Century Gothic", Arial - nazwa ze spacją w cudzysłowie, kolejność dokładnie z klucza (R.4.1: „tylko taka kolejność”, więc tu nie ma dopisanej rodziny sans-serif). Tło body ze zmiennej.

    style.css CSS nowy plik
    :root {
        --color-page: BlanchedAlmond;
        --color-brand: Sienna;
        --color-brand-dark: #4C1900;
        --color-panel: NavajoWhite;
        --color-shadow: DimGray;
    }
    
    * {
        font-family: "Century Gothic", Arial;
    }
    
    body {
        background-color: var(--color-page);
    }
    Podgląd strony po tym kroku (zamowienie.html)
  18. Krok 18: Nagłówek i stopka

    Kryteria:

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

    ok. 1 min
    Polecenie z arkusza

    Dla bloku nagłówkowego i stopki: kolor tła Sienna, biały kolor czcionki, marginesy wewnętrzne 5 px, wyrównanie tekstu do środka, rozmiar czcionki 130%.

    Jedna reguła dla obu bloków, pięć właściwości w kolejności z arkusza (R.4.5).

    style.css CSS zmienione: 9 linii
    :root {
        --color-page: BlanchedAlmond;
        --color-brand: Sienna;
        --color-brand-dark: #4C1900;
        --color-panel: NavajoWhite;
        --color-shadow: DimGray;
    }
    
    * {
        font-family: "Century Gothic", Arial;
    }
    
    body {
        background-color: var(--color-page);
    }
    
    header,
    footer {
        background-color: var(--color-brand);
        color: white;
        padding: 5px;
        text-align: center;
        font-size: 130%;
    }
    Podgląd strony po tym kroku (zamowienie.html)
  19. Krok 19: Sekcja zamówienia i siatka bloku głównego

    Kryteria:

    R.4.8 Ustawiony dla sekcji lewej styl box-shadow: 5px 5px 10px DimGray;

    ok. 4 min
    Polecenie z arkusza

    Dla sekcji lewej: kolor tła NavajoWhite, marginesy zewnętrzne 10 px, wewnętrzne 20 px, szerokość 35%, wysokość 400 px, cień o przesunięciu 5 px w obu osiach, rozmyciu 10 px i kolorze DimGray.

    Szerokość 35% wynika z siatki, więc ten krok ją zakłada:

    • display: grid na <main> i grid-template-columns: 35% 50% - kolumna zamówienia i kolumna nawigacji z koszykiem (klucz R.4.4 dopuszcza szerokości w grid-template-columns),
    • grid-template-areas - mapa: zamówienie zajmuje oba wiersze lewej kolumny, w prawej jest nawigacja, a pod nią koszyk - dokładnie ilustracja 3,
    • column-gap: 2% - odstęp między kolumnami bez marginesów, align-items: start - bloki nie rozciągają się na wysokość wiersza, tylko mają swoje wysokości z arkusza.

    Sekcja zamówienia: grid-area: order, tło ze zmiennej, margin: 10px, padding: 20px, wysokość 400 px i cień bloku box-shadow: 5px 5px 10px (R.4.8) - przesunięcie, przesunięcie, rozmycie, kolor.

    W siatce margines (10 px) i dopełnienie (20 px) mieszczą się w kolumnie 35%, więc sama sekcja wychodzi trochę węższa niż w wersji podstawowej (przy oknie 1280 px ok. 415 px zamiast 475 px). Kolumna ma dokładnie 35%, tak jak wymaga arkusz.

    Typowe błędy

    text-shadow daje cień literom, a nie blokowi - R.4.8 wymaga box-shadow.

    style.css CSS zmienione: 19 linii
    :root {
        --color-page: BlanchedAlmond;
        --color-brand: Sienna;
        --color-brand-dark: #4C1900;
        --color-panel: NavajoWhite;
        --color-shadow: DimGray;
    }
    
    * {
        font-family: "Century Gothic", Arial;
    }
    
    body {
        background-color: var(--color-page);
    }
    
    header,
    footer {
        background-color: var(--color-brand);
        color: white;
        padding: 5px;
        text-align: center;
        font-size: 130%;
    }
    
    main {
        display: grid;
        grid-template-columns: 35% 50%;
        grid-template-areas:
            "order nav"
            "order cart";
        column-gap: 2%;
        align-items: start;
    }
    
    #order {
        grid-area: order;
        background-color: var(--color-panel);
        margin: 10px;
        padding: 20px;
        height: 400px;
        box-shadow: 5px 5px 10px var(--color-shadow);
    }
    Podgląd strony po tym kroku (zamowienie.html)
  20. Krok 20: Nawigacja i koszyk

    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)

    R.4.7 Ustawione dla bloku nagłówkowego i stopki rozmiar czcionki 130%, oraz dla sekcji prawej overflow: auto;

    ok. 2 min
    Polecenie z arkusza

    Dla bloku nawigacyjnego: szerokość 50%, wysokość 50 px. Dla sekcji prawej: szerokość 50%, wysokość 420 px, paski przewijania pojawiające się tylko w przypadku przepełnienia bloku.

    Każdy blok dostaje swój obszar mapy (grid-area), a szerokość 50% daje kolumna siatki. Wysokości z arkusza; overflow: auto przy koszyku - pasek pojawi się dopiero, gdy zamówień przybędzie ponad 420 px.

    Teraz układ zgadza się z ilustracją 3 (R.3.1): zamówienie po lewej, po prawej nawigacja i pod nią koszyk - bez float i bez clear w stopce. Szerokości 35% / 50% w siatce i wysokości 400 / 50 / 420 px są kompletne; ostatnia część R.4.4 to szerokość obrazów.

    style.css CSS zmienione: 11 linii
    :root {
        --color-page: BlanchedAlmond;
        --color-brand: Sienna;
        --color-brand-dark: #4C1900;
        --color-panel: NavajoWhite;
        --color-shadow: DimGray;
    }
    
    * {
        font-family: "Century Gothic", Arial;
    }
    
    body {
        background-color: var(--color-page);
    }
    
    header,
    footer {
        background-color: var(--color-brand);
        color: white;
        padding: 5px;
        text-align: center;
        font-size: 130%;
    }
    
    main {
        display: grid;
        grid-template-columns: 35% 50%;
        grid-template-areas:
            "order nav"
            "order cart";
        column-gap: 2%;
        align-items: start;
    }
    
    #order {
        grid-area: order;
        background-color: var(--color-panel);
        margin: 10px;
        padding: 20px;
        height: 400px;
        box-shadow: 5px 5px 10px var(--color-shadow);
    }
    
    nav {
        grid-area: nav;
        height: 50px;
    }
    
    #cart {
        grid-area: cart;
        height: 420px;
        overflow: auto;
    }
    Podgląd strony po tym kroku (zamowienie.html)
  21. Krok 21: Pola, przycisk i elementy listy

    ok. 2 min
    Polecenie z arkusza

    Dla pola edycyjnego i przycisku: jedynie zewnętrzny margines górny 20 px. Dla selektora elementu listy: sposób wyświetlania liniowo-blokowy.

    margin-top: 20px tylko z góry dla input i button - kontrolki rozsuwają się w pionie. Elementy listy li (selektorem znacznika, jak każe arkusz) z display: inline-block stają w jednym rzędzie, a punktory znikają, bo element przestaje być elementem listy w sensie wyświetlania. Tu arkusz narzuca konkretną wartość, więc nie ma display: flex na liście - choć do menu w rzędzie nadawałby się równie dobrze.

    Typowe błędy

    margin: 20px ze wszystkich stron odsuwa pola także od etykiet - arkusz mówi „jedynie górny”.

    style.css CSS zmienione: 9 linii
    :root {
        --color-page: BlanchedAlmond;
        --color-brand: Sienna;
        --color-brand-dark: #4C1900;
        --color-panel: NavajoWhite;
        --color-shadow: DimGray;
    }
    
    * {
        font-family: "Century Gothic", Arial;
    }
    
    body {
        background-color: var(--color-page);
    }
    
    header,
    footer {
        background-color: var(--color-brand);
        color: white;
        padding: 5px;
        text-align: center;
        font-size: 130%;
    }
    
    main {
        display: grid;
        grid-template-columns: 35% 50%;
        grid-template-areas:
            "order nav"
            "order cart";
        column-gap: 2%;
        align-items: start;
    }
    
    #order {
        grid-area: order;
        background-color: var(--color-panel);
        margin: 10px;
        padding: 20px;
        height: 400px;
        box-shadow: 5px 5px 10px var(--color-shadow);
    }
    
    nav {
        grid-area: nav;
        height: 50px;
    }
    
    #cart {
        grid-area: cart;
        height: 420px;
        overflow: auto;
    }
    
    input,
    button {
        margin-top: 20px;
    }
    
    li {
        display: inline-block;
    }
    Podgląd strony po tym kroku (zamowienie.html)
  22. Krok 22: Obrazy: szerokość, opływanie i galeria bez clear

    Kryteria:

    R.4.9 Ustawiony dla selektora li: display: inline-block; oraz dla obrazu: float: left

    ok. 3 min
    Polecenie z arkusza

    Dla selektora obrazu: szerokość 10%, opływanie po prawej stronie (obraz po lewej stronie).

    img { width: 10%; float: left; } - arkusz każe opływanie, czyli float, a klucz R.4.9 sprawdza float: left. Tu float jest narzucony i zostaje: robi dokładnie to, do czego go wymyślono - tekst opływa obraz (w koszyku podpisy staną obok miniatury). Dziesięć obrazów po 10% zajmuje jeden rząd galerii (R.4.4: 10% dla img).

    Problem pływających obrazów: blok, w którym tylko one są, ma zerową wysokość i następne bloki wjeżdżają pod obrazy. Stary sposób to clear: both w kolejnym bloku albo „clearfix”. Nowy - display: flow-root na galerii: blok tworzy własny kontekst formatowania i obejmuje pływające dzieci. Galeria ma wysokość obrazów, a blok główny zaczyna się pod nią.

    Typowe błędy

    float: right stawia obraz po prawej - arkusz w nawiasie wyjaśnia: obraz po lewej.

    style.css CSS zmienione: 9 linii
    :root {
        --color-page: BlanchedAlmond;
        --color-brand: Sienna;
        --color-brand-dark: #4C1900;
        --color-panel: NavajoWhite;
        --color-shadow: DimGray;
    }
    
    * {
        font-family: "Century Gothic", Arial;
    }
    
    body {
        background-color: var(--color-page);
    }
    
    header,
    footer {
        background-color: var(--color-brand);
        color: white;
        padding: 5px;
        text-align: center;
        font-size: 130%;
    }
    
    main {
        display: grid;
        grid-template-columns: 35% 50%;
        grid-template-areas:
            "order nav"
            "order cart";
        column-gap: 2%;
        align-items: start;
    }
    
    #order {
        grid-area: order;
        background-color: var(--color-panel);
        margin: 10px;
        padding: 20px;
        height: 400px;
        box-shadow: 5px 5px 10px var(--color-shadow);
    }
    
    nav {
        grid-area: nav;
        height: 50px;
    }
    
    #cart {
        grid-area: cart;
        height: 420px;
        overflow: auto;
    }
    
    input,
    button {
        margin-top: 20px;
    }
    
    li {
        display: inline-block;
    }
    
    img {
        width: 10%;
        float: left;
    }
    
    #gallery {
        display: flow-root;
    }
    Podgląd strony po tym kroku (zamowienie.html)
  23. Krok 23: Odnośniki i odnośnik pod kursorem

    Kryteria: , , , ,

    R.4.2 Ustawiony kolor tła dla ciała strony BlanchedAlmond, dla bloku nagłówkowego i stopki Sienna, dla sekcji lewej NavajoWhite

    R.4.3 Ustawiony biały kolor czcionki dla bloków nagłówkowego i stopki, oraz Sienna dla selektora a

    R.4.4 Ustawiona szerokość dla sekcji lewej 35%, dla bloków nawigacyjnego i sekcji prawej 50% (width lub grid-template-columns lub flex) , dla selektora img 10%. Ustawione wysokości sekcji lewej 400px, bloku nawigacyjnego 50px, sekcji prawej 420px

    R.4.6 Ustawione marginesy wewnętrzne (padding ) dla bloku nagłówkowego i stopki 5px, dla sekcji lewej 20px oraz marginesy zewnętrzne (margin) dla sekcji lewej 10px , margin-top: 20px dla pola edycyjnego i przycisku, margin-right: 30px dla odnośnika (ustawione min. 6 właściwości)

    R.4.10 Gdy kursor znajdzie się na odnośniku jego kolor czcionki zamienia się na #4C1900

    ok. 2 min
    Polecenie z arkusza

    Dla selektora odnośnika: kolor czcionki Sienna, jedynie zewnętrzny margines prawy 30 px. W momencie, gdy kursor znajdzie się na odnośniku jego kolor czcionki zamienia się na #4C1900.

    a selektorem znacznika: kolor ze zmiennej i margin-right: 30px - z marginesami nagłówka, stopki, sekcji zamówienia i pól to komplet R.4.6. a:hover z ciemnym brązem (R.4.10). Teraz komplet także R.4.2 (tła strony, nagłówka, stopki i sekcji zamówienia), R.4.3 (biały tekst nagłówka i stopki, Sienna odnośników) i R.4.4 (szerokości i wysokości).

    style.css CSS zmienione: 9 linii
    :root {
        --color-page: BlanchedAlmond;
        --color-brand: Sienna;
        --color-brand-dark: #4C1900;
        --color-panel: NavajoWhite;
        --color-shadow: DimGray;
    }
    
    * {
        font-family: "Century Gothic", Arial;
    }
    
    body {
        background-color: var(--color-page);
    }
    
    header,
    footer {
        background-color: var(--color-brand);
        color: white;
        padding: 5px;
        text-align: center;
        font-size: 130%;
    }
    
    main {
        display: grid;
        grid-template-columns: 35% 50%;
        grid-template-areas:
            "order nav"
            "order cart";
        column-gap: 2%;
        align-items: start;
    }
    
    #order {
        grid-area: order;
        background-color: var(--color-panel);
        margin: 10px;
        padding: 20px;
        height: 400px;
        box-shadow: 5px 5px 10px var(--color-shadow);
    }
    
    nav {
        grid-area: nav;
        height: 50px;
    }
    
    #cart {
        grid-area: cart;
        height: 420px;
        overflow: auto;
    }
    
    input,
    button {
        margin-top: 20px;
    }
    
    li {
        display: inline-block;
    }
    
    img {
        width: 10%;
        float: left;
    }
    
    #gallery {
        display: flow-root;
    }
    
    a {
        color: var(--color-brand);
        margin-right: 30px;
    }
    
    a:hover {
        color: var(--color-brand-dark);
    }
    Podgląd strony po tym kroku (zamowienie.html)
  24. Etap 4: JavaScript

    Kroki 24-31 · po tym etapie masz 41 z 41 kryteriów (pkt)

    Krok 24: Plik script.js z defer i cennik w obiekcie

    Kryteria:

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

    ok. 3 min
    Polecenie z arkusza

    Wykonywany po stronie klienta, po kliknięciu przycisku. Oblicza cenę na podstawie liczby kopii i rodzaju papieru. Dla papieru błyszczącego cena jednostkowa wynosi 1,5 zł, dla papieru matowego - 2 zł.

    <script src="script.js" defer> w <head>: defer uruchamia skrypt dopiero po wczytaniu całego HTML, więc przycisk i koszyk już istnieją.

    Pierwsza rzecz w skrypcie to cennik - obiekt, w którym kluczem jest wartość pola radio (glossy, matte), a wartością cena za odbitkę. Stała ma nazwę wielkimi literami (PRICES), bo to dana konfiguracyjna, która się nie zmienia. Zamiast drabinki if (glossy) ... else ... cena będzie odczytywana jednym PRICES[paper], a trzeci rodzaj papieru to jedna nowa linia w obiekcie.

    script.js JavaScript nowy plik
    const PRICES = {
        glossy: 1.5,
        matte: 2,
    };
    zamowienie.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>Fotografia</title>
        <link rel="stylesheet" href="style.css">
        <script src="script.js" defer></script>
    </head>
    <body>
        <header>
            <h1>Fotografia artystyczna</h1>
        </header>
        <section id="gallery">
            <img src="1.jpg" alt="obrazy do sprzedaży" title="1.jpg">
            <img src="2.jpg" alt="obrazy do sprzedaży" title="2.jpg">
            <img src="3.jpg" alt="obrazy do sprzedaży" title="3.jpg">
            <img src="4.jpg" alt="obrazy do sprzedaży" title="4.jpg">
            <img src="5.jpg" alt="obrazy do sprzedaży" title="5.jpg">
            <img src="6.jpg" alt="obrazy do sprzedaży" title="6.jpg">
            <img src="7.jpg" alt="obrazy do sprzedaży" title="7.jpg">
            <img src="8.jpg" alt="obrazy do sprzedaży" title="8.jpg">
            <img src="9.jpg" alt="obrazy do sprzedaży" title="9.jpg">
            <img src="10.jpg" alt="obrazy do sprzedaży" title="10.jpg">
        </section>
        <main>
            <section id="order">
                <h3>Zamówienie</h3>
                <label for="image">Wybierz obraz: </label>
                <input type="file" id="image" accept="image/jpeg"><br>
                <label for="copies">Liczba Kopii: </label>
                <input type="number" id="copies" min="1" step="1"><br>
                <input type="radio" id="glossy" name="paper" value="glossy" checked>
                <label for="glossy">Papier błyszczący</label><br>
                <input type="radio" id="matte" name="paper" value="matte">
                <label for="matte">Papier matowy</label><br>
                <button type="button" id="add-button">Dodaj do koszyka</button>
            </section>
            <nav>
                <ul>
                    <li><a href="regulamin.html">Regulamin</a></li>
                    <li><a href="uslugi.html">Usługi</a></li>
                    <li><a href="https://pixabay.com">Zdjęcia</a></li>
                </ul>
            </nav>
            <section id="cart">
                <h3>Koszyk</h3>
                <div id="cart-items" aria-live="polite">
                </div>
            </section>
        </main>
        <footer>
            <p>Autor strony: <em>Bartosz Bryniarski</em></p>
        </footer>
    </body>
    </html>
    Podgląd strony po tym kroku (zamowienie.html)
  25. Krok 25: Funkcja addToCart i nasłuchiwacz przycisku

    Kryteria: ,

    R.3.4 Zdefiniowane dla przycisku zdarzenie związane z jego wciśnięciem, które uruchamia funkcję skryptu (obsługa zdarzenia zdefiniowana w HTML np. onClick lub w skrypcie np.addEventListener zmienna.onClick = …)

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

    ok. 3 min
    Polecenie z arkusza

    Przycisk „Dodaj do koszyka”, którego kliknięcie wywołuje skrypt.

    Pola i blok koszyka są wyszukiwane raz, przy starcie skryptu, i trzymane w stałych - funkcja nie musi szukać ich przy każdym kliknięciu. Funkcja addToCart() jest na razie pusta.

    Ostatnia linia rejestruje nasłuchiwacz: addEventListener('click', addToCart) - funkcja przekazana bez nawiasów (nie wywołana). Klucz R.3.4 wymienia tę drogę wprost obok onclick. HTML zostaje czystą strukturą, a całe zachowanie strony jest w jednym pliku.

    Typowe błędy

    addEventListener('click', addToCart()) z nawiasami wywołuje funkcję raz, przy wczytaniu strony - kliknięcie potem nic nie robi.

    script.js JavaScript zmienione: 9 linii
    const PRICES = {
        glossy: 1.5,
        matte: 2,
    };
    
    const fileInput = document.getElementById('image');
    const copiesInput = document.getElementById('copies');
    const cartItems = document.getElementById('cart-items');
    
    function addToCart() {
    }
    
    document.getElementById('add-button').addEventListener('click', addToCart);
    Podgląd strony po tym kroku (zamowienie.html)
  26. Krok 26: Odczyt liczby kopii i rodzaju papieru

    Kryteria:

    R.5.2 Skrypt pobiera dane z przynajmniej jednej kontrolki, np. zastosowana funkcja document.getElementById z parametrem, który zdefiniowany w kontrolce jako id

    ok. 3 min
    Polecenie z arkusza

    Pobiera dane z kontrolek.

    • Number(copiesInput.value) - value pola jest zawsze napisem, Number zamienia go na liczbę od razu przy odczycie (pusty napis daje 0),
    • document.querySelector('input[name="paper"]:checked') - selektor CSS z tabeli 1 arkusza: pole radio z grupy paper, które jest zaznaczone. Jego value to glossy albo matte. Jedna linia zamiast sprawdzania pól po kolei.

    Pole liczby kopii jest odczytywane przez getElementById - tak, jak podaje przykład w kluczu R.5.2.

    script.js JavaScript zmienione: 2 linie
    const PRICES = {
        glossy: 1.5,
        matte: 2,
    };
    
    const fileInput = document.getElementById('image');
    const copiesInput = document.getElementById('copies');
    const cartItems = document.getElementById('cart-items');
    
    function addToCart() {
        const copies = Number(copiesInput.value);
        const paper = document.querySelector('input[name="paper"]:checked').value;
    }
    
    document.getElementById('add-button').addEventListener('click', addToCart);
    Podgląd strony po tym kroku (zamowienie.html)
  27. Krok 27: Obliczenie ceny

    Kryteria:

    R.5.3 Skrypt oblicza cenę na podstawie liczby kopii pobranej z kontrolki i rodzaju papieru: błyszczący 1.5 zł, matowy 2 zł

    ok. 2 min
    Polecenie z arkusza

    Oblicza cenę na podstawie liczby kopii i rodzaju papieru. Dla papieru błyszczącego cena jednostkowa wynosi 1,5 zł, dla papieru matowego - 2 zł.

    copies * PRICES[paper] - nawias kwadratowy odczytuje z obiektu wartość pod kluczem zapisanym w zmiennej: dla paper równego 'glossy' to 1,5, dla 'matte' - 2 (R.5.3). Cena jest stałą const, bo nie zmienia się po obliczeniu.

    Typowe błędy

    PRICES.paper (z kropką) szuka klucza o nazwie dosłownie „paper” i daje undefined - przy kluczu w zmiennej zawsze nawias kwadratowy.

    script.js JavaScript zmienione: 1 linia
    const PRICES = {
        glossy: 1.5,
        matte: 2,
    };
    
    const fileInput = document.getElementById('image');
    const copiesInput = document.getElementById('copies');
    const cartItems = document.getElementById('cart-items');
    
    function addToCart() {
        const copies = Number(copiesInput.value);
        const paper = document.querySelector('input[name="paper"]:checked').value;
        const price = copies * PRICES[paper];
    }
    
    document.getElementById('add-button').addEventListener('click', addToCart);
    Podgląd strony po tym kroku (zamowienie.html)
  28. Krok 28: Nazwa pliku z File API i sprawdzenie danych

    Kryteria:

    R.5.4 Skrypt ustala nazwę pliku np.: indeks = plik.lastIndexOf('\\'); nazwaPliku = plik.substr(indeks+1);

    ok. 3 min
    Polecenie z arkusza

    Ustala nazwę pliku z wartości pobranej z pierwszego pola edycyjnego.

    Pole type="file" ma File API (wspomina o nim ramka „File Input Type” w arkuszu): fileInput.files to lista wybranych plików, a files[0] - pierwszy z nich. Nazwa pliku to file.name - bez wycinania jej ze ścieżki C:\fakepath\7.jpg, którą przeglądarka podstawia w value.

    Zanim cokolwiek trafi do koszyka, warunek sprawdza dane: brak pliku (!file), liczba kopii, która nie jest całkowita, albo mniejsza niż 1 - funkcja kończy się return. Bez tego puste kliknięcie dodawało do koszyka pusty obrazek i „Cena: 0”.

    Klucz R.5.4 podaje lastIndexOf + substr jako przykład („np.”) - ocenia, czy skrypt ustala nazwę pliku. substr jest zresztą w JavaScript oznaczone jako przestarzałe.

    script.js JavaScript zmienione: 4 linie
    const PRICES = {
        glossy: 1.5,
        matte: 2,
    };
    
    const fileInput = document.getElementById('image');
    const copiesInput = document.getElementById('copies');
    const cartItems = document.getElementById('cart-items');
    
    function addToCart() {
        const copies = Number(copiesInput.value);
        const paper = document.querySelector('input[name="paper"]:checked').value;
        const price = copies * PRICES[paper];
        const file = fileInput.files[0];
        if (!file || !Number.isInteger(copies) || copies < 1) {
            return;
        }
    }
    
    document.getElementById('add-button').addEventListener('click', addToCart);
    Podgląd strony po tym kroku (zamowienie.html)
  29. Krok 29: Zamówienie w koszyku: blok z obrazem

    Kryteria:

    R.5.5 Skrypt tworzy element img z ustaloną nazwą pliku i dodaje go do bloku w sekcji prawej

    ok. 3 min
    Polecenie z arkusza

    Tworzy elementy i dodaje je do bloku z sekcji prawej (ilustracja 4): element DOM dla obrazu z ustaloną nazwą pliku.

    Każde zamówienie dostaje własny blok <article> - samodzielną całość z obrazem i dwoma paragrafami. createElement('img') tworzy obraz, image.src = file.name wskazuje plik z folderu strony (zdjęcia leżą obok zamowienie.html), a alt z nazwą pliku opisuje miniaturę dla czytnika ekranu. Obraz trafia do bloku zamówienia, a blok - na koniec koszyka przez append (R.5.5).

    Wypróbuj w podglądzie: wybierz dowolne z dziesięciu zdjęć galerii, wpisz liczbę kopii i kliknij przycisk.

    Typowe błędy

    image.src = fileInput.value podaje ścieżkę z fakepath, z której przeglądarka nie wczyta pliku.

    script.js JavaScript zmienione: 7 linii
    const PRICES = {
        glossy: 1.5,
        matte: 2,
    };
    
    const fileInput = document.getElementById('image');
    const copiesInput = document.getElementById('copies');
    const cartItems = document.getElementById('cart-items');
    
    function addToCart() {
        const copies = Number(copiesInput.value);
        const paper = document.querySelector('input[name="paper"]:checked').value;
        const price = copies * PRICES[paper];
        const file = fileInput.files[0];
        if (!file || !Number.isInteger(copies) || copies < 1) {
            return;
        }
    
        const item = document.createElement('article');
        const image = document.createElement('img');
        image.src = file.name;
        image.alt = file.name;
        item.append(image);
        cartItems.append(item);
    }
    
    document.getElementById('add-button').addEventListener('click', addToCart);
    Podgląd strony po tym kroku (zamowienie.html)
  30. Krok 30: Zamówienie w koszyku: liczba kopii i cena

    Kryteria: , ,

    R.5.6 Skrypt tworzy 2 elementy p z treścią i dodaje je do bloku w sekcji prawej

    R.5.7 Skrypt wyświetla napisy: „Liczba kopii: <kopie>”, gdzie pole <> jest pobrane z kontrolki, „Cena: <cena>”, gdzie pole <> jest wyliczoną ceną

    R.5.8 Po ponownym wypełnieniu kontrolek i kliknięciu przycisku poprzednie zamówienie nie znika, następne jest dodawane pod nim

    ok. 3 min
    Polecenie z arkusza

    Element DOM dla paragrafu z treścią Liczba kopii: <kopie>, gdzie pole <> jest pobrane z kontrolki. Element DOM dla paragrafu z treścią Cena: <cena>, gdzie pole <> jest wyliczoną ceną.

    Dwa paragrafy z treścią z szablonów tekstowych: w odwrotnych apostrofach ${copies} wstawia wartość zmiennej - czytelniej niż sklejanie plusem. Treść ustawia textContent, który zawsze wstawia zwykły tekst (R.5.6, R.5.7). item.append(image, copiesParagraph, priceParagraph) wkłada do bloku zamówienia wszystkie trzy elementy naraz, w tej kolejności.

    Podgląd działa jak na ilustracji 4: trzy zamówienia - np. 10 kopii na matowym (Cena: 20), 7 na matowym (14) i 20 na błyszczącym (30) - stają jedno pod drugim, bo append dokłada na koniec i niczego nie kasuje (R.5.8).

    Typowe błędy

    cartItems.innerHTML = ... zastępuje zawartość koszyka - poprzednie zamówienie znika i R.5.8 przepada.

    script.js JavaScript zmienione: 5 linii
    const PRICES = {
        glossy: 1.5,
        matte: 2,
    };
    
    const fileInput = document.getElementById('image');
    const copiesInput = document.getElementById('copies');
    const cartItems = document.getElementById('cart-items');
    
    function addToCart() {
        const copies = Number(copiesInput.value);
        const paper = document.querySelector('input[name="paper"]:checked').value;
        const price = copies * PRICES[paper];
        const file = fileInput.files[0];
        if (!file || !Number.isInteger(copies) || copies < 1) {
            return;
        }
    
        const item = document.createElement('article');
        const image = document.createElement('img');
        image.src = file.name;
        image.alt = file.name;
        const copiesParagraph = document.createElement('p');
        copiesParagraph.textContent = `Liczba kopii: ${copies}`;
        const priceParagraph = document.createElement('p');
        priceParagraph.textContent = `Cena: ${price}`;
        item.append(image, copiesParagraph, priceParagraph);
        cartItems.append(item);
    }
    
    document.getElementById('add-button').addEventListener('click', addToCart);
    Podgląd strony po tym kroku (zamowienie.html)
  31. Krok 31: Każde zamówienie w osobnym wierszu

    ok. 1 min
    Polecenie z arkusza

    Tworzy elementy i dodaje je do bloku z sekcji prawej (ilustracja 4).

    Miniatura ma float: left, więc podpisy stoją obok niej, jak na ilustracji 4. Gdy jednak podpisy są niższe niż obraz, następna miniatura „wskoczyłaby” obok poprzedniej. Reguła #cart-items article { display: flow-root; } robi z każdego zamówienia blok, który obejmuje swój pływający obraz - kolejne zamówienie zawsze zaczyna się pod poprzednim. To ten sam mechanizm co w galerii, tym razem dla elementów tworzonych przez skrypt.

    style.css CSS zmienione: 4 linie
    :root {
        --color-page: BlanchedAlmond;
        --color-brand: Sienna;
        --color-brand-dark: #4C1900;
        --color-panel: NavajoWhite;
        --color-shadow: DimGray;
    }
    
    * {
        font-family: "Century Gothic", Arial;
    }
    
    body {
        background-color: var(--color-page);
    }
    
    header,
    footer {
        background-color: var(--color-brand);
        color: white;
        padding: 5px;
        text-align: center;
        font-size: 130%;
    }
    
    main {
        display: grid;
        grid-template-columns: 35% 50%;
        grid-template-areas:
            "order nav"
            "order cart";
        column-gap: 2%;
        align-items: start;
    }
    
    #order {
        grid-area: order;
        background-color: var(--color-panel);
        margin: 10px;
        padding: 20px;
        height: 400px;
        box-shadow: 5px 5px 10px var(--color-shadow);
    }
    
    nav {
        grid-area: nav;
        height: 50px;
    }
    
    #cart {
        grid-area: cart;
        height: 420px;
        overflow: auto;
    }
    
    input,
    button {
        margin-top: 20px;
    }
    
    li {
        display: inline-block;
    }
    
    img {
        width: 10%;
        float: left;
    }
    
    #gallery {
        display: flow-root;
    }
    
    a {
        color: var(--color-brand);
        margin-right: 30px;
    }
    
    a:hover {
        color: var(--color-brand-dark);
    }
    
    #cart-items article {
        display: flow-root;
    }
    Podgląd strony po tym kroku (zamowienie.html)
  32. Etap 5: Porównanie z wersją podstawową

    Krok 32 · po tym etapie masz 41 z 41 kryteriów (pkt)

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

    ok. 3 min
    Polecenie z arkusza

    Ocenie będzie podlegać 5 rezultatów: operacje na bazie danych, zawartość witryny internetowej, działanie witryny internetowej, styl CSS witryny internetowej, skrypt.

    wersja podstawowata wersja
    blok głównyfloat: left na trzech blokach, clear: both w stopcegrid-template-areas
    galeria z pływającymi obrazamidomyka ją clear w następnym blokudisplay: flow-root
    przyciskonclick="addToCart()"addEventListener w skrypcie
    rodzaj papieru i cenachecked + if/else:checked + obiekt PRICES
    nazwa plikulastIndexOf + substr ze ścieżkifiles[0].name
    zamówienie w koszykutrzy elementy wprost w blokublok <article> na zamówienie

    float dla obrazów i inline-block dla elementów listy zostają w obu wersjach, bo arkusz nazywa te sposoby wyświetlania wprost.

    Na egzaminie: wersja podstawowa idzie za podpowiedziami z tabeli 1 i z klucza (onclick, lastIndexOf, substr), więc łatwiej ją napisać bez dokumentacji. Ta wersja jest krótsza w skrypcie i odporna na puste pola - wybierz ją, jeśli querySelector z :checked i File API pisze się z pamięci.

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

  33. Etap 6: Oddanie pracy

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

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

    ok. 5 min
    Polecenie z arkusza

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

    Na płycie: zdjęcia 1.jpg - 10.jpg, import.png, kw1.png - kw4.png, kwerendy.txt, przeglądarka.txt, regulamin.html, uslugi.html, zamowienie.html, style.css i script.js. Po nagraniu sprawdź odczyt płyty.

    przeglądarka.txt Tekst nowy plik
    Google Chrome

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

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.