Programowanie po stronie klienta (JavaScript)
Zmienne, typy danych, pętle, instrukcje warunkowe, manipulacja modelem DOM, obsługa zdarzeń (events), walidacja formularzy po stronie klienta, ciasteczka i pamięć lokalna (Local Storage).
Wstęp: JavaScript to dział, w którym pisze się kod, a nie opisuje
W części praktycznej INF.03 JavaScript pojawia się prawie zawsze w tej samej
roli: masz gotowy formularz w HTML i masz go ożywić — pobrać elementy przez
getElementById,
podpiąć zdarzenie, sprawdzić dane, wyświetlić komunikat. Część pisemna pyta
o rzeczy, które trzeba znać z pamięci: różnicę między
== a
===,
zasięg let kontra
var,
wynik konkretnej pętli albo metodę tablicy. Ten artykuł idzie dokładnie tą
ścieżką: od wstawienia skryptu do strony aż do zapisu danych w Local Storage.
📋 Spis treści
Podstawy języka
1
Osadzanie skryptu w stronie
JavaScript wykonuje się w przeglądarce użytkownika — to właśnie znaczy
„po stronie klienta". Serwer wysyła plik, przeglądarka go uruchamia. Skrypt
trafia do dokumentu na jeden z dwóch sposobów: wpisany wprost w znacznik
<script>
albo dołączony z zewnętrznego pliku .js.
<!-- 1. Skrypt wewnetrzny (inline) -->
<script>
document.getElementById("tytul").textContent = "Witaj!";
</script>
<!-- 2. Skrypt zewnetrzny - atrybut src, znacznik MUSI byc zamkniety -->
<script src="js/skrypt.js"></script>
<!-- 3. Odroczenie wykonania -->
<script src="js/skrypt.js" defer></script>
<script src="js/analityka.js" async></script>
Przeglądarka wstrzymuje budowanie strony, pobiera skrypt i od razu go
wykonuje. Jeśli taki skrypt stoi w sekcji
<head>
i szuka elementu z <body>,
dostanie null — bo tego elementu jeszcze nie ma.
Skrypt pobiera się równolegle z budową strony, ale wykonuje dopiero po
zbudowaniu całego DOM. Kolejność wielu skryptów z
defer
jest zachowana. To domyślny wybór do skryptów operujących na stronie.
Skrypt pobiera się równolegle i wykonuje natychmiast po pobraniu, przerywając budowę strony. Kolejność nie jest gwarantowana. Nadaje się do niezależnych skryptów (statystyki, reklamy), nie do kodu operującego na DOM.
Zasada praktyczna na egzamin:
Skrypt operujący na elementach strony umieszczasz albo na końcu
sekcji body, tuż przed </body>,
albo w <head>
z atrybutem defer. Oba warianty gwarantują, że drzewo DOM
jest gotowe. Atrybuty defer
i async
działają wyłącznie dla skryptów zewnętrznych — na skrypcie wpisanym wprost
w stronę są ignorowane.
- Instrukcje kończy się średnikiem. Nie jest to bezwzględnie wymagane, ale na egzaminie stawiaj je zawsze — czytelność jest oceniana.
- Komentarz jednoliniowy: dwa ukośniki. Wieloliniowy: ukośnik-gwiazdka ... gwiazdka-ukośnik.
- JavaScript rozróżnia wielkość liter.
getElementByIdzapisane jakogetElementByIDto błąd. - Do szybkiego podglądu wartości służy
console.log()— wynik widać w konsoli przeglądarki (klawisz F12). alert(),prompt()iconfirm()to okienka systemowe. prompt zawsze zwraca tekst, nawet gdy wpiszesz liczbę.
2
Zmienne, typy danych i operatory
Zmienną deklaruje się słowem var,
let albo
const.
Różnice między nimi to jedno z najpewniejszych pytań testowych z tego działu.
| Cecha | var | let | const |
|---|---|---|---|
| Zasięg | funkcyjny (cała funkcja) | blokowy (para nawiasów klamrowych) | blokowy |
| Ponowna deklaracja w tym samym zasięgu | tak, bez błędu | nie — SyntaxError | nie — SyntaxError |
| Zmiana wartości | tak | tak | nie — TypeError |
| Hoisting (wyniesienie deklaracji) | tak, wartość przed przypisaniem to undefined | deklaracja wyniesiona, ale użycie przed nią to ReferenceError | tak samo jak let |
| Wymagana wartość początkowa | nie | nie | tak, przy deklaracji |
Praktyka: domyślnie piszesz const, a gdy wartość ma się zmieniać — let. var zostawiasz starym kodom, ale musisz je rozpoznawać na egzaminie.
// Zasieg blokowy kontra funkcyjny
if (true) {
var a = 10;
let b = 20;
}
console.log(a); // 10 - var "wycieka" poza blok
console.log(b); // ReferenceError: b is not defined
// const chroni PRZYPISANIE, nie zawartosc obiektu
const lista = [1, 2, 3];
lista.push(4); // OK - tablica sie zmienia
console.log(lista); // [1, 2, 3, 4]
lista = [9]; // TypeError: Assignment to constant variable
- number — jeden typ na liczby całkowite i zmiennoprzecinkowe. Separator dziesiętny to kropka.
- string — tekst w apostrofach lub cudzysłowach.
- boolean —
truealbofalse. - undefined — zmienna zadeklarowana, ale bez wartości.
- null — celowy brak wartości, ustawiany przez programistę.
- NaN — Not a Number, wynik nieudanej operacji liczbowej. Typem jest… number.
Number("12")→ 12,Number("abc")→ NaNparseInt("12.9")→ 12, ucina część ułamkowąparseFloat("12.9zł")→ 12.9, czyta do pierwszego nieliczbowego znakuString(12)lub(12).toString()→ "12"isNaN(x)— sprawdza, czy wartość nie da się przeliczyć na liczbętypeof x— zwraca nazwę typu jako tekst
Wartość pobrana z pola formularza przez .value
jest zawsze tekstem, nawet dla pola typu number. Jeśli jej
nie przekonwertujesz, plus skleja zamiast dodawać:
"2" + 3 // "23" - plus sklada teksty (konkatenacja)
"2" - 3 // -1 - minus wymusza liczby
"6" * "2" // 12 - mnozenie tez wymusza liczby
Number("2") + 3 // 5 - poprawnie
const cena = Number(document.getElementById("cena").value);
const ile = parseInt(document.getElementById("ilosc").value, 10);
console.log(cena * ile);==
porównuje po wcześniejszej konwersji typów (porównanie luźne).
===
porównuje wartość i typ, bez żadnej konwersji (porównanie
ścisłe). Negacje to odpowiednio
!= i
!==.
| Porównanie | Wynik == | Wynik === | Dlaczego |
|---|---|---|---|
| 5 i "5" | true | false | tekst zamieniony na liczbę |
| 0 i false | true | false | false przeliczane na 0 |
| "" i 0 | true | false | pusty tekst to liczba 0 |
| null i undefined | true | false | wyjątek zapisany w standardzie |
| "10" i 10 | true | false | konwersja tekstu na liczbę |
| NaN i NaN | false | false | NaN nie równa się niczemu, nawet sobie |
Na egzaminie i w praktyce używaj ===. Jedyne sensowne zastosowanie == to sprawdzenie „null albo undefined" jednym warunkiem.
- Arytmetyczne: plus, minus, gwiazdka, ukośnik, procent (reszta z dzielenia), dwie gwiazdki (potęgowanie).
17 % 5daje 2 — to standardowy sposób sprawdzenia parzystości. - Inkrementacja:
i++zwraca starą wartość i zwiększa,++inajpierw zwiększa, potem zwraca. - Przypisania złożone:
x += 5to skrót odx = x + 5. - Porównania: mniejszość, większość oraz warianty z równością — zapisywane jako <, >, <= i >=.
- Logiczne: podwójny ampersand (i — wszystkie warunki prawdziwe), podwójna kreska pionowa (lub — wystarczy jeden), wykrzyknik (negacja).
- Warunkowy (trójargumentowy):
wynik = wiek >= 18 ? "dorosly" : "nieletni";
3
Warunki, switch i pętle
Typowe zadanie testowe pokazuje kilka linii kodu i pyta, co zostanie wypisane. Trzeba umieć „wykonać kod w głowie": policzyć obroty pętli i sprawdzić, czy warunek jest spełniony.
// Instrukcja warunkowa
const ocena = 4;
if (ocena >= 5) {
console.log("bardzo dobry");
} else if (ocena >= 4) {
console.log("dobry");
} else if (ocena >= 3) {
console.log("dostateczny");
} else {
console.log("niedostateczny");
}
// switch - porownuje SCISLE (jak ===)
const dzien = 3;
switch (dzien) {
case 1:
case 2:
case 3:
case 4:
case 5:
console.log("dzien roboczy");
break;
case 6:
case 7:
console.log("weekend");
break;
default:
console.log("bledny numer dnia");
}
Brak break = przelot (fall-through).
Jeśli w bloku case
zapomnisz instrukcji break,
wykonanie „przeleci" do kolejnych przypadków, aż napotka break albo koniec
bloku. Czasem wykorzystuje się to celowo, jak w przykładzie wyżej, gdzie pięć
przypadków dzieli jedną instrukcję. Częściej jest to jednak błąd i typowy
haczyk w pytaniu „co wypisze ten kod".
// for - gdy znasz liczbe powtorzen
let suma = 0;
for (let i = 1; i <= 10; i++) {
suma += i;
}
console.log(suma); // 55
// while - warunek sprawdzany PRZED wykonaniem
let n = 5;
while (n > 0) {
console.log(n);
n--;
}
// do...while - blok wykona sie CO NAJMNIEJ RAZ
let x = 100;
do {
console.log("wykonalo sie mimo falszywego warunku");
} while (x < 10);
// for...of - po WARTOSCIACH tablicy
const oceny = [3, 4, 5];
for (const o of oceny) {
console.log(o); // 3, 4, 5
}
// for...in - po KLUCZACH / INDEKSACH
const uczen = { imie: "Ala", klasa: "3A" };
for (const klucz in uczen) {
console.log(klucz + " = " + uczen[klucz]); // imie = Ala, klasa = 3A
}
// break przerywa cala petle, continue pomija jeden obieg
for (let i = 1; i <= 10; i++) {
if (i % 2 === 0) continue; // pomin parzyste
if (i > 7) break; // zakoncz przy 9
console.log(i); // 1, 3, 5, 7
}
| Pętla | Kiedy | Co daje w każdym obiegu |
|---|---|---|
| for | liczba powtórzeń znana z góry | licznik, którym sam sterujesz |
| while | powtarzasz, dopóki warunek prawdziwy | nic — sam pilnujesz zmiennej |
| do...while | blok ma się wykonać przynajmniej raz | nic; warunek sprawdzany na końcu |
| for...of | przechodzisz tablicę lub tekst | wartość elementu |
| for...in | przechodzisz właściwości obiektu | nazwę klucza (dla tablicy — indeks jako tekst) |
4
Funkcje: deklaracja, wyrażenie, strzałkowa
Funkcja to nazwany fragment kodu, który przyjmuje parametry i może zwrócić
wartość instrukcją return.
Funkcja bez return
zwraca undefined.
// 1. Deklaracja funkcji - podlega hoistingowi,
// mozna ja wywolac PRZED miejscem definicji
console.log(pole(3, 4)); // 12 - dziala
function pole(a, b) {
return a * b;
}
// 2. Wyrazenie funkcyjne - przypisane do zmiennej,
// wywolanie przed definicja konczy sie bledem
const obwod = function (a, b) {
return 2 * (a + b);
};
// 3. Funkcja strzalkowa (arrow function)
const podwoj = (x) => x * 2; // krotki zapis: return domyslny
const suma = (a, b) => {
return a + b;
};
// Parametr domyslny - uzywany, gdy argument nie zostal podany
function powitaj(imie = "gosciu") {
return "Witaj, " + imie + "!";
}
console.log(powitaj()); // Witaj, gosciu!
console.log(powitaj("Ala")); // Witaj, Ala!
// return NATYCHMIAST konczy funkcje
function sprawdzWiek(wiek) {
if (wiek < 0) {
return "bledna wartosc";
}
return wiek >= 18 ? "pelnoletni" : "nieletni";
}
- Parametr to nazwa w nawiasach przy definicji, argument to konkretna wartość podana przy wywołaniu.
- Brakujący argument to
undefined, chyba że ustawiono wartość domyślną. Nadmiarowe argumenty są ignorowane. - Zmienne zadeklarowane wewnątrz funkcji są lokalne — z zewnątrz niewidoczne.
- Wywołanie to nazwa z nawiasami.
polebez nawiasów to sama funkcja, nie jej wynik.
- Jeden parametr — nawiasy okrągłe można pominąć.
- Ciało to jedno wyrażenie — można pominąć klamry i słowo
return. - Zero parametrów — puste nawiasy są obowiązkowe.
- Funkcje strzałkowe nie mają własnego
thisi nie podlegają hoistingowi. Najczęściej używa się ich jako krótkich funkcji przekazywanych domap,filteri obsługi zdarzeń.
5
Tablice, obiekty i praca z tekstem
Tablica przechowuje uporządkowaną listę wartości, indeksowaną od zera. Obiekt przechowuje pary klucz–wartość. Metody tablic to wdzięczny materiał na pytania „co wypisze ten kod", bo część z nich zmienia oryginał, a część zwraca nową tablicę.
const owoce = ["jablko", "gruszka", "sliwka"];
console.log(owoce.length); // 3
console.log(owoce[0]); // jablko
console.log(owoce[2]); // sliwka
console.log(owoce[3]); // undefined - nie ma bledu!
// METODY ZMIENIAJACE ORYGINAL
owoce.push("banan"); // dodaje na KONIEC, zwraca nowa dlugosc
owoce.pop(); // usuwa OSTATNI, zwraca usuniety element
owoce.unshift("wisnia"); // dodaje na POCZATEK
owoce.shift(); // usuwa PIERWSZY, zwraca usuniety element
owoce.splice(1, 1); // usuwa 1 element od indeksu 1
owoce.splice(1, 0, "malina");// wstawia bez usuwania
// METODY ZWRACAJACE NOWA TABLICE (oryginal bez zmian)
const liczby = [5, 12, 8, 130, 44];
console.log(liczby.slice(1, 3)); // [12, 8] - od 1 do 3 BEZ 3
console.log(liczby.indexOf(8)); // 2
console.log(liczby.indexOf(99)); // -1 - nie znaleziono
console.log(liczby.includes(44)); // true
console.log(liczby.join(" - ")); // "5 - 12 - 8 - 130 - 44"
// PRZEBIEG PO ELEMENTACH
liczby.forEach(function (el, i) {
console.log(i + ": " + el); // nic nie zwraca (undefined)
});
const podwojone = liczby.map((el) => el * 2); // [10, 24, 16, 260, 88]
const duze = liczby.filter((el) => el > 10); // [12, 130, 44]
const razem = liczby.reduce((a, b) => a + b, 0); // 199
Zapamiętaj trzy różnice, o które pyta test:
slice nie zmienia tablicy i nie obejmuje elementu o indeksie
końcowym. splice zmienia tablicę w miejscu, a jej drugi
argument to liczba elementów do usunięcia, nie indeks końcowy.
forEach tylko przechodzi po elementach i zwraca undefined,
natomiast map zwraca nową tablicę tej samej długości,
a filter — nową tablicę zawierającą wyłącznie elementy
spełniające warunek.
// OBIEKT - pary klucz: wartosc
const uczen = {
imie: "Jan",
nazwisko: "Kowalski",
klasa: "3A",
oceny: [4, 5, 3],
srednia: function () {
let s = 0;
for (const o of this.oceny) s += o;
return s / this.oceny.length;
}
};
console.log(uczen.imie); // Jan - notacja z kropka
console.log(uczen["klasa"]); // 3A - notacja z nawiasem kwadratowym
uczen.wiek = 17; // dodanie nowej wlasciwosci
delete uczen.klasa; // usuniecie wlasciwosci
console.log(uczen.srednia().toFixed(2)); // 4.00
// TABLICA OBIEKTOW - typowa struktura w zadaniach
const uczniowie = [
{ imie: "Ala", punkty: 45 },
{ imie: "Bartek", punkty: 78 },
{ imie: "Celina", punkty: 61 }
];
const zdali = uczniowie.filter((u) => u.punkty >= 50);
console.log(zdali.length); // 2
const tekst = " Programowanie w JavaScript ";
console.log(tekst.length); // 30 - razem ze spacjami
console.log(tekst.trim()); // "Programowanie w JavaScript"
console.log(tekst.trim().length); // 26
console.log(tekst.toUpperCase()); // wszystko wielkimi literami
console.log(tekst.toLowerCase()); // wszystko malymi literami
const s = "JavaScript";
console.log(s.substring(0, 4)); // "Java" - od 0 do 4 BEZ 4
console.log(s.charAt(0)); // "J"
console.log(s[0]); // "J"
console.log(s.indexOf("Script")); // 4
console.log(s.replace("Java", "Type")); // "TypeScript" - zwraca NOWY tekst
const dane = "Kowalski;Jan;3A";
const czesci = dane.split(";"); // ["Kowalski", "Jan", "3A"]
console.log(czesci[1]); // Jan
// Sprawdzenie poprawnosci adresu e-mail bez wyrazen regularnych
const mail = "ala@example.com";
const ok = mail.includes("@") && mail.indexOf(".") > mail.indexOf("@");
console.log(ok); // true
6
Model DOM i modyfikacja strony
DOM (Document Object Model) to drzewiasta reprezentacja dokumentu
HTML w pamięci przeglądarki. Każdy znacznik jest węzłem, do którego skrypt ma
dostęp przez obiekt document.
Zmiana węzła jest natychmiast widoczna na ekranie — bez przeładowania strony.
| Metoda | Szuka po | Zwraca |
|---|---|---|
| getElementById("x") | identyfikatorze (bez znaku hash) | jeden element albo null |
| getElementsByClassName("k") | nazwie klasy (bez kropki) | kolekcję żywą (HTMLCollection) |
| getElementsByTagName("li") | nazwie znacznika | kolekcję żywą |
| querySelector(".k") | selektorze CSS | PIERWSZY pasujący element albo null |
| querySelectorAll(".k") | selektorze CSS | NodeList wszystkich pasujących (statyczna) |
Kluczowa różnica zapisu: w getElementById podajesz samą nazwę identyfikatora, a w querySelector pełny selektor CSS, czyli z krzyżykiem dla id i kropką dla klasy.
// WYSZUKIWANIE
const naglowek = document.getElementById("tytul");
const pierwszy = document.querySelector("#tytul"); // to samo, selektor CSS
const menu = document.querySelector("nav .menu");
const wszystkie = document.querySelectorAll("li.produkt");
console.log(wszystkie.length);
// ZMIANA TRESCI
naglowek.textContent = "Nowy tytul"; // wstawia CZYSTY TEKST
naglowek.innerHTML = "Nowy <strong>tytul</strong>"; // interpretuje HTML
// ZMIANA ATRYBUTOW
const obrazek = document.querySelector("img");
obrazek.src = "grafika/logo.png";
obrazek.setAttribute("alt", "Logo firmy");
console.log(obrazek.getAttribute("alt"));
// POLA FORMULARZA
const pole = document.getElementById("login");
console.log(pole.value); // tresc wpisana przez uzytkownika
pole.value = ""; // czyszczenie pola
const zgoda = document.getElementById("regulamin");
console.log(zgoda.checked); // true / false dla checkboxa
// STYLE - wlasciwosci CSS pisane camelCase
naglowek.style.color = "red";
naglowek.style.backgroundColor = "#eee"; // nie background-color!
naglowek.style.fontSize = "24px";
// KLASY CSS - czystszy sposob niz recznie ustawiane style
naglowek.classList.add("aktywny");
naglowek.classList.remove("ukryty");
naglowek.classList.toggle("zaznaczony");
console.log(naglowek.classList.contains("aktywny")); // true
// TWORZENIE NOWEGO ELEMENTU - trzy kroki
const lista = document.getElementById("lista");
const nowy = document.createElement("li"); // 1. utworz
nowy.textContent = "Nowy produkt"; // 2. wypelnij
nowy.classList.add("produkt");
lista.appendChild(nowy); // 3. dolacz do DOM
// Wstawienie PRZED istniejacym elementem
const pierwszyEl = lista.firstElementChild;
lista.insertBefore(nowy, pierwszyEl);
// USUWANIE
nowy.remove(); // element usuwa sam siebie
lista.removeChild(lista.lastElementChild); // rodzic usuwa dziecko
lista.innerHTML = ""; // szybkie wyczyszczenie calej zawartosci
// Wypelnienie listy z tablicy - czesty motyw w zadaniach
const produkty = ["Mysz", "Klawiatura", "Monitor"];
produkty.forEach(function (nazwa) {
const li = document.createElement("li");
li.textContent = nazwa;
lista.appendChild(li);
});
textContent czy innerHTML?
textContent wstawia tekst dosłownie — znaczniki wpisane przez
użytkownika zobaczy on jako zwykłe znaki. innerHTML parsuje
zawartość jako kod HTML, więc pozwala dodać formatowanie, ale wstawianie tam
danych od użytkownika otwiera drogę do ataku XSS. Reguła: jeśli wstawiasz
sam tekst — zawsze textContent.
7
Zdarzenia i walidacja formularza
Zdarzenie to sygnał, że coś się stało: użytkownik kliknął, wpisał znak,
wysłał formularz, strona się wczytała. Skrypt reaguje na zdarzenie, podpinając
do elementu funkcję nasłuchującą metodą
addEventListener.
| Zdarzenie | Kiedy zachodzi | Typowy element |
|---|---|---|
| click | kliknięcie myszą | przycisk, odnośnik, dowolny element |
| submit | próba wysłania formularza | znacznik form (nie przycisk!) |
| input | przy każdej zmianie treści pola | pole tekstowe |
| change | po zatwierdzeniu zmiany i utracie fokusu | lista rozwijana, checkbox, plik |
| load | po wczytaniu strony wraz z grafiką | obiekt window |
| mouseover / keyup / focus / blur | najechanie, puszczenie klawisza, wejście i wyjście z pola | dowolny element, pole formularza |
// Skladnia: element.addEventListener("nazwa", funkcja);
// UWAGA: nazwa zdarzenia BEZ przedrostka "on"
const przycisk = document.getElementById("start");
przycisk.addEventListener("click", function (event) {
console.log("Kliknieto element:", event.target);
console.log("Typ zdarzenia:", event.type);
});
// To samo funkcja strzalkowa
przycisk.addEventListener("click", (e) => alert("Klik!"));
// Odnosnik, ktory ma NIE przechodzic pod adres
document.querySelector("a.pomoc").addEventListener("click", function (e) {
e.preventDefault(); // anuluje domyslne dzialanie przegladarki
console.log("Zablokowano przejscie");
});
// Kod uruchamiany po zbudowaniu drzewa DOM
document.addEventListener("DOMContentLoaded", function () {
console.log("Strona gotowa");
});
To najczęstsze zadanie z tego działu. Schemat jest zawsze taki sam: pobierz formularz i pola, podepnij zdarzenie submit, zablokuj domyślne wysłanie metodą preventDefault, sprawdź kolejne pola i wypisz komunikaty. Poniższy kod jest kompletny — HTML i skrypt.
<!-- HTML -->
<form id="rejestracja" novalidate>
<label for="login">Login:</label>
<input type="text" id="login">
<span class="blad" id="bladLogin"></span>
<label for="email">E-mail:</label>
<input type="text" id="email">
<span class="blad" id="bladEmail"></span>
<label for="wiek">Wiek:</label>
<input type="text" id="wiek">
<span class="blad" id="bladWiek"></span>
<button type="submit">Zarejestruj</button>
</form>
<p id="komunikat"></p>// JavaScript - plik dolaczony z atrybutem defer
const formularz = document.getElementById("rejestracja");
const poleLogin = document.getElementById("login");
const poleEmail = document.getElementById("email");
const poleWiek = document.getElementById("wiek");
const komunikat = document.getElementById("komunikat");
formularz.addEventListener("submit", function (e) {
e.preventDefault(); // BEZ TEGO strona sie przeladuje
// 1. czyszczenie poprzednich komunikatow
document.querySelectorAll(".blad").forEach(function (el) {
el.textContent = "";
});
komunikat.textContent = "";
let poprawny = true;
// 2. login - niepusty, co najmniej 3 znaki
const login = poleLogin.value.trim();
if (login === "") {
document.getElementById("bladLogin").textContent = "Pole jest wymagane";
poprawny = false;
} else if (login.length < 3) {
document.getElementById("bladLogin").textContent = "Minimum 3 znaki";
poprawny = false;
}
// 3. e-mail - musi zawierac @ oraz kropke PO malpie
const email = poleEmail.value.trim();
const malpa = email.indexOf("@");
const kropka = email.lastIndexOf(".");
if (email === "") {
document.getElementById("bladEmail").textContent = "Podaj adres e-mail";
poprawny = false;
} else if (malpa < 1 || kropka < malpa + 2 || kropka === email.length - 1) {
document.getElementById("bladEmail").textContent = "Nieprawidlowy adres";
poprawny = false;
}
// 4. wiek - liczba z zakresu 1-120
const wiek = Number(poleWiek.value);
if (poleWiek.value.trim() === "" || isNaN(wiek)) {
document.getElementById("bladWiek").textContent = "Wpisz liczbe";
poprawny = false;
} else if (wiek < 1 || wiek > 120) {
document.getElementById("bladWiek").textContent = "Zakres od 1 do 120";
poprawny = false;
}
// 5. wynik
if (poprawny) {
komunikat.style.color = "green";
komunikat.textContent = "Formularz wyslany poprawnie";
formularz.submit(); // dopiero teraz realne wyslanie
} else {
komunikat.style.color = "red";
komunikat.textContent = "Popraw zaznaczone pola";
}
});
// Kasowanie komunikatu w trakcie pisania
poleLogin.addEventListener("input", function () {
document.getElementById("bladLogin").textContent = "";
});Walidacja po stronie klienta nie zastępuje walidacji na serwerze.
Użytkownik może wyłączyć JavaScript albo wysłać dane z pominięciem
formularza. Walidacja w przeglądarce to wyłącznie wygoda i szybka informacja
zwrotna — dane zawsze trzeba sprawdzić jeszcze raz po stronie
serwera, np. w PHP. To zdanie regularnie pada w pytaniach testowych.
8
Local Storage, Session Storage, ciasteczka
Przeglądarka daje trzy mechanizmy przechowywania danych po stronie klienta. Egzamin pyta głównie o różnice: czas życia, pojemność i to, czy dane wędrują do serwera przy każdym żądaniu.
| Cecha | Local Storage | Session Storage | Cookie |
|---|---|---|---|
| Pojemność (typowo) | ok. 5–10 MB | ok. 5–10 MB | ok. 4 KB na ciasteczko |
| Czas życia | bezterminowo, aż skasuje je skrypt lub użytkownik | do zamknięcia karty przeglądarki | do daty podanej w atrybucie expires; bez niej — do zamknięcia przeglądarki |
| Wysyłane na serwer | nie | nie | tak, przy każdym żądaniu HTTP |
| Zasięg | wszystkie karty tej samej domeny | tylko bieżąca karta | domena i ścieżka podana w atrybutach |
| Dostęp z PHP | brak | brak | tak, tablica superglobalna COOKIE |
| Typowe zastosowanie | motyw kolorystyczny, koszyk, ustawienia | dane jednorazowego kreatora, formularz w trakcie wypełniania | identyfikator sesji, zgoda na regulamin |
// LOCAL STORAGE - cztery metody, wszystko na TEKSTACH
localStorage.setItem("motyw", "ciemny");
const motyw = localStorage.getItem("motyw"); // "ciemny"
localStorage.removeItem("motyw"); // usuwa jeden klucz
localStorage.clear(); // czysci wszystko
// Klucza, ktorego nie ma, to null - stad typowy warunek startowy
if (localStorage.getItem("licznik") === null) {
localStorage.setItem("licznik", "0");
}
// Liczby trzeba konwertowac RECZNIE - w pamieci sa jako tekst
let licznik = Number(localStorage.getItem("licznik"));
licznik++;
localStorage.setItem("licznik", licznik);
// Obiekty i tablice zapisujesz przez JSON
const koszyk = [
{ nazwa: "Mysz", cena: 79 },
{ nazwa: "Monitor", cena: 899 }
];
localStorage.setItem("koszyk", JSON.stringify(koszyk)); // obiekt -> tekst
const wczytany = JSON.parse(localStorage.getItem("koszyk")); // tekst -> obiekt
console.log(wczytany[1].nazwa); // Monitor
// SESSION STORAGE - identyczne metody, inny czas zycia
sessionStorage.setItem("krok", "2");
console.log(sessionStorage.getItem("krok"));
// CIASTECZKA - jedna wlasciwosc tekstowa document.cookie
// Zapis: nazwa=wartosc, po srednikach atrybuty
document.cookie = "jezyk=pl; max-age=2592000; path=/"; // 30 dni w sekundach
// Odczyt zwraca WSZYSTKIE ciasteczka w jednym tekscie
console.log(document.cookie); // "jezyk=pl; zgoda=tak"
// Dlatego odczyt pojedynczego wymaga rozbicia tekstu
function pobierzCookie(nazwa) {
const pary = document.cookie.split("; ");
for (const para of pary) {
const czesci = para.split("=");
if (czesci[0] === nazwa) return czesci[1];
}
return null;
}
console.log(pobierzCookie("jezyk")); // pl
// Usuniecie = ustawienie daty waznosci w przeszlosci
document.cookie = "jezyk=; max-age=0; path=/";
Najkrótsza odpowiedź na pytanie „czym różni się cookie od Local Storage":
Ciasteczko jest małe (około 4 KB), ma datę wygaśnięcia i jest
automatycznie dołączane do każdego żądania HTTP, więc serwer
może je odczytać — na tym opierają się sesje. Local Storage jest znacznie
pojemniejszy, nie wygasa i nigdy nie opuszcza przeglądarki —
serwer o nim nie wie, dopóki skrypt sam mu tych danych nie wyśle. Żadnego
z tych mechanizmów nie wolno używać do haseł ani danych wrażliwych, bo
użytkownik i skrypty na stronie mają do nich pełny dostęp.
- Brak preventDefault w obsłudze submit. Strona przeładowuje się, komunikat błyska na ułamek sekundy i znika. To najczęstszy powód utraty punktów w zadaniu z walidacją.
- Zapominanie, że .value zwraca tekst. Bez
Number()lubparseInt()plus skleja zamiast dodawać, a porównanie liczbowe daje zaskakujące wyniki. - Mylenie == z ===. Zapis
"0" == falsedaje true, a"0" === false— false. W kodzie egzaminacyjnym stosuj wyłącznie porównanie ścisłe. - Skrypt w head bez defer.
getElementByIdzwraca wtedy null i pojawia się błąd „cannot read properties of null". - Selektor podany w złej formie.
getElementById("#login")nie zadziała — tu podaje się samo id, krzyżyk należy do querySelector. - Mylenie slice ze splice. slice zwraca fragment i nie rusza oryginału, splice wycina z oryginału. Drugi argument splice to liczba elementów, nie indeks.
- Traktowanie forEach jak map. forEach zwraca undefined, więc przypisanie jego wyniku do zmiennej to gwarantowany błąd logiczny.
- Uznanie walidacji w JavaScripcie za zabezpieczenie. To tylko wygoda dla użytkownika. Dane zawsze weryfikuje jeszcze raz serwer.
JavaScript wchodzi dopiero wtedy, gdy sam napiszesz walidację formularza bez zaglądania do notatek i bezbłędnie przewidzisz wynik kilku porównań. Rozwiąż test z tego działu i zobacz, gdzie jeszcze masz luki.