Dlaczego nowe projekty zaczynam bez jQuery – i dlaczego to nie jest atak na jQuery
Zacznę od wyznania, nie od tezy. Kiedy na dobre pojawiło się PHP, pisałem w Perlu. Skrypty w cgi-bin, formularze, generowane strony – wszystko działało i wszystko rozumiałem. PHP wyglądało jak zabawka dla tych, którzy nie chcieli nauczyć się porządnego CGI. Trzymałem się swojego tak długo, że na PHP spojrzałem serio dopiero koło wydania czwartego – kiedy pół internetu stawiało już strony w godzinę, a ja wciąż dłubałem w tym, co znałem.
Nie było w tym żadnej głębokiej analizy. Był odruch: nauczyłem się czegoś raz, sprawdza się, więc będę tak pisał dalej – a że jest coś nowego, niech sobie będzie. Ten odruch nie jest głupi. Zwykle jest racjonalny: znane narzędzie działa, a nauka nowego kosztuje. Problem zaczyna się dopiero wtedy, gdy odruch przestaje być decyzją, a staje się nawykiem, którego już się nie kwestionuje.
Piszę o tym, bo ten sam mechanizm widzę dziś przy jQuery. I chcę od razu postawić sprawę jasno: to nie jest tekst „jQuery jest złe”. jQuery było jednym z najważniejszych kawałków oprogramowania w historii weba i rozwiązywało prawdziwe problemy. Ten wpis jest o czymś innym – o tym, dlaczego przy nowym projekcie warto sprawdzić, czy te problemy jeszcze istnieją, zanim odruchowo sięgnie się po $.
Po co naprawdę braliśmy jQuery
Żeby zrozumieć, czego jQuery już nie musi robić, trzeba pamiętać, po co je braliśmy. A braliśmy z trzech konkretnych powodów, i wszystkie trzy były w swoim czasie palące.
Po pierwsze: przeglądarki były niezgodne. W czasach IE6, IE7 i wczesnego Firefoksa ten sam kod działał różnie albo nie działał wcale. addEventListener w jednej przeglądarce, attachEvent w drugiej. jQuery brało tę niespójność na siebie – pisałeś $(el).on('click', …) i biblioteka martwiła się resztą.
Po drugie: goły DOM API był rozwlekły. Zanim pojawił się querySelectorAll, wyszukanie elementów po klasie oznaczało pętlę po getElementsByTagName z ręcznym sprawdzaniem className. $('.card') było po prostu krótsze i czytelniejsze.
Po trzecie: AJAX był koszmarem. XMLHttpRequest z ręcznym sprawdzaniem readyState, obsługą stanów i różnicami między przeglądarkami. $.ajax chowało to wszystko za jedną funkcją.
Zapamiętaj te trzy powody, bo za chwilę przejdziemy je po kolei i zobaczymy, co się z każdym stało.
Co się zmieniło
Niezgodność przeglądarek – problem, którego już nie ma
To jest najważniejszy punkt, bo to był pierwotny powód istnienia jQuery. Dziś rynek przeglądarek to trzy silniki (Blink w Chrome i Edge, Gecko w Firefoksie, WebKit w Safari), które aktualizują się automatycznie i w drodze Interop celowo zbliżają swoje zachowania. IE6 i cała jego rodzina to historia. Kod napisany na gołym API działa dziś tak samo we wszystkich trzech – a to dokładnie ta warstwa, którą jQuery brało na siebie.
Innymi słowy: filar, na którym stała cała biblioteka, po prostu zniknął.
Wyszukiwanie i manipulacja DOM
querySelector i querySelectorAll przyjmują dowolny selektor CSS – dokładnie tak, jak $():
// jQuery
$('.card').addClass('active');
// Goła przeglądarka
document.querySelectorAll('.card').forEach((el) => el.classList.add('active'));Trochę dłużej, owszem. Ale bez zależności, i – co ważniejsze – to jest umiejętność, która nie zestarzeje się razem z wersją biblioteki. classList daje add, remove, toggle, contains; textContent i innerHTML obsługują treść; append, prepend, remove i replaceWith – strukturę.
Zdarzenia i delegacja
Sztandarowy argument za jQuery brzmiał: „ale delegacja zdarzeń!”. Chodzi o wzorzec, w którym jeden listener na kontenerze obsługuje kliknięcia w elementy dodane później. To był $(document).on('click', '.item', …). Goły odpowiednik to addEventListener plus closest():
// Jeden listener na liście obsługuje kliknięcie w dowolny .item,
// także taki dodany do DOM już po podpięciu listenera.
list.addEventListener('click', (e) => {
const item = e.target.closest('.item');
if (!item) return;
// obsługa kliknięcia w item
});closest() idzie w górę drzewa, szukając pasującego selektora – to cała magia delegacji, wbudowana w przeglądarkę.
AJAX – z $.ajax na fetch
Trzeci z pierwotnych powodów. fetch jest w każdej przeglądarce od lat i jest przyjemniejszy niż $.ajax, bo oparty na obietnicach:
const res = await fetch('/api/posts');
if (!res.ok) throw new Error(`HTTP ${res.status}`);
const posts = await res.json();Anulowanie żądania (kiedyś nieoczywiste) daje AbortController. Nagłówki, strumienie, FormData – wszystko jest w standardzie.
To już przerabiałem na żywym projekcie
Nie piszę tego z pozycji teoretyka. Mam na blogu trylogię o kalendarzu w HTML, CSS i jQuery z 2024 roku – działający komponent, z którego jestem zadowolony do dziś. A obok niej ten sam kalendarz napisany od zera bez jednej zależności: Web Component, Intl.DateTimeFormat zamiast ręcznego formatowania, fetch zamiast $.ajax. Wyszło mniej kodu, zero paczek do aktualizowania i komponent, który wkleję do dowolnego projektu bez ciągnięcia biblioteki za sobą.
Szerszy przegląd „co z projektu można dziś usunąć” – nie tylko jQuery, ale też moment.js, lodash czy Popper – zebrałem w osobnym wpisie Platforma webowa dogoniła biblioteki. Tutaj chodzi mi o coś węższego: o sam odruch sięgania po $ w nowym projekcie.
Sekcja uczciwości: kiedy jQuery wciąż ma sens
Gdybym skończył na „usuwajcie jQuery”, byłbym dokładnie tym, kogo sam nie znoszę – ewangelistą nowego, który nie widzi kosztów. Więc uczciwie, kiedy jQuery jest właściwym wyborem:
- W istniejącym projekcie, który działa. To jest sedno całej serii. Stary projekt na jQuery to nie dług do spłacenia – to działający kod. Nie przepisuje się czegoś, co się sprawdza, tylko dlatego, że jest nowsza moda. Na tym blogu mam wprost zasadę: stare wpisy i stary kod zostają nietknięte, a modernizacja to zawsze nowy byt obok, nie przepisywanie w miejscu.
- Gdy zespół zna jQuery i nie zna reszty. Narzędzie, które zespół rozumie, bije narzędzie teoretycznie lepsze, którego nikt nie zna. To realny koszt, nie wymówka.
- Gdy projekt już ciągnie jQuery z innego powodu. Jeśli w projekcie siedzi wtyczka albo motyw wymagający jQuery (cały ekosystem WordPressa), to biblioteka i tak jest w pamięci. Dokładanie do niej kolejnego użycia nic nie kosztuje.
Wspólny mianownik: jQuery ma sens tam, gdzie już jest. Cała teza dotyczy momentu, w którym zaczynasz od czystej kartki i odruchowo dopisujesz je do package.json.
Puenta
Ja z Perlem przegrałem walkę z czasem, choć długo się broniłem. PHP nie było lepsze dlatego, że nowsze – było lepsze do tego, co wtedy robiłem, a ja tego nie widziałem, bo patrzyłem przez nawyk. Kiedy w końcu spojrzałem, żałowałem tych straconych lat dłubania w cgi-bin.
Z jQuery jest inaczej niż z Perlem, ale mechanizm w głowie ten sam. Nie chodzi o to, żeby porzucić to, co znasz. Chodzi o to, żeby przy nowym projekcie zadać sobie jedno pytanie: czy problem, który ta biblioteka rozwiązuje, jeszcze istnieje? Przy jQuery odpowiedź coraz częściej brzmi „nie”, bo trzy powody, dla których je braliśmy, przeglądarka pozałatwiała sama.
To pierwszy z trzech wpisów o odruchach, które kiedyś były rozsądkiem, a dziś bywają tylko przyzwyczajeniem. W kolejnym wezmę na warsztat coś węższego i bardziej zaskakującego: rzeczy, które piszemy w JavaScripcie, choć od dawna są w samym CSS.