Przejdź do treści

Temat 34 · 2 × 45 min

jQuery – czytanie i porównanie z czystym JS

Otwórz źródło losowej strony firmowej, sklepu na WordPressie albo projektu z 2015 roku i zobaczysz $('#menu').slideToggle(). jQuery to biblioteka, która przez ponad dekadę była synonimem „JavaScriptu na stronie” – według W3Techs w październiku 2026 r. nadal działa na ok. 64 % wszystkich witryn. Nowych projektów raczej w niej nie zaczniesz, ale czytać ją musisz: w pracy odziedziczysz taki kod, a podstawa programowa INF.03 wymienia jQuery z nazwy. Ta lekcja uczy czytać jQuery przez porównanie z czystym JavaScriptem, który już znasz z lekcji 11–17 i 27.

Czym jest jQuery i dlaczego podbiło internet

jQuery (2006, John Resig) to jeden plik .js (ok. 30 kB po kompresji), który dodaje do strony funkcję $. W 2006–2015 rozwiązywała trzy realne problemy:

  1. Różnice między przeglądarkami. Internet Explorer 6–8 miał inne nazwy metod (attachEvent zamiast addEventListener), inny model zdarzeń, brak querySelector. jQuery ukrywało to za jednym API.
  2. Rozwlekłość DOM. document.getElementsByClassName('karta') plus pętla, żeby zmienić kolor – w jQuery $('.karta').css('color', 'red').
  3. AJAX przez XMLHttpRequest był koszmarnie niewygodny; $.ajax i $.getJSON dawały to w jednej linii.

Potem przeglądarki dogoniły bibliotekę: querySelectorAll (2009), classList (2011), fetch (2015), addEventListener wszędzie od IE9. Dziś prawie każda linia jQuery ma równie krótki odpowiednik w czystym JS, a frameworki (React, Vue) rozwiązują problem, którego jQuery nie dotykało – synchronizację danych z widokiem. Dlatego nowy projekt w 2026 r. nie zaczyna się od jQuery, ale istniejące strony nie przestały działać i ktoś musi je utrzymywać.

Aktualne wersje: 3.7.1 (sierpień 2023, najczęściej spotykana) i 4.0.0 (styczeń 2026 – usunięto wsparcie IE 10 i starszych oraz przestarzałe funkcje typu $.trim, $.isArray; wszystko z tej lekcji działa w obu).

Dołączanie biblioteki

index.html
<!-- z CDN – wymaga internetu -->
<script src="https://code.jquery.com/jquery-3.7.1.min.js"></script>
<!-- albo lokalnie – plik pobrany raz, działa offline -->
<script src="js/jquery-3.7.1.min.js"></script>

<!-- twój kod ZAWSZE po bibliotece -->
<script src="js/app.js"></script>

Po załadowaniu masz globalną funkcję jQuery i jej alias $. Starszy kod zaczyna się od „poczekaj, aż DOM będzie gotowy”:

app.js
$(document).ready(function () { /* … */ });   // wersja z 2010
$(function () { /* … */ });                   // skrót – to samo
// czysty JS: document.addEventListener('DOMContentLoaded', () => { … });  albo <script defer>

W placach zabaw tej lekcji jQuery jest dołączone w zakładce HTML – zajrzyj tam, zobaczysz ten sam tag <script src>.

$() i selektory

$(selektor) przyjmuje selektor CSS – taki sam jak w querySelectorAll – i zwraca obiekt jQuery: opakowanie na zero, jeden lub wiele elementów. Na tym obiekcie wołasz metody jQuery, a każda z nich działa na wszystkich znalezionych elementach i (zwykle) zwraca ten sam obiekt, więc wywołania można łańcuchować.

jQueryCzysty JS
$('#wynik')document.querySelector('#wynik') / getElementById('wynik')
$('.karta')document.querySelectorAll('.karta')
$('ul li:first-child')document.querySelectorAll('ul li:first-child')
$('.karta').first() / .eq(2)karty[0] / karty[2]
$('#lista').find('li')lista.querySelectorAll('li')
$('#x').parent() / .closest('.karta')x.parentElement / x.closest('.karta')
$('#x').lengthx !== null ? 1 : 0
Sprawdź się

Co wypisze ten kod, jeśli na stronie są trzy elementy z klasą karta?

console.log($('.karta').length, document.querySelector('.karta').length);
Selektory i łańcuchowanie
// jQuery: jeden łańcuch działa na wszystkich li
$('#oceny li').css('color', '#555').first().css('font-weight', 'bold');
$('#info').text('Pozycji: ' + $('#oceny li').length);

// to samo w czystym JS
const pozycje = document.querySelectorAll('#oceny li');
pozycje.forEach((li) => (li.style.color = '#555'));
pozycje[0].style.fontWeight = 'bold';
document.getElementById('info').textContent += ' | vanilla: ' + pozycje.length;
Podgląd

Zdarzenia: .on()

zdarzenia.js
// jQuery
$('#dodaj').on('click', function () {
  console.log(this);            // this = kliknięty element DOM (zwykła funkcja, nie strzałka!)
  $(this).addClass('aktywny');  // żeby użyć metod jQuery, zapakuj this w $()
});
$('#dodaj').click(function () {}); // skrót spotykany w starym kodzie – to samo co .on('click')

// czysty JS
document.getElementById('dodaj').addEventListener('click', function (e) {
  e.currentTarget.classList.add('aktywny');
});

Najcenniejsza rzecz w .on() to wbudowana delegacja (lekcja 27): drugi argument to selektor dzieci, a nasłuchiwacz siedzi na rodzicu – działa też dla elementów dodanych później.

delegacja.js
// jQuery: jeden nasłuchiwacz na liście, reaguje na każde li – także przyszłe
$('#lista').on('click', 'li', function () {
  $(this).toggleClass('wykonane');
});

// czysty JS
document.getElementById('lista').addEventListener('click', (e) => {
  const li = e.target.closest('li');
  if (li) li.classList.toggle('wykonane');
});

.off('click') zdejmuje nasłuchiwacze; .trigger('click') wywołuje zdarzenie programowo (vanilla: el.click() lub dispatchEvent).

Treść i wartości: .text(), .html(), .val()

Metody jQuery są jednocześnie getterem i setterem: bez argumentu czytają, z argumentem ustawiają. To najczęstsze źródło pomyłek przy przepisywaniu – w czystym JS to właściwości, nie funkcje.

jQueryCzysty JSUwagi
$('#p').text()p.textContentodczyt
$('#p').text('Suma: 5')p.textContent = 'Suma: 5'zapis, bez interpretacji HTML
$('#p').html('<b>5</b>')p.innerHTML = '<b>5</b>'HTML interpretowany
$('#pole').val()pole.valuezawsze napis – jak w czystym JS
$('#pole').val('')pole.value = ''czyszczenie
$('#zgoda').prop('checked')zgoda.checkedtrue/false
$('#img').attr('src', 'b.jpg')img.src = 'b.jpg' / setAttributeatrybuty
Sprawdź się

Pole #ilosc zawiera 7. Co wypisze ten kod?

var ilosc = $('#ilosc').val();
console.log(ilosc + 3, typeof ilosc);
Kalkulator w dwu dialektach
// jQuery
$('#oblicz-jq').on('click', function () {
  var cena = parseFloat($('#cena').val());
  var kg = parseFloat($('#kg').val());
  $('#wynik').text('Koszt: ' + (cena * kg).toFixed(2) + ' zł').css('color', 'navy');
});

// czysty JS – identyczny efekt
document.getElementById('oblicz-js').addEventListener('click', () => {
  const cena = parseFloat(document.getElementById('cena').value);
  const kg = parseFloat(document.getElementById('kg').value);
  const wynik = document.getElementById('wynik');
  wynik.textContent = `Koszt: ${(cena * kg).toFixed(2)} zł`;
  wynik.style.color = 'darkgreen';
});
Podgląd

Style i klasy: .css(), .addClass(), .toggleClass()

jQueryCzysty JS
$('#p').css('color', 'red')p.style.color = 'red'
$('#p').css({ color: 'red', fontSize: '20px' })Object.assign(p.style, { color: 'red', fontSize: '20px' })
$('#p').css('color')getComputedStyle(p).color
$('#p').addClass('aktywny')p.classList.add('aktywny')
$('#p').removeClass('aktywny')p.classList.remove('aktywny')
$('#p').toggleClass('aktywny')p.classList.toggle('aktywny')
$('#p').hasClass('aktywny')p.classList.contains('aktywny')

Różnica praktyczna: .css('background-color', …) przyjmuje nazwę CSS z myślnikiem lub camelCase – obie działają. W style.* musi być camelCase (backgroundColor). Jedna pułapka mniej w jQuery, ale za to kolejna rzecz, którą trzeba pamiętać przy przepisywaniu.

Zakładki: jQuery vs vanilla
// wersja jQuery
$('nav').on('click', '.zakladka', function () {
  if ($('#tryb').prop('checked')) return;            // przełącznik tylko do porównania
  $('.zakladka').removeClass('aktywna');
  $(this).addClass('aktywna');
  $('.panel').hide();
  $('#' + $(this).data('cel')).show();
});

// wersja vanilla – ten sam efekt
document.querySelector('nav').addEventListener('click', (e) => {
  const przycisk = e.target.closest('.zakladka');
  if (!przycisk || !document.getElementById('tryb').checked) return;
  document.querySelectorAll('.zakladka').forEach((z) => z.classList.remove('aktywna'));
  przycisk.classList.add('aktywna');
  document.querySelectorAll('.panel').forEach((p) => {
    p.hidden = true;
    p.style.display = ''; // usuń styl wpisany wcześniej przez .hide()/.show() jQuery
  });
  document.getElementById(przycisk.dataset.cel).hidden = false;
});
Podgląd

Pokazywanie i ukrywanie: .hide(), .show(), .toggle(), .fadeIn()

Tu jQuery nadal wygląda wygodniej, bo ma wbudowane animacje:

jQueryCzysty JS
$('#p').hide()p.style.display = 'none' lub p.hidden = true
$('#p').show()p.style.display = '' lub p.hidden = false
$('#p').toggle()p.hidden = !p.hidden
$('#p').fadeIn(400) / .fadeOut(400)klasa + CSS transition: opacity .4s (lekcja 28)
$('#p').slideToggle()klasa + transition na max-height / grid-template-rows

.hide() zapamiętuje poprzednie display, więc .show() przywraca flex czy inline-block, nie zawsze block. W czystym JS hidden robi to samo prościej – chyba że CSS nadpisuje display elementu; wtedy użyj klasy .ukryty { display: none }.

Komunikat z animacją
$('#jq').on('click', function () {
  $('#kom-jq').fadeToggle(400);          // animuje opacity i na końcu ustawia display:none
});

document.getElementById('js').addEventListener('click', () => {
  document.getElementById('kom-js').classList.toggle('widoczny');
});
Podgląd

Tworzenie elementów: .append(), .prepend(), .remove(), .each()

lista.js
// jQuery – element z napisu HTML
$('#lista').append('<li class="nowe">' + tytul + '</li>');
$('#lista').prepend($('<li>').text(tytul));       // $('<li>') tworzy element; .text() chroni przed HTML w danych
$('#lista li.wykonane').remove();
$('#lista').empty();                               // usuń wszystkie dzieci

// czysty JS
const li = document.createElement('li');
li.className = 'nowe';
li.textContent = tytul;
lista.append(li);                                  // append przyjmuje też napisy: lista.append('tekst')
lista.querySelectorAll('li.wykonane').forEach((el) => el.remove());
lista.replaceChildren();                           // = empty()

.each() to pętla po elementach obiektu jQuery. Kolejność argumentów jest odwrotna niż w forEach – najpierw indeks, potem element (zwykły element DOM, nie obiekt jQuery):

each.js
$('#lista li').each(function (indeks, element) {
  $(element).text((indeks + 1) + '. ' + $(element).text());   // this === element
});

document.querySelectorAll('#lista li').forEach((element, indeks) => {
  element.textContent = `${indeks + 1}. ${element.textContent}`;
});
Sprawdź się

Co zrobi ten kod?

$('#lista li').each(function (a, b) {
console.log(a);
});
Lista zadań w jQuery
function odswiezLicznik() {
  $('#licznik').text('Wykonane: ' + $('#lista li.wykonane').length + ' z ' + $('#lista li').length);
}

$('#form').on('submit', function (e) {
  e.preventDefault();
  var tytul = $('#tytul').val().trim();
  if (!tytul) return;
  $('#lista').append($('<li>').text(tytul).hide().fadeIn(300));
  $('#tytul').val('');
  odswiezLicznik();
});

// delegacja: li dodane później też reagują
$('#lista').on('click', 'li', function () {
  $(this).toggleClass('wykonane');
  odswiezLicznik();
});
Podgląd

Przepisanie tego placu zabaw na czysty JS to zadanie 3.

$.ajax vs fetch

Przed fetch jQuery było głównym sposobem na AJAX. W starym kodzie spotkasz trzy formy:

ajax.js
// 1. skrót do GET z JSON
$.getJSON('/api/zadania', function (dane) {
  console.log(dane);
});

// 2. pełna forma z callbackami (w kodzie z lat 2010–2015)
$.ajax({
  url: '/api/zadania',
  method: 'POST',
  contentType: 'application/json',
  data: JSON.stringify({ tytul: 'Nowe' }),
  success: function (odpowiedz) { console.log(odpowiedz); },
  error: function (xhr) { console.log('Błąd ' + xhr.status); },
});

// 3. forma „promise-like” (od jQuery 1.5) – .done/.fail zamiast then/catch
$.ajax({ url: '/api/zadania' }).done(function (dane) { /* … */ }).fail(function () { /* … */ });

Odpowiednik z lekcji 30 i 31:

fetch.js
const res = await fetch('/api/zadania', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({ tytul: 'Nowe' }),
});
if (!res.ok) throw new Error(`Błąd ${res.status}`);
const odpowiedz = await res.json();

Dwie różnice, które gryzą przy przepisywaniu: $.ajax sam parsuje JSON (gdy dataType: 'json' lub serwer wysyła Content-Type: application/json) i traktuje 404/500 jako błąd (error/fail). fetch wymaga await res.json() i uznaje 404 za poprawną odpowiedź – stąd obowiązkowe if (!res.ok).

$.getJSON vs fetch
var URL_TODO = 'https://jsonplaceholder.typicode.com/todos/1';

$('#jq').on('click', function () {
  $.getJSON(URL_TODO)
    .done(function (todo) { $('#out').text('jQuery: ' + JSON.stringify(todo)); })
    .fail(function (xhr) { $('#out').text('Błąd ' + xhr.status); });
});

document.getElementById('js').addEventListener('click', async () => {
  const out = document.getElementById('out');
  try {
    const res = await fetch(URL_TODO);
    if (!res.ok) throw new Error(res.status);
    out.textContent = 'fetch: ' + JSON.stringify(await res.json());
  } catch (e) {
    out.textContent = 'Błąd ' + e.message;
  }
});
Podgląd

Tabela: jQuery obok czystego JS

ZadaniejQueryCzysty JS (2026)
gotowy DOM$(function () {…})<script defer> lub DOMContentLoaded
jeden element$('#id')document.getElementById('id')
wiele elementów$('.k')document.querySelectorAll('.k')
zdarzenie.on('click', fn).addEventListener('click', fn)
delegacja.on('click', 'li', fn)addEventListener + e.target.closest('li')
tekst.text() / .text(x).textContent
HTML.html() / .html(x).innerHTML
wartość pola.val() / .val(x).value
checkbox.prop('checked').checked
atrybut.attr('src', x).src = x / .setAttribute
styl.css('color', x).style.color = x
klasy.addClass/.removeClass/.toggleClass/.hasClass.classList.add/remove/toggle/contains
ukryj/pokaż.hide()/.show()/.toggle().hidden = true/false
animacja.fadeIn()/.slideToggle()klasa + CSS transition
dodaj element.append('<li>…</li>')createElement + .append()
usuń.remove() / .empty().remove() / .replaceChildren()
pętla.each(function (i, el) {…}).forEach((el, i) => {…})
rodzic / przodek.parent() / .closest(sel).parentElement / .closest(sel)
GET JSON$.getJSON(url, cb)await (await fetch(url)).json()
POST JSON$.ajax({ method:'POST', data: JSON.stringify(o), contentType:'application/json' })fetch(url, { method:'POST', headers:{…}, body: JSON.stringify(o) })

Czytanie starego kodu: komentarz linia po linii

Taki fragment znajdziesz w tysiącach motywów WordPress. Przeczytaj go, zanim spojrzysz na komentarze:

legacy.js
$(document).ready(function () {
  $('.menu-toggle').click(function (e) {
    e.preventDefault();
    $(this).toggleClass('open');
    $('#menu').slideToggle(300);
  });
  $('#menu a').each(function () {
    if ($(this).attr('href') === window.location.pathname) {
      $(this).parent().addClass('current');
    }
  });
  $('.alert .close').on('click', function () {
    $(this).closest('.alert').fadeOut(200, function () { $(this).remove(); });
  });
});
  • $(document).ready(fn) – uruchom po zbudowaniu DOM.
  • .menu-toggle + .click – przycisk hamburgera: preventDefault (to zwykle <a href="#">), przełącz klasę open na przycisku, rozwiń/zwiń #menu z animacją 300 ms.
  • .each po linkach menu: jeśli href równa się aktualnej ścieżce, rodzicowi (li) dodaj klasę current – podświetlenie bieżącej strony.
  • Zamknięcie alertu: znajdź najbliższy przodek .alert, wygaś go, a po zakończeniu animacji (callback fadeOut) usuń z DOM.

Ten sam fragment w czystym JS – zadanie 4 każe ci to napisać; porównaj potem z własną wersją.

Pułapki

Ściąga

SkładniaCo robiVanilla
<script src="https://code.jquery.com/jquery-3.7.1.min.js">dołącz bibliotekę (przed własnym kodem)–
$(fn)po załadowaniu DOMdefer / DOMContentLoaded
$(sel)zbiór elementów (ma .length)querySelectorAll(sel)
$(sel).on('click', fn)zdarzenie; this = elementaddEventListener
$(sel).on('click', 'li', fn)delegacjaclosest('li') w nasłuchiwaczu
.text(x) / .html(x) / .val(x)setter; bez argumentu getter.textContent / .innerHTML / .value
.css('prop', x)styl (nazwa z myślnikiem lub camelCase).style.prop (camelCase)
.addClass/.removeClass/.toggleClass/.hasClassklasy.classList.*
.hide()/.show()/.toggle()display: none i z powrotem.hidden
.fadeIn(ms)/.fadeOut(ms)/.slideToggle(ms)animacjeCSS transition + klasa
.append(x)/.prepend(x)/.remove()/.empty()drzewo DOM.append/.prepend/.remove/.replaceChildren
.each(function (i, el) {…})pętla (indeks, element).forEach((el, i) => …)
$(el) / $(sel)[0]element → jQuery / jQuery → element–
$.getJSON(url, cb) / $.ajax({...})AJAX; 404 = błąd; JSON parsowanyfetch + res.ok + res.json()

Zadania

Zadanie 1
łatwe

Przetłumacz dziesięć linii

Przepisz każdą linię na czysty JS (zakładamy, że selektory trafiają w pojedyncze elementy, poza .karta):

do-przepisania.js
$('#tytul').text('Sklep');
$('#cena').val('99.99');
$('#opis').html('<b>Nowość</b>');
$('#baner').hide();
$('.karta').addClass('wyrozniona');
$('#przycisk').on('click', pokazKoszyk);
$('#zdjecie').attr('src', 'duze.jpg');
$('#komunikat').css('color', 'red');
var zaznaczony = $('#zgoda').prop('checked');
$('#lista').append('<li>Nowy</li>');
Pokaż rozwiązanie
przepisane.js
document.getElementById('tytul').textContent = 'Sklep';
document.getElementById('cena').value = '99.99';
document.getElementById('opis').innerHTML = '<b>Nowość</b>';
document.getElementById('baner').hidden = true;             // lub .style.display = 'none'
document.querySelectorAll('.karta').forEach((k) => k.classList.add('wyrozniona'));
document.getElementById('przycisk').addEventListener('click', pokazKoszyk);
document.getElementById('zdjecie').src = 'duze.jpg';        // lub setAttribute('src', 'duze.jpg')
document.getElementById('komunikat').style.color = 'red';
const zaznaczony = document.getElementById('zgoda').checked;

const li = document.createElement('li');                     // bezpieczniej niż innerHTML +=
li.textContent = 'Nowy';
document.getElementById('lista').append(li);

Uwaga do linii 5: w jQuery jedna metoda działa na całym zbiorze; w czystym JS querySelectorAll zwraca kolekcję, więc potrzebna jest pętla.

Zadanie 2
średnie

Galeria z lekcji 17 w jQuery

Weź galerię < / > z zawijaniem indeksu 1–7 z lekcji 17 (obrazy 1.jpg…7.jpg, duży <img id="duzy">, podpis <p id="numer">). Napisz ją w jQuery (dialekt z lat 2012–2015: var, function, .click, .attr, .text), a potem policz linie w obu wersjach. Czy jQuery jest wyraźnie krótsze?

Pokaż rozwiązanie
galeria.html
<script src="https://code.jquery.com/jquery-3.7.1.min.js"></script>
<button id="poprzedni">&lt;</button>
<img id="duzy" src="1.jpg" alt="Zdjęcie 1" width="300">
<button id="nastepny">&gt;</button>
<p id="numer">1 / 7</p>
galeria-jquery.js
var biezacy = 1;
var ILE = 7;

function pokaz() {
  $('#duzy').attr('src', biezacy + '.jpg').attr('alt', 'Zdjęcie ' + biezacy);
  $('#numer').text(biezacy + ' / ' + ILE);
}

$('#nastepny').click(function () {
  biezacy = biezacy % ILE + 1;            // 7 → 1
  pokaz();
});

$('#poprzedni').click(function () {
  biezacy = biezacy === 1 ? ILE : biezacy - 1;
  pokaz();
});
galeria-vanilla.js
let biezacy = 1;
const ILE = 7;
const duzy = document.getElementById('duzy');
const numer = document.getElementById('numer');

function pokaz() {
  duzy.src = `${biezacy}.jpg`;
  duzy.alt = `Zdjęcie ${biezacy}`;
  numer.textContent = `${biezacy} / ${ILE}`;
}

document.getElementById('nastepny').addEventListener('click', () => {
  biezacy = (biezacy % ILE) + 1;
  pokaz();
});

document.getElementById('poprzedni').addEventListener('click', () => {
  biezacy = biezacy === 1 ? ILE : biezacy - 1;
  pokaz();
});

Obie wersje mają po kilkanaście linii – logika (indeks, zawijanie) jest identyczna, bo jQuery nie dotyka logiki, tylko dostępu do DOM. Różnica w długości jest kosmetyczna; różnica w zależnościach – 30 kB biblioteki z sieci kontra zero.

Zadanie 3
średnie

Lista zadań: z jQuery na vanilla

Przepisz plac zabaw „Lista zadań w jQuery” z tej lekcji na czysty JS. Zachowaj: dodawanie przez submit (z preventDefault), delegację kliknięć na ul, przełączanie klasy wykonane, licznik Wykonane: X z Y oraz efekt pojawiania się nowego li (użyj klasy i transition, nie fadeIn).

Pokaż rozwiązanie
Lista zadań – vanilla
const form = document.getElementById('form');
const pole = document.getElementById('tytul');
const lista = document.getElementById('lista');
const licznik = document.getElementById('licznik');

function odswiezLicznik() {
  const wszystkie = lista.querySelectorAll('li').length;
  const wykonane = lista.querySelectorAll('li.wykonane').length;
  licznik.textContent = `Wykonane: ${wykonane} z ${wszystkie}`;
}

form.addEventListener('submit', (e) => {
  e.preventDefault();
  const tytul = pole.value.trim();
  if (!tytul) return;
  const li = document.createElement('li');
  li.textContent = tytul;                 // odpowiednik $('<li>').text(tytul)
  lista.append(li);
  void li.offsetWidth;                    // wymuś obliczenie stylu startowego (opacity: 0)
  li.classList.add('widoczne');           // dopiero teraz zmiana uruchomi transition
  pole.value = '';
  odswiezLicznik();
});

// delegacja: odpowiednik $('#lista').on('click', 'li', …)
lista.addEventListener('click', (e) => {
  const li = e.target.closest('li');
  if (!li) return;
  li.classList.toggle('wykonane');
  odswiezLicznik();
});
Podgląd

Linia void li.offsetWidth jest potrzebna, bo klasa dodana zaraz po wstawieniu elementu nie uruchomi transition – przeglądarka musi najpierw „zobaczyć” stan opacity: 0. Odczyt offsetWidth zmusza ją do policzenia stylów (pojedynczy requestAnimationFrame nie wystarcza, bo jego callback wykonuje się jeszcze przed pierwszym obliczeniem stylów nowego elementu). To jedyny fragment, w którym fadeIn jQuery jest realnie wygodniejszy.

Zadanie 4
trudne

Legacy menu bez jQuery

Przepisz fragment legacy.js z sekcji „Czytanie starego kodu” na czysty JS tak, by motyw WordPress mógł pozbyć się biblioteki. Wymagania: slideToggle zastąp klasą open na #menu i CSS z transition na max-height; podświetlenie bieżącej strony ma działać dla wielu linków; zamknięcie alertu ma usunąć element z DOM po zakończeniu animacji wygaszania (zdarzenie transitionend). Kod ma działać przy <script defer>.

Pokaż rozwiązanie
menu.css
#menu { max-height: 0; overflow: hidden; transition: max-height 0.3s ease; }
#menu.open { max-height: 500px; }
.alert { transition: opacity 0.2s; }
.alert.znika { opacity: 0; }
menu.js
// <script src="menu.js" defer> – DOM gotowy, bez $(document).ready

// 1. hamburger: przełącz klasę na przycisku i rozwiń/zwiń menu
for (const toggle of document.querySelectorAll('.menu-toggle')) {
  toggle.addEventListener('click', (e) => {
    e.preventDefault();
    toggle.classList.toggle('open');
    document.getElementById('menu').classList.toggle('open');
  });
}

// 2. podświetl link do bieżącej strony (jak .each + .attr('href'))
for (const link of document.querySelectorAll('#menu a')) {
  if (link.getAttribute('href') === window.location.pathname) {
    link.parentElement.classList.add('current');
  }
}

// 3. zamknięcie alertu: wygaś, a po animacji usuń (jak callback fadeOut)
for (const close of document.querySelectorAll('.alert .close')) {
  close.addEventListener('click', () => {
    const alert = close.closest('.alert');
    alert.classList.add('znika');
    alert.addEventListener('transitionend', () => alert.remove(), { once: true });
  });
}

Uwagi: link.getAttribute('href') zamiast link.href, bo .href zwraca pełny adres z domeną, a jQuery .attr('href') – dokładnie to, co jest w HTML. { once: true } zdejmuje nasłuchiwacz po pierwszym użyciu – element i tak znika. Jeśli alert mógłby nie mieć transition (starsza przeglądarka, prefers-reduced-motion), dodaj zapasowe setTimeout(() => alert.remove(), 250).

Test powtórkowy

Test powtórkowy

Dziewięć pytań o jQuery i jego odpowiedniki w czystym JS. Jedna poprawna odpowiedź.

0/0poprawnych
Sprawdź się

Który zapis jQuery odpowiada document.getElementById('wynik').innerHTML = 'Suma: 5'?

Sprawdź się

Co zwraca $('#pole').val() dla pola type=number z wpisaną wartością 5?

Sprawdź się

Który kod w czystym JS odpowiada $('.karta').addClass('aktywna')?

Sprawdź się

Co robi $('#lista').on('click', 'li', fn)?

Sprawdź się

Dlaczego ten kod nie działa?

$('#baner').style.display = 'none';
Sprawdź się

W .each(function (a, b) {…}) czym są a i b?

Sprawdź się

Czym $.ajax różni się od fetch przy odpowiedzi 404?

Sprawdź się

Co się stanie na stanowisku egzaminacyjnym INF.03 ze skryptem zaczynającym się od $('#oblicz').on('click', …) i tagiem script z CDN?

Sprawdź się

Który zapis uruchamia kod po zbudowaniu DOM w jQuery?