Przejdź do głównej treści
INF.03-02-24.06-SGCzerwiec 2024 · 34 kroki · ok. 77 min · 40 kryteriów z klucza

Rozwiązanie krok po kroku: Prototyp front-endu chatu (Nowoczesny kod (grid, addEventListener))

To drugi sposób rozwiązania tego samego arkusza. Wynik jest ten sam - baza chat z pięcioma zapytaniami, przycięte zdjęcie i strona chat.html z oknem rozmowy, do którego skrypt dokłada wiadomości Jolanty i losowe odpowiedzi Krzysztofa - i tak samo spełnia każde kryterium klucza. Inna jest droga:

  • siatka CSS na całej stronie: grid-template-areas stawia blok boczny obok banera i bloku głównego, a szerokości 80% i 20% stoją w grid-template-columns (klucz R.4.4 przyjmuje je wprost). Dzięki temu kod HTML ma kolejność treści - baner, blok główny, blok boczny, stopka - a nie kolejność, której wymaga float,
  • kolory w zmiennych CSS,
  • wiadomość wysyłana formularzem: zdarzenie submit obsługuje kliknięcie „Wyślij” i klawisz Enter jednym kodem, a puste pole zatrzymuje przeglądarka,
  • szablon wypowiedzi w HTML-u (<template>): skrypt klonuje gotowy blok zamiast składać go z createElement, a dane autorów są w jednym obiekcie,
  • współczesny JavaScript: const, querySelector, textContent, addEventListener od początku,
  • zapytanie 1 bez przepisywania liczby: identyfikator dla wiersza logowania baza bierze z relacji sama.

Na końcu krótkie porównanie z wersją podstawową (zakładka obok). Baza, zdjęcia i teksty odpowiedzi pochodzą z prawdziwego archiwum pliki2, zapytania zostały wykonane na tej bazie w MariaDB z XAMPP-a, a działanie skryptu jest sprawdzone w prawdziwej przeglądarce Chrome.

Jak liczą się punkty. Klucz ma 40 kryteriów (R.1.1 - R.5.9), 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-8 · po tym etapie masz 7 z 40 kryteriów (pkt)

    Krok 1: Baza danych chat

    ok. 2 min
    Polecenie z arkusza

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

    Archiwum pliki2.zip rozpakuj hasłem ChaT_Ch@t (wielkość liter ma znaczenie) do folderu z numerem zdającego. W phpMyAdmin Nowa → chat, porównywanie utf8_unicode_ci, albo zapytaniem:

    CREATE DATABASE chat CHARACTER SET utf8 COLLATE utf8_unicode_ci;
    
  2. Krok 2: Import tabel z pliku baza.sql

    ok. 2 min
    Polecenie z arkusza

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

    Import → baza.sql. Dwie tabele z obrazu 1: logowanie (nick i hasło, na razie 2 wiersze) i uczestnicy (3 osoby). Pole logowanie_id uczestnika wskazuje jego wiersz w logowanie - Jeremi Kowalski ma już logowanie_id = 3, choć takiego wiersza jeszcze nie ma.

  3. Krok 3: Zrzut ekranu import.png

    Kryteria:

    R.1.1 Wykonano import tabel do bazy danych chat czynność udokumentowano plikiem o nazwie import w formacie PNG

    ok. 2 min
    Polecenie z arkusza

    Wykonaj zrzut ekranu po imporcie. Zrzut zapisz w formacie PNG i nazwij 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ń. Uwaga na formaty: import w PNG, zrzuty zapytań w JPEG (kw1.jpg - kw5.jpg).

    Typowe błędy

    Wykadrowany zrzut albo zrzut bez paska zadań nie jest oceniany (uwaga w kluczu przy R.1).

  4. Krok 4: Zapytanie 1: dane logowania Jeremiego

    Kryteria: ,

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

    R.1.3 Wykonano zapytanie 1 wstawiające do tabeli logowanie nick "Jeremi" z hasłem „Jer123” o id=3. W pliku z kwerendami lub na zrzucie istnieje zapis np.: INSERT INTO `logowanie`(`id`, `nick`, `haslo`) VALUES (3, 'Jeremi','Jer123'); oraz wynik działania udokumentowano zrzutem, na którym jest widoczne pomyślnie wykonane zapytanie lub tabela ze wstawionym wierszem o id=3

    ok. 3 min
    Polecenie z arkusza

    Zapytanie 1: wstawiające do tabeli logowanie nick „Jeremi” z hasłem „Jer123”. Wstawianemu wierszowi należy nadać identyfikator, odpowiadający wartości klucza obcego dla wiersza z danymi „Jeremi Kowalski” z tabeli uczestnicy

    Zamiast odczytać liczbę 3 w phpMyAdmin i przepisać ją do VALUES, zapytanie bierze ją z relacji samo - INSERT ... SELECT:

    • INSERT INTO logowanie (id, nick, haslo) - do których kolumn,
    • SELECT logowanie_id, 'Jeremi', 'Jer123' FROM uczestnicy WHERE ... - wartości: klucz obcy z wiersza Jeremiego Kowalskiego i dwa stałe napisy.

    Dokładnie to mówi polecenie: identyfikator „odpowiadający wartości klucza obcego” - i nie da się go pomylić przy przepisywaniu. Wynik: w logowanie wiersz 3, Jeremi, Jer123, jak w kluczu (R.1.3 podaje wariant z VALUES (3, ...) jako przykład). Zapytania zapisz w kwerendy.txt, zrzut jako kw1.jpg.

    Typowe błędy

    Gdyby w tabeli było dwóch Jeremich Kowalskich, SELECT zwróciłby dwa wiersze i wstawienie drugiego skończyłoby się błędem duplikatu klucza - warunek WHERE musi wskazać jedną osobę.

    kwerendy.txt Tekst nowy plik
    -- Zapytanie 1
    INSERT INTO logowanie (id, nick, haslo)
    SELECT logowanie_id, 'Jeremi', 'Jer123'
    FROM uczestnicy
    WHERE imie = 'Jeremi' AND nazwisko = 'Kowalski';
    Podgląd strony po tym kroku
    Podgląd strony po kroku 4
  5. Krok 5: Zapytanie 2: średni rok urodzenia zaokrąglony w dół

    Kryteria:

    R.1.4 Wykonano zapytanie 2 obliczające średni rok urodzenia uczestników. Wybrana kolumna powinna nosić nazwę "Średni rok urodzenia", a obliczony wynik powinien być zaokrąglony do najbliższej mniejszej liczby całkowitej. W pliku z kwerendami lub na zrzucie istnieje zapis np.: SELECT FLOOR(AVG(rokUr)) AS "Średni rok urodzenia" FROM uczestnicy; oraz wynik działania udokumentowano zrzutem, na którym jest widoczna wartość całkowita 2001, nazwa kolumny (alias) "Średni rok urodzenia"

    ok. 2 min
    Polecenie z arkusza

    Zapytanie 2: obliczające średni rok urodzenia uczestników. Wybrana kolumna powinna mieć nazwę (alias) „Średni rok urodzenia”, a obliczony wynik powinien być zaokrąglony w dół do liczby całkowitej

    AVG(rokUr) - średnia, FLOOR(...) - zaokrąglenie w dół (tabela 3 arkusza; ROUND zaokrągla do najbliższej, CEIL w górę). Alias ze spacjami w odwróconych apostrofach - w MySQL i MariaDB to znak dla nazw. Wynik: 2001. Zrzut: kw2.jpg.

    Typowe błędy

    Alias bez cudzysłowu (AS Średni rok urodzenia) to błąd składni.

    kwerendy.txt Tekst zmienione: 4 linie
    -- Zapytanie 1
    INSERT INTO logowanie (id, nick, haslo)
    SELECT logowanie_id, 'Jeremi', 'Jer123'
    FROM uczestnicy
    WHERE imie = 'Jeremi' AND nazwisko = 'Kowalski';
    
    -- Zapytanie 2
    SELECT FLOOR(AVG(rokUr)) AS `Średni rok urodzenia`
    FROM uczestnicy;
    Podgląd strony po tym kroku
    Podgląd strony po kroku 5
  6. Krok 6: Zapytanie 3: uczestnicy na J z danymi logowania

    Kryteria:

    R.1.5 Wykonano zapytanie 3 wybierające jedynie imię i nazwisko uczestnika oraz odpowiadające mu nick i hasło dla imion rozpoczynających się literą J. W pliku z kwerendami lub na zrzucie istnieje zapis np.: SELECT imie, nazwisko, nick, haslo FROM uczestnicy JOIN logowanie ON logowanie_id = logowanie.id WHERE imie LIKE "J%"; (możliwe również INNER JOIN lub bez JOIN i porównanie kluczy po WHERE) oraz wynik działania udokumentowano zrzutem, na którym są widoczne dokładnie 2 rekordy: Jolanta Nowak Jolka Jol123; Jeremi Kowalski Jeremi Jer123. Jeżeli nie wykonano R.1.3 jest widoczny dokładnie 1 rekord dla Jolanty

    ok. 2 min
    Polecenie z arkusza

    Zapytanie 3: wybierające jedynie imię i nazwisko uczestnika oraz odpowiadające mu nick i hasło dla imion rozpoczynających się literą J. Należy posłużyć się relacją

    Aliasy tabel u (uczestnicy) i l (logowanie): przy każdej kolumnie widać, skąd pochodzi, a ON l.id = u.logowanie_id pokazuje parę kluczy relacji. LIKE 'J%' - imię zaczyna się od J. Wynik: Jolanta Nowak Jolka Jol123 i Jeremi Kowalski Jeremi Jer123 - Jeremi jest w wyniku tylko dzięki zapytaniu 1. Zrzut: kw3.jpg.

    kwerendy.txt Tekst zmienione: 6 linii
    -- Zapytanie 1
    INSERT INTO logowanie (id, nick, haslo)
    SELECT logowanie_id, 'Jeremi', 'Jer123'
    FROM uczestnicy
    WHERE imie = 'Jeremi' AND nazwisko = 'Kowalski';
    
    -- Zapytanie 2
    SELECT FLOOR(AVG(rokUr)) AS `Średni rok urodzenia`
    FROM uczestnicy;
    
    -- Zapytanie 3
    SELECT u.imie, u.nazwisko, l.nick, l.haslo
    FROM uczestnicy AS u
    JOIN logowanie AS l ON l.id = u.logowanie_id
    WHERE u.imie LIKE 'J%';
    Podgląd strony po tym kroku
    Podgląd strony po kroku 6
  7. Krok 7: Zapytanie 4: użytkownik uczestnik

    Kryteria:

    R.1.6 Wykonano zapytanie 4 tworzące użytkownika uczestnik na localhost z hasłem Ucz123& W pliku z kwerendami lub na zrzucie istnieje zapis np.: CREATE USER uczestnik@localhost IDENTIFIED BY "Ucz123&" oraz wynik działania udokumentowano zrzutem, na którym widoczne jest poprawnie wykonane zapytanie

    ok. 2 min
    Polecenie z arkusza

    Zapytanie 4: tworzące użytkownika uczestnik na localhost z hasłem Ucz123&

    CREATE USER 'uczestnik'@'localhost' - konto serwera bazy (nie wiersz tabeli): nazwa i host, z którego wolno się logować; IDENTIFIED BY - hasło. Zapytanie nie zwraca wierszy, zrzut kw4.jpg pokazuje komunikat o wykonaniu.

    kwerendy.txt Tekst zmienione: 3 linie
    -- Zapytanie 1
    INSERT INTO logowanie (id, nick, haslo)
    SELECT logowanie_id, 'Jeremi', 'Jer123'
    FROM uczestnicy
    WHERE imie = 'Jeremi' AND nazwisko = 'Kowalski';
    
    -- Zapytanie 2
    SELECT FLOOR(AVG(rokUr)) AS `Średni rok urodzenia`
    FROM uczestnicy;
    
    -- Zapytanie 3
    SELECT u.imie, u.nazwisko, l.nick, l.haslo
    FROM uczestnicy AS u
    JOIN logowanie AS l ON l.id = u.logowanie_id
    WHERE u.imie LIKE 'J%';
    
    -- Zapytanie 4
    CREATE USER 'uczestnik'@'localhost' IDENTIFIED BY 'Ucz123&';
    Podgląd strony po tym kroku
    Podgląd strony po kroku 7
  8. Krok 8: Zapytanie 5: prawa SELECT i UPDATE do tabeli uczestnicy

    Kryteria:

    R.1.7 Wykonano zapytanie 5 nadające utworzonemu użytkownikowi prawa do wybierania i aktualizacji danych jedynie dla tabeli uczestnicy . W pliku z kwerendami lub na zrzucie istnieje zapis np.: GRANT SELECT, UPDATE ON chat.uczestnicy TO uczestnik@localhost; oraz wynik działania udokumentowano zrzutem, na którym widoczne jest poprawnie wykonane zapytanie lub użytkownik z przydzielonymi prawami SELECT, UPDATE

    ok. 2 min
    Polecenie z arkusza

    Zapytanie 5: nadające utworzonemu użytkownikowi prawa do wybierania i aktualizacji danych jedynie dla tabeli uczestnicy

    GRANT SELECT, UPDATE ON chat.uczestnicy - tylko te dwa prawa i tylko ta tabela (chat.* dałoby dostęp do wszystkich tabel bazy). To zasada najmniejszych uprawnień: konto aplikacji dostaje tyle, ile potrzebuje, więc nawet przejęte nie odczyta haseł z logowanie. Sprawdzone: SHOW GRANTS pokazuje GRANT SELECT, UPDATE ON chat.uczestnicy. Zrzut: kw5.jpg.

    kwerendy.txt Tekst zmienione: 3 linie
    -- Zapytanie 1
    INSERT INTO logowanie (id, nick, haslo)
    SELECT logowanie_id, 'Jeremi', 'Jer123'
    FROM uczestnicy
    WHERE imie = 'Jeremi' AND nazwisko = 'Kowalski';
    
    -- Zapytanie 2
    SELECT FLOOR(AVG(rokUr)) AS `Średni rok urodzenia`
    FROM uczestnicy;
    
    -- Zapytanie 3
    SELECT u.imie, u.nazwisko, l.nick, l.haslo
    FROM uczestnicy AS u
    JOIN logowanie AS l ON l.id = u.logowanie_id
    WHERE u.imie LIKE 'J%';
    
    -- Zapytanie 4
    CREATE USER 'uczestnik'@'localhost' IDENTIFIED BY 'Ucz123&';
    
    -- Zapytanie 5
    GRANT SELECT, UPDATE ON chat.uczestnicy TO 'uczestnik'@'localhost';
    Podgląd strony po tym kroku
    Podgląd strony po kroku 8
  9. Etap 2: Grafika i strona HTML

    Kroki 9-18 · po tym etapie masz 18 z 40 kryteriów (pkt)

    Krok 9: Jolka.jpg: kadr do kwadratu i skala 70 × 70 px

    Kryteria:

    R.2.1 Plik Jolka.jpg wykadrowano do kwadratu, tak, aby była widoczna cała twarz. Wymiary grafiki to 70 px na 70 px

    ok. 4 min
    Polecenie z arkusza

    Grafika Jolka.jpg przygotowana, podobnie jak Krzysiek.jpg. Grafikę Jolka.jpg należy wykadrować do kwadratu tak, aby była widoczna tylko cała twarz. Następnie należy przeskalować dokładnie do rozmiaru 70 px na 70 px.

    Grafika jest taka sama jak w wersji podstawowej. Wzorem jest Krzysiek.jpg: sama twarz, 70 × 70 px. W GIMP-ie na Jolka.jpg (640 × 427):

    1. Zaznaczanie prostokątne z opcją Stałe: Proporcje 1:1, obrys od czoła do brody, Obraz → Przytnij do zaznaczenia (tu kwadrat 180 × 180 od punktu 165, 70),
    2. Obraz → Skaluj obraz: 70 × 70 - kadr jest kwadratem, więc twarz się nie spłaszcza,
    3. Plik → Nadpisz Jolka.jpg.

    Obraz obok to sam kadr przed skalowaniem.

    Typowe błędy

    Skalowanie całego zdjęcia do 70 × 70 bez kadrowania spłaszcza postać i nie pokazuje „tylko twarzy”.

    Podgląd strony po tym kroku
    Podgląd strony po kroku 9
  10. Krok 10: Plik chat.html: HTML5, język, kodowanie

    Kryteria:

    R.2.2 Witrynę internetową zapisano w pliku chat.html oraz zapisano jawnie standard kodowania polskich znaków i deklarację HTML5 <!DOCTYPE HTML> i zadeklarowano język witryny polski np.<HTML lang="pl">. Strona zawiera sekcje head i body

    ok. 2 min
    Polecenie z arkusza

    Składa się ze strony o nazwie chat.html zapisanej w języku HTML5. Ustawiony język zawartości strony na polski. Jawnie zastosowany właściwy standard kodowania polskich znaków

    <!DOCTYPE html>, <html lang="pl">, <meta charset="UTF-8"> jako pierwszy element <head> (R.2.2). Strona nie ma PHP, więc wystarczy rozszerzenie .html - plik otwiera się w przeglądarce prosto z folderu.

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

    Kryteria: ,

    R.2.3 Nadano tytuł stronie: Chat

    R.3.5 Strona zawiera działające połączenie z zewnętrznym arkuszem stylów o nazwie styl.css , formatowanie CSS pochodzi jedynie z tego arkusza Uwaga: kryterium to nie jest spełnione również, gdy zdający źle przypisał co najmniej jeden selektor do bloku (np. kropka zamiast #)

    ok. 1 min
    Polecenie z arkusza

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

    <title> (R.2.3), <link> do styl.css (R.3.5) i viewport, żeby telefon nie pomniejszał strony.

    chat.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>Chat</title>
        <link rel="stylesheet" href="styl.css">
    </head>
    <body>
    </body>
    </html>
    Podgląd strony po tym kroku (chat.html)
  12. Krok 12: Bloki semantyczne w kolejności treści

    Kryteria:

    R.2.4 Układ strony zdefiniowano dzieląc ją na bloki: banera, boczny, główny oraz stopkę. Zastosowano semantyczne znaczniki sekcji HTML5 zgodnie z ich znaczeniem, w tym do części nagłówkowej - header, stopki - footer, dopuszcza się zagnieżdżenie bloków div w znacznikach semantycznych

    ok. 2 min
    Polecenie z arkusza

    Podział strony na baner i pod nim blok główny, obok obu bloków blok boczny oraz na dole stopka. Podział zrealizowany wyłącznie za pomocą semantycznych znaczników sekcji języka HTML5 tak, aby po uruchomieniu w przeglądarce wygląd układu bloków był zgodny z obrazem 2

    Baner header, blok główny main, blok boczny aside, stopka footer (R.2.4). Kolejność w kodzie to kolejność treści: najpierw okno rozmowy, potem lista chatów i uczestników. W tej kolejności czyta stronę czytnik ekranu i tak ułoży ją telefon. W wersji podstawowej aside musiał stać przed main, bo tego wymagało ułożenie bloków przez float; siatka CSS umieści bloki niezależnie od kolejności w kodzie.

    chat.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>Chat</title>
        <link rel="stylesheet" href="styl.css">
    </head>
    <body>
        <header>
        </header>
        <main>
        </main>
        <aside>
        </aside>
        <footer>
        </footer>
    </body>
    </html>
    Podgląd strony po tym kroku (chat.html)
  13. Krok 13: Baner i blok boczny: listy chatów

    Kryteria:

    R.2.8 W bloku bocznym umieszczono jedną listę punktowaną o dwóch elementach, z czego drugi zagnieżdża jedną listę numerowaną o czterech elementach. Zastosowano znaczniki <ul>, <ol>, <li>, zagnieżdżenie wewnątrz znacznika <li>

    ok. 2 min
    Polecenie z arkusza

    Zawartość banera: nagłówek drugiego stopnia o treści „Chat”. Zawartość bloku bocznego: Nagłówek trzeciego stopnia o treści „Chaty”. Lista punktowana z elementami: „Ogólny” , „Tematyczne”. Drugi element zagnieżdża listę numerowaną z elementami „Turystyczny”, „Żeglarski”, „Filatelistyczny”, „Hodowla zwierzątek domowych”

    <h2> w banerze, <h3> i <ul> w bloku bocznym. „Zagnieżdża” znaczy: <ol> stoi wewnątrz <li> z tekstem „Tematyczne”, przed jego </li> (R.2.8). Lista <ol> po </li> byłaby błędem - w <ul> mogą stać tylko elementy <li>.

    chat.html HTML zmienione: 13 linii
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <title>Chat</title>
        <link rel="stylesheet" href="styl.css">
    </head>
    <body>
        <header>
            <h2>Chat</h2>
        </header>
        <main>
        </main>
        <aside>
            <h3>Chaty</h3>
            <ul>
                <li>Ogólny</li>
                <li>Tematyczne
                    <ol>
                        <li>Turystyczny</li>
                        <li>Żeglarski</li>
                        <li>Filatelistyczny</li>
                        <li>Hodowla zwierzątek domowych</li>
                    </ol>
                </li>
            </ul>
        </aside>
        <footer>
        </footer>
    </body>
    </html>
    Podgląd strony po tym kroku (chat.html)
  14. Krok 14: Blok boczny: uczestnicy

    Kryteria:

    R.2.7 W bloku bocznym obraz Jolka.jpg ma tekst alternatywny Jolanta Nowak a obraz Krzysiek.jpg ma tekst alternatywny Krzysztof Łukasiński

    ok. 2 min
    Polecenie z arkusza

    Nagłówek trzeciego stopnia o treści „Uczestnicy”. Paragraf (akapit) o treści „Jolanta Nowak”. Obraz Jolka.jpg z tekstem alternatywnym "Jolanta Nowak". Paragraf o treści „Krzysztof Łukasiński”. Obraz Krzysiek.jpg z tekstem alternatywnym "Krzysztof Łukasiński".

    Drugi <h3>, dwa akapity z imionami i dwa zdjęcia z tekstem alternatywnym (R.2.7). Nazwy plików z wielką literą, jak w archiwum - na serwerze wielkość liter w nazwach plików ma znaczenie.

    chat.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>Chat</title>
        <link rel="stylesheet" href="styl.css">
    </head>
    <body>
        <header>
            <h2>Chat</h2>
        </header>
        <main>
        </main>
        <aside>
            <h3>Chaty</h3>
            <ul>
                <li>Ogólny</li>
                <li>Tematyczne
                    <ol>
                        <li>Turystyczny</li>
                        <li>Żeglarski</li>
                        <li>Filatelistyczny</li>
                        <li>Hodowla zwierzątek domowych</li>
                    </ol>
                </li>
            </ul>
            <h3>Uczestnicy</h3>
            <p>Jolanta Nowak</p>
            <img src="Jolka.jpg" alt="Jolanta Nowak">
            <p>Krzysztof Łukasiński</p>
            <img src="Krzysiek.jpg" alt="Krzysztof Łukasiński">
        </aside>
        <footer>
        </footer>
    </body>
    </html>
    Podgląd strony po tym kroku (chat.html)
  15. Krok 15: Blok chatu z dwiema wypowiedziami

    Kryteria: ,

    R.2.6 W oknie chatu oraz bloku bocznym umieszczono dwa obrazy Jolka.jpg i Krzysiek.jpg

    R.3.2 W bloku głównym zdefiniowano blok okna chatu zawierający dwa bloki

    ok. 3 min
    Polecenie z arkusza

    Zawartość bloku głównego: Blok chatu zawierający: Jeden blok z wypowiedzią Jolanty: Obraz Jolka.jpg i paragraf: „Cześć idziesz jutro do kina?”. Jeden blok z wypowiedzią Krzysztofa: Obraz Krzysiek.jpg i paragraf: „Tak! A ty?”

    • okno rozmowy to <div id="chat"> - jedno na stronie, więc identyfikator; po nim skrypt znajdzie okno (R.3.2),
    • wypowiedź to <div> z dwiema klasami: message (wspólny wygląd) i jolanta albo krzysztof (kolor i strona zdjęcia). Wypowiedzi będzie wiele, więc klasy, nie identyfikatory - klucz R.4.9 wprost uznaje identyfikator za błąd,
    • w obu blokach zdjęcie stoi przed akapitem; na prawą stronę zdjęcie Krzysztofa przeniesie CSS.

    Zdjęcia obojga są teraz i w oknie chatu, i w bloku bocznym (R.2.6).

    chat.html HTML zmienione: 10 linii
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <title>Chat</title>
        <link rel="stylesheet" href="styl.css">
    </head>
    <body>
        <header>
            <h2>Chat</h2>
        </header>
        <main>
            <div id="chat">
                <div class="message jolanta">
                    <img src="Jolka.jpg" alt="Jolanta Nowak">
                    <p>Cześć idziesz jutro do kina?</p>
                </div>
                <div class="message krzysztof">
                    <img src="Krzysiek.jpg" alt="Krzysztof Łukasiński">
                    <p>Tak! A ty?</p>
                </div>
            </div>
        </main>
        <aside>
            <h3>Chaty</h3>
            <ul>
                <li>Ogólny</li>
                <li>Tematyczne
                    <ol>
                        <li>Turystyczny</li>
                        <li>Żeglarski</li>
                        <li>Filatelistyczny</li>
                        <li>Hodowla zwierzątek domowych</li>
                    </ol>
                </li>
            </ul>
            <h3>Uczestnicy</h3>
            <p>Jolanta Nowak</p>
            <img src="Jolka.jpg" alt="Jolanta Nowak">
            <p>Krzysztof Łukasiński</p>
            <img src="Krzysiek.jpg" alt="Krzysztof Łukasiński">
        </aside>
        <footer>
        </footer>
    </body>
    </html>
    Podgląd strony po tym kroku (chat.html)
  16. Krok 16: Formularz: napis, pole edycyjne i przycisk „Wyślij”

    ok. 3 min
    Polecenie z arkusza

    Obok siebie: napis „Wpisz wiadomość:”, pole edycyjne, przycisk o treści „Wyślij”. Kliknięcie przycisku powoduje wywołanie funkcji skryptu

    Trzy elementy w formularzu <form id="message-form">:

    • <label for="text"> powiązany z polem - kliknięcie napisu ustawia kursor w polu,
    • pole z required (pustego formularza przeglądarka nie wyśle i sama pokaże podpowiedź) i autocomplete="off" (przeglądarka nie podsuwa starych wiadomości),
    • <button> w formularzu jest przyciskiem wysyłającym.

    Po co formularz, skoro nic nie idzie na serwer? Bo formularz ma zdarzenie submit, które zachodzi i po kliknięciu „Wyślij”, i po naciśnięciu Enter w polu - skrypt obsłuży oba jednym kodem. Elementy są liniowe, więc stoją obok siebie bez CSS.

    chat.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>Chat</title>
        <link rel="stylesheet" href="styl.css">
    </head>
    <body>
        <header>
            <h2>Chat</h2>
        </header>
        <main>
            <div id="chat">
                <div class="message jolanta">
                    <img src="Jolka.jpg" alt="Jolanta Nowak">
                    <p>Cześć idziesz jutro do kina?</p>
                </div>
                <div class="message krzysztof">
                    <img src="Krzysiek.jpg" alt="Krzysztof Łukasiński">
                    <p>Tak! A ty?</p>
                </div>
            </div>
            <form id="message-form">
                <label for="text">Wpisz wiadomość:</label>
                <input type="text" id="text" name="text" required autocomplete="off">
                <button>Wyślij</button>
            </form>
        </main>
        <aside>
            <h3>Chaty</h3>
            <ul>
                <li>Ogólny</li>
                <li>Tematyczne
                    <ol>
                        <li>Turystyczny</li>
                        <li>Żeglarski</li>
                        <li>Filatelistyczny</li>
                        <li>Hodowla zwierzątek domowych</li>
                    </ol>
                </li>
            </ul>
            <h3>Uczestnicy</h3>
            <p>Jolanta Nowak</p>
            <img src="Jolka.jpg" alt="Jolanta Nowak">
            <p>Krzysztof Łukasiński</p>
            <img src="Krzysiek.jpg" alt="Krzysztof Łukasiński">
        </aside>
        <footer>
        </footer>
    </body>
    </html>
    Podgląd strony po tym kroku (chat.html)
  17. Krok 17: Przycisk „Generuj losową odpowiedź”

    Kryteria:

    R.2.9 W bloku głównym umieszczono pole edycyjne oraz przyciski: "Wyślij" i "Generuj losową odpowiedź"

    ok. 1 min
    Polecenie z arkusza

    Poniżej: przycisk o treści: „Generuj losową odpowiedź”. Kliknięcie przycisku powoduje wywołanie funkcji skryptu

    Przycisk stoi poza formularzem, a formularz jest elementem blokowym - więc przycisk trafia do nowej linii bez <br>. type="button" mówi jasno, że niczego nie wysyła; identyfikator generate pozwoli podpiąć do niego funkcję. Blok główny ma już pole i oba przyciski (R.2.9).

    chat.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>Chat</title>
        <link rel="stylesheet" href="styl.css">
    </head>
    <body>
        <header>
            <h2>Chat</h2>
        </header>
        <main>
            <div id="chat">
                <div class="message jolanta">
                    <img src="Jolka.jpg" alt="Jolanta Nowak">
                    <p>Cześć idziesz jutro do kina?</p>
                </div>
                <div class="message krzysztof">
                    <img src="Krzysiek.jpg" alt="Krzysztof Łukasiński">
                    <p>Tak! A ty?</p>
                </div>
            </div>
            <form id="message-form">
                <label for="text">Wpisz wiadomość:</label>
                <input type="text" id="text" name="text" required autocomplete="off">
                <button>Wyślij</button>
            </form>
            <button type="button" id="generate">Generuj losową odpowiedź</button>
        </main>
        <aside>
            <h3>Chaty</h3>
            <ul>
                <li>Ogólny</li>
                <li>Tematyczne
                    <ol>
                        <li>Turystyczny</li>
                        <li>Żeglarski</li>
                        <li>Filatelistyczny</li>
                        <li>Hodowla zwierzątek domowych</li>
                    </ol>
                </li>
            </ul>
            <h3>Uczestnicy</h3>
            <p>Jolanta Nowak</p>
            <img src="Jolka.jpg" alt="Jolanta Nowak">
            <p>Krzysztof Łukasiński</p>
            <img src="Krzysiek.jpg" alt="Krzysztof Łukasiński">
        </aside>
        <footer>
        </footer>
    </body>
    </html>
    Podgląd strony po tym kroku (chat.html)
  18. Krok 18: Stopka z autorem

    Kryteria:

    R.2.5 W bloku banera zapisano nagłówek h2 "Chat", w bloku bocznym h3: "Chaty" i "Uczestnicy", poniżej dwa paragrafy, w bloku stopki nagłówek h5: "Chat wykonał: " z numerem zdającego. W oknie chatu zapisano dwa paragrafy

    ok. 1 min
    Polecenie z arkusza

    Zawartość stopki: nagłówek piątego stopnia o treści: „Chat wykonał: ”, dalej wstawiony numer zdającego.

    Tym razem nagłówek <h5>, nie akapit. Na egzaminie po dwukropku wpisujesz swój numer zdającego (PESEL), tu stoi autor rozwiązania. Komplet nagłówków i akapitów to R.2.5.

    chat.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>Chat</title>
        <link rel="stylesheet" href="styl.css">
    </head>
    <body>
        <header>
            <h2>Chat</h2>
        </header>
        <main>
            <div id="chat">
                <div class="message jolanta">
                    <img src="Jolka.jpg" alt="Jolanta Nowak">
                    <p>Cześć idziesz jutro do kina?</p>
                </div>
                <div class="message krzysztof">
                    <img src="Krzysiek.jpg" alt="Krzysztof Łukasiński">
                    <p>Tak! A ty?</p>
                </div>
            </div>
            <form id="message-form">
                <label for="text">Wpisz wiadomość:</label>
                <input type="text" id="text" name="text" required autocomplete="off">
                <button>Wyślij</button>
            </form>
            <button type="button" id="generate">Generuj losową odpowiedź</button>
        </main>
        <aside>
            <h3>Chaty</h3>
            <ul>
                <li>Ogólny</li>
                <li>Tematyczne
                    <ol>
                        <li>Turystyczny</li>
                        <li>Żeglarski</li>
                        <li>Filatelistyczny</li>
                        <li>Hodowla zwierzątek domowych</li>
                    </ol>
                </li>
            </ul>
            <h3>Uczestnicy</h3>
            <p>Jolanta Nowak</p>
            <img src="Jolka.jpg" alt="Jolanta Nowak">
            <p>Krzysztof Łukasiński</p>
            <img src="Krzysiek.jpg" alt="Krzysztof Łukasiński">
        </aside>
        <footer>
            <h5>Chat wykonał: Bartosz Bryniarski</h5>
        </footer>
    </body>
    </html>
    Podgląd strony po tym kroku (chat.html)
  19. Etap 3: Styl CSS

    Kroki 19-26 · po tym etapie masz 30 z 40 kryteriów (pkt)

    Krok 19: Plik styl.css: zmienne i krój czcionki

    Kryteria:

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

    ok. 1 min
    Polecenie z arkusza

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

    Kolory z arkusza w zmiennych CSS na :root - Tomato występuje w banerze, stopce, przyciskach i ramce okna chatu. Selektor * z Garamondem i zapasowym krojem szeryfowym (Garamond jest szeryfowy); krój dostają też przyciski i pole, które nie dziedziczą go po <body> (R.4.1).

    styl.css CSS nowy plik
    :root {
        --tomato: Tomato;
        --coral: Coral;
        --border-dark: DarkRed;
    }
    
    * {
        font-family: Garamond, serif;
    }
    Podgląd strony po tym kroku (chat.html)
  20. Krok 20: Układ bloków na siatce

    Kryteria:

    R.3.1 Układ bloków po uruchomieniu strony w przeglądarce jest zgodny z obrazem 2 w arkuszu egzaminacyjnym, nie należy brać pod uwagę zawartości bloku głównego (prawidłowo zastosowano właściwości CSS decydujące o układzie strony, np. float i clear albo display flex albo grid)

    ok. 3 min
    Polecenie z arkusza

    Podział strony na baner i pod nim blok główny, obok obu bloków blok boczny oraz na dole stopka. Podział zrealizowany wyłącznie za pomocą semantycznych znaczników sekcji języka HTML5 tak, aby po uruchomieniu w przeglądarce wygląd układu bloków był zgodny z obrazem 2

    grid-template-areas rysuje obraz 2 słowami: w lewej kolumnie baner, pod nim blok główny, w prawej - blok boczny w obu wierszach („obok obu bloków”), na dole stopka na całą szerokość (R.3.1). grid-template-columns: 80% 20% to od razu szerokości (klucz R.4.4 przyjmuje je w tej postaci), a align-items: start - bloki mają swoje wysokości z arkusza, zamiast rozciągać się do wiersza.

    Kolejność bloków w kodzie nie ma tu znaczenia: aside stoi w HTML-u po main, a i tak trafia obok banera.

    styl.css CSS zmienione: 26 linii
    :root {
        --tomato: Tomato;
        --coral: Coral;
        --border-dark: DarkRed;
    }
    
    * {
        font-family: Garamond, serif;
    }
    
    body {
        display: grid;
        grid-template-columns: 80% 20%;
        grid-template-areas:
            "header aside"
            "main   aside"
            "footer footer";
        align-items: start;
    }
    
    header {
        grid-area: header;
    }
    
    main {
        grid-area: main;
    }
    
    aside {
        grid-area: aside;
    }
    
    footer {
        grid-area: footer;
    }
    Podgląd strony po tym kroku (chat.html)
  21. Krok 21: Baner, blok główny, blok boczny i stopka

    ok. 3 min
    Polecenie z arkusza

    Dla banera: kolor tła Tomato, biały kolor czcionki, wyrównanie tekstu do środka, szerokość 80%, wysokość 80 px. Dla bloku głównego: szerokość 80%, wysokość 600 px. Dla bloku bocznego: kolor tła Coral, szerokość 20%, wysokość 680 px. Dla stopki: kolor tła Tomato, biały kolor czcionki, wyrównanie tekstu do środka.

    Cechy z arkusza dopisane do reguł z polami siatki. Szerokości 80% i 20% dają kolumny - dodatkowe width: 80% liczyłoby się od kolumny. 680 px bloku bocznego = 80 px banera + 600 px bloku głównego, więc blok boczny sięga od góry banera do dołu bloku głównego, jak na obrazie 2.

    styl.css CSS zmienione: 10 linii
    :root {
        --tomato: Tomato;
        --coral: Coral;
        --border-dark: DarkRed;
    }
    
    * {
        font-family: Garamond, serif;
    }
    
    body {
        display: grid;
        grid-template-columns: 80% 20%;
        grid-template-areas:
            "header aside"
            "main   aside"
            "footer footer";
        align-items: start;
    }
    
    header {
        grid-area: header;
        background-color: var(--tomato);
        color: white;
        text-align: center;
        height: 80px;
    }
    
    main {
        grid-area: main;
        height: 600px;
    }
    
    aside {
        grid-area: aside;
        background-color: var(--coral);
        height: 680px;
    }
    
    footer {
        grid-area: footer;
        background-color: var(--tomato);
        color: white;
        text-align: center;
    }
    Podgląd strony po tym kroku (chat.html)
  22. Krok 22: Obrazy: koło i odstępy po bokach

    Kryteria:

    R.4.8 Ustawiono zaokrąglenie rogów 50% dla obrazu

    ok. 1 min
    Polecenie z arkusza

    Dla selektora obrazu: zaokrąglenie rogów 50%, marginesy wewnętrzne lewy i prawy 10 px

    border-radius: 50% robi z kwadratowego zdjęcia koło (R.4.8). padding-inline: 10px - właściwość logiczna, jednocześnie lewy i prawy margines wewnętrzny, bez ruszania górnego i dolnego (to samo co padding: 0 10px). Zgodnie z uwagą arkusza selektor to sam znacznik img.

    styl.css CSS zmienione: 5 linii
    :root {
        --tomato: Tomato;
        --coral: Coral;
        --border-dark: DarkRed;
    }
    
    * {
        font-family: Garamond, serif;
    }
    
    body {
        display: grid;
        grid-template-columns: 80% 20%;
        grid-template-areas:
            "header aside"
            "main   aside"
            "footer footer";
        align-items: start;
    }
    
    header {
        grid-area: header;
        background-color: var(--tomato);
        color: white;
        text-align: center;
        height: 80px;
    }
    
    main {
        grid-area: main;
        height: 600px;
    }
    
    aside {
        grid-area: aside;
        background-color: var(--coral);
        height: 680px;
    }
    
    footer {
        grid-area: footer;
        background-color: var(--tomato);
        color: white;
        text-align: center;
    }
    
    img {
        border-radius: 50%;
        padding-inline: 10px;
    }
    Podgląd strony po tym kroku (chat.html)
  23. Krok 23: Okno chatu: ramka, margines, wysokość, przewijanie

    Kryteria:

    R.4.3 Ustawiono wysokość bloków banera 80px, głównego 600px i bocznego 680px oraz bloku chatu 470px

    ok. 2 min
    Polecenie z arkusza

    Dla bloku chatu: obramowanie linią ciągłą o szerokości 1 px i kolorze Tomato, marginesy zewnętrzne 15 px, wysokość 470 px, paski przewijania zawsze widoczne na stronie

    „Paski przewijania zawsze widoczne” to overflow: scroll (przy auto pojawiłyby się dopiero po zapełnieniu okna). Stała wysokość 470 px sprawia, że kolejne wypowiedzi nie rozciągają strony, tylko okno się przewija. Wysokości banera, bloków i okna chatu to komplet R.4.3.

    styl.css CSS zmienione: 7 linii
    :root {
        --tomato: Tomato;
        --coral: Coral;
        --border-dark: DarkRed;
    }
    
    * {
        font-family: Garamond, serif;
    }
    
    body {
        display: grid;
        grid-template-columns: 80% 20%;
        grid-template-areas:
            "header aside"
            "main   aside"
            "footer footer";
        align-items: start;
    }
    
    header {
        grid-area: header;
        background-color: var(--tomato);
        color: white;
        text-align: center;
        height: 80px;
    }
    
    main {
        grid-area: main;
        height: 600px;
    }
    
    aside {
        grid-area: aside;
        background-color: var(--coral);
        height: 680px;
    }
    
    footer {
        grid-area: footer;
        background-color: var(--tomato);
        color: white;
        text-align: center;
    }
    
    img {
        border-radius: 50%;
        padding-inline: 10px;
    }
    
    #chat {
        border: 1px solid var(--tomato);
        margin: 15px;
        height: 470px;
        overflow: scroll;
    }
    Podgląd strony po tym kroku (chat.html)
  24. Krok 24: Wspólny wygląd wypowiedzi

    Kryteria:

    R.4.9 Ustawiono styl dla obu bloków z wypowiedziami: zaokrąglenie rogów 5px, szerokość 90%, wysokość 70px, marginesy zewnętrzne 2%, marginesy wewnętrzne 3px (np. za pomocą klasy, id jest błędem)

    ok. 1 min
    Polecenie z arkusza

    Wspólne dla bloków z wypowiedziami: zaokrąglenie rogów 5 px, szerokość 90%, wysokość 70 px, marginesy zewnętrzne 2%, marginesy wewnętrzne 3 px

    Klasa .message - także przy wypowiedziach dokładanych przez skrypt (R.4.9). Wysokość 70 px to dokładnie wysokość zdjęć.

    styl.css CSS zmienione: 8 linii
    :root {
        --tomato: Tomato;
        --coral: Coral;
        --border-dark: DarkRed;
    }
    
    * {
        font-family: Garamond, serif;
    }
    
    body {
        display: grid;
        grid-template-columns: 80% 20%;
        grid-template-areas:
            "header aside"
            "main   aside"
            "footer footer";
        align-items: start;
    }
    
    header {
        grid-area: header;
        background-color: var(--tomato);
        color: white;
        text-align: center;
        height: 80px;
    }
    
    main {
        grid-area: main;
        height: 600px;
    }
    
    aside {
        grid-area: aside;
        background-color: var(--coral);
        height: 680px;
    }
    
    footer {
        grid-area: footer;
        background-color: var(--tomato);
        color: white;
        text-align: center;
    }
    
    img {
        border-radius: 50%;
        padding-inline: 10px;
    }
    
    #chat {
        border: 1px solid var(--tomato);
        margin: 15px;
        height: 470px;
        overflow: scroll;
    }
    
    .message {
        border-radius: 5px;
        width: 90%;
        height: 70px;
        margin: 2%;
        padding: 3px;
    }
    Podgląd strony po tym kroku (chat.html)
  25. Krok 25: Wypowiedzi Jolanty i Krzysztofa

    Kryteria: ,

    R.3.3 W bloku z wypowiedziami Jolanty obraz jest opływany tekstem po prawej stronie a z wypowiedziami Krzysztofa po stronie lewej

    R.4.10 Ustawiono kolor tła bloku wypowiedzi Jolanty #EEE oraz wypowiedzi Krzysztofa #CCC, wypowiedzi Krzysztofa są wyrównane do prawej strony

    ok. 2 min
    Polecenie z arkusza

    Dodatkowo dla bloków z wypowiedziami Jolanty: kolor tła #EEE, obraz opływany tekstem z prawej strony (obraz po lewej stronie). Dodatkowo dla bloków z wypowiedziami Krzysztofa: kolor tła #CCC, wyrównanie tekstu do prawej strony, obraz opływany tekstem z lewej strony (obraz po prawej stronie).

    Tu float zostaje, bo robi dokładnie to, do czego powstał: opływanie obrazu tekstem - arkusz mówi to wprost, a klucz R.3.3 ocenia opływanie. Siatka służy do układu bloków strony, float do obrazka w akapicie. Selektor .jolanta img dotyczy tylko zdjęć w wypowiedziach Jolanty, więc zdjęcia w bloku bocznym zostają na miejscu (R.3.3, R.4.10).

    styl.css CSS zmienione: 17 linii
    :root {
        --tomato: Tomato;
        --coral: Coral;
        --border-dark: DarkRed;
    }
    
    * {
        font-family: Garamond, serif;
    }
    
    body {
        display: grid;
        grid-template-columns: 80% 20%;
        grid-template-areas:
            "header aside"
            "main   aside"
            "footer footer";
        align-items: start;
    }
    
    header {
        grid-area: header;
        background-color: var(--tomato);
        color: white;
        text-align: center;
        height: 80px;
    }
    
    main {
        grid-area: main;
        height: 600px;
    }
    
    aside {
        grid-area: aside;
        background-color: var(--coral);
        height: 680px;
    }
    
    footer {
        grid-area: footer;
        background-color: var(--tomato);
        color: white;
        text-align: center;
    }
    
    img {
        border-radius: 50%;
        padding-inline: 10px;
    }
    
    #chat {
        border: 1px solid var(--tomato);
        margin: 15px;
        height: 470px;
        overflow: scroll;
    }
    
    .message {
        border-radius: 5px;
        width: 90%;
        height: 70px;
        margin: 2%;
        padding: 3px;
    }
    
    .jolanta {
        background-color: #EEE;
    }
    
    .jolanta img {
        float: left;
    }
    
    .krzysztof {
        background-color: #CCC;
        text-align: right;
    }
    
    .krzysztof img {
        float: right;
    }
    Podgląd strony po tym kroku (chat.html)
  26. Krok 26: Przyciski i pole edycyjne

    Kryteria: , , , ,

    R.4.2 Ustawiono kolor tła Tomato dla bloków banera, stopki i przycisków; Coral dla bloku bocznego oraz biały kolor czcionki dla banera i stopki

    R.4.4 Ustawiono szerokość bloków banera i głównego 80%, bloku bocznego 20% (width lub grid- template-columns lub flex) oraz pola edycyjnego 500px

    R.4.5 Ustawiono wyrównanie tekstu do środka dla banera i stopki oraz czcionkę pogrubioną dla przycisków

    R.4.6 Ustawiono marginesy wewnętrzne (padding) lewy i prawy 10px dla selektora img, 5px dla przycisków oraz marginesy zewnętrzne (margin) dla bloku chatu 15px

    R.4.7 Ustawiono obramowanie linią ciągłą o szerokości 1px i kolorze Tomato dla bloku chatu oraz obramowanie linią ciągłą o szerokości 1px i kolorze DarkRed dla przycisków oraz paski przewijania zawsze widoczne na stronie (overflow: scroll;) dla bloku chatu

    ok. 2 min
    Polecenie z arkusza

    Dla obu przycisków: kolor tła Tomato, obramowanie linią ciągłą o szerokości 1 px i kolorze DarkRed, marginesy wewnętrzne 5 px, czcionka pogrubiona. Dla pola edycyjnego tekstowego: szerokość 500 px

    Selektor button obejmuje oba przyciski - także ten w formularzu. Ta reguła domyka kolory tła (R.4.2), pogrubienie obok wyśrodkowania banera i stopki (R.4.5), marginesy wewnętrzne i zewnętrzne (R.4.6) oraz obramowania razem z przewijaniem chatu (R.4.7). Pole przez identyfikator - z szerokościami kolumn to R.4.4.

    styl.css CSS zmienione: 11 linii
    :root {
        --tomato: Tomato;
        --coral: Coral;
        --border-dark: DarkRed;
    }
    
    * {
        font-family: Garamond, serif;
    }
    
    body {
        display: grid;
        grid-template-columns: 80% 20%;
        grid-template-areas:
            "header aside"
            "main   aside"
            "footer footer";
        align-items: start;
    }
    
    header {
        grid-area: header;
        background-color: var(--tomato);
        color: white;
        text-align: center;
        height: 80px;
    }
    
    main {
        grid-area: main;
        height: 600px;
    }
    
    aside {
        grid-area: aside;
        background-color: var(--coral);
        height: 680px;
    }
    
    footer {
        grid-area: footer;
        background-color: var(--tomato);
        color: white;
        text-align: center;
    }
    
    img {
        border-radius: 50%;
        padding-inline: 10px;
    }
    
    #chat {
        border: 1px solid var(--tomato);
        margin: 15px;
        height: 470px;
        overflow: scroll;
    }
    
    .message {
        border-radius: 5px;
        width: 90%;
        height: 70px;
        margin: 2%;
        padding: 3px;
    }
    
    .jolanta {
        background-color: #EEE;
    }
    
    .jolanta img {
        float: left;
    }
    
    .krzysztof {
        background-color: #CCC;
        text-align: right;
    }
    
    .krzysztof img {
        float: right;
    }
    
    button {
        background-color: var(--tomato);
        border: 1px solid var(--border-dark);
        padding: 5px;
        font-weight: bold;
    }
    
    #text {
        width: 500px;
    }
    Podgląd strony po tym kroku (chat.html)
  27. Etap 4: JavaScript

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

    Krok 27: Wyślij: tekst z pola na końcu okna chatu

    Kryteria: , ,

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

    R.5.2 Funkcja wywoływana przyciskiem Wyślij pobiera dane z pola edycyjnego

    R.5.3 Dane z pola edycyjnego są wyświetlane w oknie chatu

    ok. 3 min
    Polecenie z arkusza

    Działanie funkcji wywoływanej po kliknięciu przycisku Wyślij: Pobierany jest tekst z pola edycyjnego i umieszczany w oknie chatu jako ostatni

    Skrypt na końcu <body> - gdy przeglądarka go czyta, wszystkie elementy strony już istnieją.

    • const - stałe na elementy strony: okno chatu, formularz i pole. querySelector przyjmuje selektor CSS (#chat), ten sam co w arkuszu stylów,
    • addEventListener("submit", ...) - funkcja (tu zapisana strzałką (event) => { ... }) wykona się po kliknięciu „Wyślij” albo po Enterze w polu,
    • event.preventDefault() - bez tego formularz zrobiłby to, do czego służy: przeładowałby stronę i wiadomość by zniknęła,
    • textField.value - tekst z pola (R.5.2), textContent wpisuje go do nowego akapitu jako zwykły tekst, a append dokłada akapit na koniec okna - jako ostatni (R.5.3).

    Na razie tekst pojawia się bez tła i zdjęcia. Podgląd działa: wpisz coś i naciśnij Enter.

    chat.html HTML zmienione: 12 linii
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <title>Chat</title>
        <link rel="stylesheet" href="styl.css">
    </head>
    <body>
        <header>
            <h2>Chat</h2>
        </header>
        <main>
            <div id="chat">
                <div class="message jolanta">
                    <img src="Jolka.jpg" alt="Jolanta Nowak">
                    <p>Cześć idziesz jutro do kina?</p>
                </div>
                <div class="message krzysztof">
                    <img src="Krzysiek.jpg" alt="Krzysztof Łukasiński">
                    <p>Tak! A ty?</p>
                </div>
            </div>
            <form id="message-form">
                <label for="text">Wpisz wiadomość:</label>
                <input type="text" id="text" name="text" required autocomplete="off">
                <button>Wyślij</button>
            </form>
            <button type="button" id="generate">Generuj losową odpowiedź</button>
        </main>
        <aside>
            <h3>Chaty</h3>
            <ul>
                <li>Ogólny</li>
                <li>Tematyczne
                    <ol>
                        <li>Turystyczny</li>
                        <li>Żeglarski</li>
                        <li>Filatelistyczny</li>
                        <li>Hodowla zwierzątek domowych</li>
                    </ol>
                </li>
            </ul>
            <h3>Uczestnicy</h3>
            <p>Jolanta Nowak</p>
            <img src="Jolka.jpg" alt="Jolanta Nowak">
            <p>Krzysztof Łukasiński</p>
            <img src="Krzysiek.jpg" alt="Krzysztof Łukasiński">
        </aside>
        <footer>
            <h5>Chat wykonał: Bartosz Bryniarski</h5>
        </footer>
        <script>
            const chatWindow = document.querySelector("#chat");
            const messageForm = document.querySelector("#message-form");
            const textField = document.querySelector("#text");
    
            messageForm.addEventListener("submit", (event) => {
                event.preventDefault();
                const paragraph = document.createElement("p");
                paragraph.textContent = textField.value;
                chatWindow.append(paragraph);
            });
        </script>
    </body>
    </html>
    Podgląd strony po tym kroku (chat.html)
  28. Krok 28: Wyślij: blok wypowiedzi Jolanty z szablonu

    Kryteria:

    R.5.4 Dane z funkcji związanej z przyciskiem Wyślij są formatowane jako wypowiedzi Jolanty (tak samo jak pierwsza wypowiedź w chacie)

    ok. 4 min
    Polecenie z arkusza

    Tekst jest formatowany jako wypowiedź Jolanty, zgodnie z obrazem 3, czyli utworzony jest blok z wypowiedzią zawierający obraz Jolka.jpg i paragraf zgodny z tekstem wpisanym do pola edycyjnego

    • w HTML-u, pod formularzem, <template> z wzorem wypowiedzi: div.message z obrazem i akapitem. Zawartości szablonu przeglądarka nie wyświetla ani nie wczytuje (pusty src niczego nie pobiera) - to tylko wzór do kopiowania,
    • obiekt AUTHORS trzyma dane obojga uczestników: plik zdjęcia i podpis. Klucz obiektu (jolanta) jest też nazwą klasy CSS,
    • funkcja addMessage(text, authorKey): cloneNode(true) kopiuje wzór z całą zawartością, classList.add dokłada klasę autora, a potem uzupełnia zdjęcie, tekst alternatywny i akapit. Blok ma tę samą budowę co pierwsza wypowiedź w HTML-u (R.5.4).

    Wygląd wypowiedzi jest więc opisany w jednym miejscu, w HTML-u, a skrypt tylko wypełnia dane. Sprawdzone w Chrome: wiadomość „Ok, cieszę się” dostaje ten sam wygląd co „Cześć idziesz jutro do kina?” (obraz 3).

    chat.html HTML zmienione: 24 linie
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <title>Chat</title>
        <link rel="stylesheet" href="styl.css">
    </head>
    <body>
        <header>
            <h2>Chat</h2>
        </header>
        <main>
            <div id="chat">
                <div class="message jolanta">
                    <img src="Jolka.jpg" alt="Jolanta Nowak">
                    <p>Cześć idziesz jutro do kina?</p>
                </div>
                <div class="message krzysztof">
                    <img src="Krzysiek.jpg" alt="Krzysztof Łukasiński">
                    <p>Tak! A ty?</p>
                </div>
            </div>
            <form id="message-form">
                <label for="text">Wpisz wiadomość:</label>
                <input type="text" id="text" name="text" required autocomplete="off">
                <button>Wyślij</button>
            </form>
            <template id="message-template">
                <div class="message">
                    <img src="" alt="">
                    <p></p>
                </div>
            </template>
            <button type="button" id="generate">Generuj losową odpowiedź</button>
        </main>
        <aside>
            <h3>Chaty</h3>
            <ul>
                <li>Ogólny</li>
                <li>Tematyczne
                    <ol>
                        <li>Turystyczny</li>
                        <li>Żeglarski</li>
                        <li>Filatelistyczny</li>
                        <li>Hodowla zwierzątek domowych</li>
                    </ol>
                </li>
            </ul>
            <h3>Uczestnicy</h3>
            <p>Jolanta Nowak</p>
            <img src="Jolka.jpg" alt="Jolanta Nowak">
            <p>Krzysztof Łukasiński</p>
            <img src="Krzysiek.jpg" alt="Krzysztof Łukasiński">
        </aside>
        <footer>
            <h5>Chat wykonał: Bartosz Bryniarski</h5>
        </footer>
        <script>
            const chatWindow = document.querySelector("#chat");
            const messageForm = document.querySelector("#message-form");
            const textField = document.querySelector("#text");
            const messageTemplate = document.querySelector("#message-template");
    
            const AUTHORS = {
                jolanta: { image: "Jolka.jpg", name: "Jolanta Nowak" },
                krzysztof: { image: "Krzysiek.jpg", name: "Krzysztof Łukasiński" },
            };
    
            function addMessage(text, authorKey) {
                const author = AUTHORS[authorKey];
                const block = messageTemplate.content.firstElementChild.cloneNode(true);
                block.classList.add(authorKey);
                const image = block.querySelector("img");
                image.src = author.image;
                image.alt = author.name;
                block.querySelector("p").textContent = text;
                chatWindow.append(block);
            }
    
            messageForm.addEventListener("submit", (event) => {
                event.preventDefault();
                addMessage(textField.value, "jolanta");
            });
        </script>
    </body>
    </html>
    Podgląd strony po tym kroku (chat.html)
  29. Krok 29: Wyślij: przewinięcie do nowej wypowiedzi

    Kryteria:

    R.5.5 Po zapełnieniu bloku chatu następuje przewinięcie do nowo wstawionej wypowiedzi dla przynajmniej jednej funkcji.

    ok. 2 min
    Polecenie z arkusza

    Jeśli blok chatu jest cały wypełniony, powinien być przewinięty do nowo wstawionej wypowiedzi

    scrollTop to odległość przewinięcia okna od góry, a scrollHeight - wysokość całej jego zawartości. Ustawienie pierwszego na drugie przewija okno chatu na sam dół, do nowej wypowiedzi (R.5.5). Gdy okno nie jest pełne, nie ma czego przewijać - warunek „jeśli wypełniony” spełnia się sam. Przewija się tylko okno chatu; scrollIntoView() z tabeli arkusza przewinąłby przy okazji całą stronę.

    chat.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>Chat</title>
        <link rel="stylesheet" href="styl.css">
    </head>
    <body>
        <header>
            <h2>Chat</h2>
        </header>
        <main>
            <div id="chat">
                <div class="message jolanta">
                    <img src="Jolka.jpg" alt="Jolanta Nowak">
                    <p>Cześć idziesz jutro do kina?</p>
                </div>
                <div class="message krzysztof">
                    <img src="Krzysiek.jpg" alt="Krzysztof Łukasiński">
                    <p>Tak! A ty?</p>
                </div>
            </div>
            <form id="message-form">
                <label for="text">Wpisz wiadomość:</label>
                <input type="text" id="text" name="text" required autocomplete="off">
                <button>Wyślij</button>
            </form>
            <template id="message-template">
                <div class="message">
                    <img src="" alt="">
                    <p></p>
                </div>
            </template>
            <button type="button" id="generate">Generuj losową odpowiedź</button>
        </main>
        <aside>
            <h3>Chaty</h3>
            <ul>
                <li>Ogólny</li>
                <li>Tematyczne
                    <ol>
                        <li>Turystyczny</li>
                        <li>Żeglarski</li>
                        <li>Filatelistyczny</li>
                        <li>Hodowla zwierzątek domowych</li>
                    </ol>
                </li>
            </ul>
            <h3>Uczestnicy</h3>
            <p>Jolanta Nowak</p>
            <img src="Jolka.jpg" alt="Jolanta Nowak">
            <p>Krzysztof Łukasiński</p>
            <img src="Krzysiek.jpg" alt="Krzysztof Łukasiński">
        </aside>
        <footer>
            <h5>Chat wykonał: Bartosz Bryniarski</h5>
        </footer>
        <script>
            const chatWindow = document.querySelector("#chat");
            const messageForm = document.querySelector("#message-form");
            const textField = document.querySelector("#text");
            const messageTemplate = document.querySelector("#message-template");
    
            const AUTHORS = {
                jolanta: { image: "Jolka.jpg", name: "Jolanta Nowak" },
                krzysztof: { image: "Krzysiek.jpg", name: "Krzysztof Łukasiński" },
            };
    
            function addMessage(text, authorKey) {
                const author = AUTHORS[authorKey];
                const block = messageTemplate.content.firstElementChild.cloneNode(true);
                block.classList.add(authorKey);
                const image = block.querySelector("img");
                image.src = author.image;
                image.alt = author.name;
                block.querySelector("p").textContent = text;
                chatWindow.append(block);
                chatWindow.scrollTop = chatWindow.scrollHeight;
            }
    
            messageForm.addEventListener("submit", (event) => {
                event.preventDefault();
                addMessage(textField.value, "jolanta");
            });
        </script>
    </body>
    </html>
    Podgląd strony po tym kroku (chat.html)
  30. Krok 30: Tablica dziewięciu odpowiedzi Krzysztofa

    Kryteria:

    R.5.6 W kodzie zdefiniowano tablicę z dziewięcioma wypowiedziami Krzysztofa

    ok. 2 min
    Polecenie z arkusza

    Losowana jest wypowiedź Krzysztofa z wcześniej zadeklarowanej tablicy. Tablica zawiera 9 elementów, którymi są wypowiedzi. Należy je skopiować z pliku pomocniczego tekstyDoChatu.txt rozpakowanego z archiwum

    Plik tekstyDoChatu.txt jest gotowy do wklejenia: teksty w cudzysłowach, z przecinkami. Wystarczy objąć je nawiasami kwadratowymi i nazwać tablicę - replies, jako const: tablica nie zostanie podmieniona na inną. Stoi poza funkcjami, więc powstaje raz, przy wczytaniu strony („wcześniej zadeklarowana”) (R.5.6).

    chat.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>Chat</title>
        <link rel="stylesheet" href="styl.css">
    </head>
    <body>
        <header>
            <h2>Chat</h2>
        </header>
        <main>
            <div id="chat">
                <div class="message jolanta">
                    <img src="Jolka.jpg" alt="Jolanta Nowak">
                    <p>Cześć idziesz jutro do kina?</p>
                </div>
                <div class="message krzysztof">
                    <img src="Krzysiek.jpg" alt="Krzysztof Łukasiński">
                    <p>Tak! A ty?</p>
                </div>
            </div>
            <form id="message-form">
                <label for="text">Wpisz wiadomość:</label>
                <input type="text" id="text" name="text" required autocomplete="off">
                <button>Wyślij</button>
            </form>
            <template id="message-template">
                <div class="message">
                    <img src="" alt="">
                    <p></p>
                </div>
            </template>
            <button type="button" id="generate">Generuj losową odpowiedź</button>
        </main>
        <aside>
            <h3>Chaty</h3>
            <ul>
                <li>Ogólny</li>
                <li>Tematyczne
                    <ol>
                        <li>Turystyczny</li>
                        <li>Żeglarski</li>
                        <li>Filatelistyczny</li>
                        <li>Hodowla zwierzątek domowych</li>
                    </ol>
                </li>
            </ul>
            <h3>Uczestnicy</h3>
            <p>Jolanta Nowak</p>
            <img src="Jolka.jpg" alt="Jolanta Nowak">
            <p>Krzysztof Łukasiński</p>
            <img src="Krzysiek.jpg" alt="Krzysztof Łukasiński">
        </aside>
        <footer>
            <h5>Chat wykonał: Bartosz Bryniarski</h5>
        </footer>
        <script>
            const replies = [
                "Świetnie!",
                "Kto gra główną rolę?",
                "Lubisz filmy Tego reżysera?",
                "Będę 10 minut wcześniej",
                "Może kupimy sobie popcorn?",
                "Ja wolę Colę",
                "Zaproszę jeszcze Grześka",
                "Tydzień temu też byłem w kinie na Diunie",
                "Ja funduję bilety"
            ];
            const chatWindow = document.querySelector("#chat");
            const messageForm = document.querySelector("#message-form");
            const textField = document.querySelector("#text");
            const messageTemplate = document.querySelector("#message-template");
    
            const AUTHORS = {
                jolanta: { image: "Jolka.jpg", name: "Jolanta Nowak" },
                krzysztof: { image: "Krzysiek.jpg", name: "Krzysztof Łukasiński" },
            };
    
            function addMessage(text, authorKey) {
                const author = AUTHORS[authorKey];
                const block = messageTemplate.content.firstElementChild.cloneNode(true);
                block.classList.add(authorKey);
                const image = block.querySelector("img");
                image.src = author.image;
                image.alt = author.name;
                block.querySelector("p").textContent = text;
                chatWindow.append(block);
                chatWindow.scrollTop = chatWindow.scrollHeight;
            }
    
            messageForm.addEventListener("submit", (event) => {
                event.preventDefault();
                addMessage(textField.value, "jolanta");
            });
        </script>
    </body>
    </html>
    Podgląd strony po tym kroku (chat.html)
  31. Krok 31: Generuj: losowa odpowiedź jako wypowiedź Krzysztofa

    Kryteria: , ,

    R.3.4 Dla dowolnego przycisku zdefiniowano zdarzenie związane z jego kliknięciem, które odwołuje się do funkcji w skrypcie (atrybut onClick lub słuchacz zdarzenia click lub właściwość onclick obiektu DOM)

    R.5.7 Funkcja wywoływana przyciskiem Generuj… losuje index tablicy z wypowiedziami Krzysztofa (liczba od 0 do 8), wypowiedź o tym indeksie jest wyświetlana w oknie chatu (sprawdzić w kodzie i w przeglądarce)

    R.5.8 Dane z funkcji związanej z przyciskiem Generuj... są formatowane jako wypowiedzi Krzysztofa (tak samo jak druga wypowiedź w chacie)

    ok. 3 min
    Polecenie z arkusza

    Losowana liczba z przedziału od 0 do 8 jest indeksem tablicy. Wypowiedź jest umieszczana w oknie chatu jako ostatnia. Tekst jest formatowany jako wypowiedź Krzysztofa, zgodnie z obrazem 4, czyli utworzony jest blok z wypowiedzią zawierający obraz Krzysiek.jpg i paragraf zgodny z wylosowanym tekstem

    • addEventListener("click", ...) na przycisku #generate - kliknięcie wywołuje funkcję (R.3.4),
    • Math.random() daje ułamek od 0 (włącznie) do 1 (bez jedynki); po pomnożeniu przez replies.length, czyli 9, i obcięciu Math.floor wychodzi liczba całkowita od 0 do 8 - poprawny indeks tablicy (R.5.7). Długość tablicy zamiast wpisanej 9: dopisanie dziesiątego tekstu nie wymaga zmiany losowania,
    • ta sama addMessage z kluczem krzysztof - zdjęcie po prawej, ciemniejsze tło, tekst do prawej (obraz 4) i przewinięcie okna (R.5.8). Formatowania i przewijania nie trzeba pisać drugi raz.
    chat.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>Chat</title>
        <link rel="stylesheet" href="styl.css">
    </head>
    <body>
        <header>
            <h2>Chat</h2>
        </header>
        <main>
            <div id="chat">
                <div class="message jolanta">
                    <img src="Jolka.jpg" alt="Jolanta Nowak">
                    <p>Cześć idziesz jutro do kina?</p>
                </div>
                <div class="message krzysztof">
                    <img src="Krzysiek.jpg" alt="Krzysztof Łukasiński">
                    <p>Tak! A ty?</p>
                </div>
            </div>
            <form id="message-form">
                <label for="text">Wpisz wiadomość:</label>
                <input type="text" id="text" name="text" required autocomplete="off">
                <button>Wyślij</button>
            </form>
            <template id="message-template">
                <div class="message">
                    <img src="" alt="">
                    <p></p>
                </div>
            </template>
            <button type="button" id="generate">Generuj losową odpowiedź</button>
        </main>
        <aside>
            <h3>Chaty</h3>
            <ul>
                <li>Ogólny</li>
                <li>Tematyczne
                    <ol>
                        <li>Turystyczny</li>
                        <li>Żeglarski</li>
                        <li>Filatelistyczny</li>
                        <li>Hodowla zwierzątek domowych</li>
                    </ol>
                </li>
            </ul>
            <h3>Uczestnicy</h3>
            <p>Jolanta Nowak</p>
            <img src="Jolka.jpg" alt="Jolanta Nowak">
            <p>Krzysztof Łukasiński</p>
            <img src="Krzysiek.jpg" alt="Krzysztof Łukasiński">
        </aside>
        <footer>
            <h5>Chat wykonał: Bartosz Bryniarski</h5>
        </footer>
        <script>
            const replies = [
                "Świetnie!",
                "Kto gra główną rolę?",
                "Lubisz filmy Tego reżysera?",
                "Będę 10 minut wcześniej",
                "Może kupimy sobie popcorn?",
                "Ja wolę Colę",
                "Zaproszę jeszcze Grześka",
                "Tydzień temu też byłem w kinie na Diunie",
                "Ja funduję bilety"
            ];
            const chatWindow = document.querySelector("#chat");
            const messageForm = document.querySelector("#message-form");
            const textField = document.querySelector("#text");
            const messageTemplate = document.querySelector("#message-template");
    
            const AUTHORS = {
                jolanta: { image: "Jolka.jpg", name: "Jolanta Nowak" },
                krzysztof: { image: "Krzysiek.jpg", name: "Krzysztof Łukasiński" },
            };
    
            function addMessage(text, authorKey) {
                const author = AUTHORS[authorKey];
                const block = messageTemplate.content.firstElementChild.cloneNode(true);
                block.classList.add(authorKey);
                const image = block.querySelector("img");
                image.src = author.image;
                image.alt = author.name;
                block.querySelector("p").textContent = text;
                chatWindow.append(block);
                chatWindow.scrollTop = chatWindow.scrollHeight;
            }
    
            messageForm.addEventListener("submit", (event) => {
                event.preventDefault();
                addMessage(textField.value, "jolanta");
            });
    
            document.querySelector("#generate").addEventListener("click", () => {
                const index = Math.floor(Math.random() * replies.length);
                addMessage(replies[index], "krzysztof");
            });
        </script>
    </body>
    </html>
    Podgląd strony po tym kroku (chat.html)
  32. Krok 32: Pusta wiadomość pominięta, pole czyszczone

    Kryteria:

    R.5.9 Zastosowano znaczące nazewnictwo dla wszystkich zmiennych i funkcji w języku polskim lub angielskim. Nazwy odzwierciedlają cel zastosowania.

    ok. 3 min

    Dodatek spoza arkusza - klucz go nie ocenia. required w polu zatrzymuje pusty formularz, ale wiadomość z samych spacji przechodzi. Dlatego:

    • trim() obcina spacje z początku i końca, a dla pustego napisu funkcja kończy się od razu (return),
    • po wysłaniu messageForm.reset() czyści pole, a focus() zostawia w nim kursor - kolejną wiadomość można pisać od razu.

    Sprawdzone w prawdziwym Chrome: wiadomość „Ok, cieszę się” trafia na koniec okna jako message jolanta ze zdjęciem i podpisem, pole jest czyszczone; losowanie (przy Math.random zwracającym 0,95) daje ostatni tekst tablicy w bloku message krzysztof; po 12 kolejnych odpowiedziach okno chatu jest przewinięte do końca, a strona stoi w miejscu; spacje i puste pole nie dodają niczego; <b>X</b> wyświetla się dosłownie, bo textContent nie tworzy znaczników. Nazwy zmiennych i funkcji mówią, do czego służą (R.5.9).

    chat.html HTML zmienione: 7 linii
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <title>Chat</title>
        <link rel="stylesheet" href="styl.css">
    </head>
    <body>
        <header>
            <h2>Chat</h2>
        </header>
        <main>
            <div id="chat">
                <div class="message jolanta">
                    <img src="Jolka.jpg" alt="Jolanta Nowak">
                    <p>Cześć idziesz jutro do kina?</p>
                </div>
                <div class="message krzysztof">
                    <img src="Krzysiek.jpg" alt="Krzysztof Łukasiński">
                    <p>Tak! A ty?</p>
                </div>
            </div>
            <form id="message-form">
                <label for="text">Wpisz wiadomość:</label>
                <input type="text" id="text" name="text" required autocomplete="off">
                <button>Wyślij</button>
            </form>
            <template id="message-template">
                <div class="message">
                    <img src="" alt="">
                    <p></p>
                </div>
            </template>
            <button type="button" id="generate">Generuj losową odpowiedź</button>
        </main>
        <aside>
            <h3>Chaty</h3>
            <ul>
                <li>Ogólny</li>
                <li>Tematyczne
                    <ol>
                        <li>Turystyczny</li>
                        <li>Żeglarski</li>
                        <li>Filatelistyczny</li>
                        <li>Hodowla zwierzątek domowych</li>
                    </ol>
                </li>
            </ul>
            <h3>Uczestnicy</h3>
            <p>Jolanta Nowak</p>
            <img src="Jolka.jpg" alt="Jolanta Nowak">
            <p>Krzysztof Łukasiński</p>
            <img src="Krzysiek.jpg" alt="Krzysztof Łukasiński">
        </aside>
        <footer>
            <h5>Chat wykonał: Bartosz Bryniarski</h5>
        </footer>
        <script>
            const replies = [
                "Świetnie!",
                "Kto gra główną rolę?",
                "Lubisz filmy Tego reżysera?",
                "Będę 10 minut wcześniej",
                "Może kupimy sobie popcorn?",
                "Ja wolę Colę",
                "Zaproszę jeszcze Grześka",
                "Tydzień temu też byłem w kinie na Diunie",
                "Ja funduję bilety"
            ];
            const chatWindow = document.querySelector("#chat");
            const messageForm = document.querySelector("#message-form");
            const textField = document.querySelector("#text");
            const messageTemplate = document.querySelector("#message-template");
    
            const AUTHORS = {
                jolanta: { image: "Jolka.jpg", name: "Jolanta Nowak" },
                krzysztof: { image: "Krzysiek.jpg", name: "Krzysztof Łukasiński" },
            };
    
            function addMessage(text, authorKey) {
                const author = AUTHORS[authorKey];
                const block = messageTemplate.content.firstElementChild.cloneNode(true);
                block.classList.add(authorKey);
                const image = block.querySelector("img");
                image.src = author.image;
                image.alt = author.name;
                block.querySelector("p").textContent = text;
                chatWindow.append(block);
                chatWindow.scrollTop = chatWindow.scrollHeight;
            }
    
            messageForm.addEventListener("submit", (event) => {
                event.preventDefault();
                const text = textField.value.trim();
                if (text === "") {
                    return;
                }
                addMessage(text, "jolanta");
                messageForm.reset();
                textField.focus();
            });
    
            document.querySelector("#generate").addEventListener("click", () => {
                const index = Math.floor(Math.random() * replies.length);
                addMessage(replies[index], "krzysztof");
            });
        </script>
    </body>
    </html>
    Podgląd strony po tym kroku (chat.html)
  33. Etap 5: Porównanie z wersją podstawową

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

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

    ok. 2 min

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

    wersja podstawowawersja nowoczesna
    układfloat banera, bloku głównego i bocznego + cleargrid-template-areas na <body>
    kolejność w HTML-uaside przed main (wymóg float)kolejność treści: main, potem aside
    zapytanie 1VALUES (3, ...) - liczba odczytana ręcznieINSERT ... SELECT - identyfikator z relacji
    pole i przycisk Wyślijluźne elementy, onclickformularz, zdarzenie submit (także Enter), required
    tworzenie wypowiedzidwie funkcje z createElement (w dobrych praktykach addMessage)<template> + cloneNode i obiekt AUTHORS
    tekst wiadomościinnerTexttextContent
    przewijaniescrollIntoView() (przewija też stronę)scrollTop = scrollHeight (tylko okno chatu)
    zmienneletconst

    Na egzaminie wersja podstawowa trzyma się funkcji z tabel arkusza i łatwiej ją napisać bez podpowiedzi. Nowoczesna ma wygląd wypowiedzi w jednym miejscu (szablon), a dodanie trzeciego uczestnika to jeden wpis w AUTHORS i jedna reguła CSS. float został w obu wersjach tam, gdzie jest naturalny - przy zdjęciu opływanym tekstem.

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

  34. Etap 6: Oddanie pracy

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

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

    ok. 5 min
    Polecenie z arkusza

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

    W przeglądarka.txt nazwa przeglądarki, w której była sprawdzana strona. W folderze z numerem zdającego: chat.html, styl.css, kwerendy.txt, import.png, kw1.jpg - kw5.jpg, przycięte Jolka.jpg, Krzysiek.jpg i przeglądarka.txt. Nagraj płytę, sprawdź jej odczyt i opisz ją numerem zdającego.

    przeglądarka.txt Tekst nowy plik
    Google Chrome

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

Podgląd

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

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