jQuery

jQuery - biblioteka która zrobiła niemałe zamieszanie w internecie.

Jej popularność nie wzięła się znikąd. Po pierwsze jQuery bardzo upraszcza pisanie skryptów. Dzięki temu zamiast skupiać się jak napisać dany fragment kodu, skupiamy się na tym jak rozwiązać dany problem.
Druga sprawa to olbrzymie zaplecze gotowych pluginów. Chcesz użyć lightboxa na stronie? Dorzucasz plugin, dopisujesz 2-3 linijki kodu i masz lightbox. Chcesz użyć na stronie responsywnej karuzeli? Znowu - zastosujesz odpowiedni plugin, napiszesz kilka linii kodu i problem rozwiązany. Chcesz przeciąganie i zarządzanie listami? Proszę bardzo

Dzięki takiemu podejściu tworzenie projektów staje się naprawdę szybkie (a i zaletą jest to, że takie rozwiązania są spradzane przez setki osób).

Kolejną zaletą - w dzisiejszych czasach może mniej znaczącą - jest jednolitość kodu. Gdy jQuery wchodziła na rynek wciąż istniała na rynku spora rozbieżność w implementacji różnych "ficzerów". Dla przykładu - event DOMContentLoaded nie był wspierany przez wszystkie przeglądarki i najbezpieczniejszym rozwiązaniem było zastosować odpowiedni kod jQuery.

Rozpoczęcie pracy

Aby zacząć używać jQuery, będziemy musieli dołączyć jej plik do naszej strony. Możemy albo ściągnąć plik z biblioteką na dysk, albo odwołać się do wystawionej w necie wersji. Najlepiej skorzystać z serwera CDN jQuery (lub innego np. https://cdnjs.com/).
Wystarczy wstawić taką ścieżkę przed nasze skrypty po czym można zaczynać tworzyć.

Przy nauce jQuery bardzo pomocna będzie dokumentacja tej biblioteki znajdująca się pod adresem http://jqapi.com/

Znak $ i składnia łańcuchowa

jQuery ma bardzo zwięzłą konstrukcję, do której wykorzystuje składnię łańcuchową.
Większość opieracji będziemy wykonywać na obiektach pobranych ze strony. Aby pobrać obiekt ze strony wystarczy zastosować konstrukcję:


$('.selektor')....

$('#pierwszy').css({background : 'blue'}).delay(2000).slideUp().delay(1000).fadeIn(1000);

//wcale nie musisz wszystkiego pisać w jednej linii
$('#pierwszy')
    .css({
        background : 'blue'
    })
    .delay(2000)
    .slideUp()
    .delay(1000)
    .fadeIn(1000);

Powyższe polecenie wystarczy podzielić względem kropek:

  1. Pobieramy obiekt #pierwszy (tak jak za pomocą querySelectorAll)
  2. Ustawiamy mu kolor na niebieski
  3. Czekamy 2 sekundy
  4. Ukrywamy go (zwijamy do góry)
  5. Czekamy 1 sekundę
  6. Po czym pokazujemy go płynnie w czasie 1 sekundy

document ready

Tak samo jak w przypadku vanilla JS aby pobierać elementy ze strony, powinniśmy się upewnić, że drzewo DOM zostało już wczytane) korzystając z eventu DOMContentLoaded. jQuery upraszcza zapis tego eventu:


$(document).ready(function() {
    ...
})

//lub to samo w skróconej wersji

$(function() {
    ...
})

Osobiście zalecam pozostanie przy skróconej wersji.

Pobieranie elementu

Aby pobrać element ze strony, tak samo jak w przypadku querySelectorAll() korzystamy ze składni CSS:


$('div.czerwony') //pobranie divów o klasie czerwony
$('div p') //wszystkie p zawierające się w div
$('div#cnt > p') //wszystkie p mieszczące się bezpośrednio w div #cnt
$('p:first-of-type') //pobranie wszystkich p, które są pierwszymi elementami danego typu
$('img[width=100]') //pobranie wszystkich img, które mają atrybut width="100"
$(':checkbox ~ label') //wszystkie labele, które następują po checkboxach na danym poziomie drzewa

jQuery udostępnia nam też sporo metod dodatkowych, które są odpowiednikami selektorów CSS3.
Wśród nich najczęściej używanymi są:

eq(index) zwraca z listy element o danym indexie
first() zwraca pierwszy element z listy elementów
last() zwraca ostatni element z listy elementów
lt(index) zwraca elementy o indeksie mniejszym od danego
gt(index) zwraca elementy o indeksie większym od danego
not() zwraca element, jeżeli nie jest on ...
is() zwraca element, jeżeli jest on ...
filter() filtruje listę elementów przez zadane kryterium
add() dodaje do kolekcji inne elementy lub kolekcje

$('div.czerwony p').eq(2)
//lub
$('div.czerwony p:eq(2)')
//lub
$('div.czerwony p:nth-of-type(2)')

$('p').gt(2).addClass('with-border')
//lub
$('p:gt(2)').addClass('with-border')
//lub
$('p:nth-of-type(n+2)').addClass('with-border')

$('div').not('.red')
//lub
$('div:not(.red)')

$('div').is(':visible'); //wszystkie divy widoczne
$('div').not(':animated'); //wszystkie divy które nie są obecnie animowane

$('div').add('p'); //dodaje do kolekcji div elementy p

const $div = $('div');
const $p = $('p');
const $all = $div.add($p); //tutaj także dodaję 2 kolekcje

$('p').filter('.important'); //pobierz wszystkie p a potem odfiltruj z nich te z klasą .important

Szczególnie metoda filter(parametr) ma bardzo ciekawe zastosowanie. Jako parametr może ona dostać tekst będący selektorem, lub funkcję która zwraca prawdę lub fałsz. Jeżeli zostanie zwrócona prawda, oznacza to, że dany element tablicy pasuje i zostanie on zwrócony.


const $div = $('div');
$div.filter(':visible').hide() //ukrywa divy, które są widoczne

const $inputs = $('input');
$inputs.filter(':not(:checkbox)') //wszystkie inputy nie będące checboxami

const $div = $('div');
$div.filter(function() {
    return ($(this).children('span').length > 2)? true : false
}) //filtruje te divy, które zawierają min 3 spany

Jeżeli chcesz się dowiedzieć więcej na temat selektorów jquery, odwiedź stronę http://jqapi.com/#p=class-selector po czym przejść do zakładki Selectors > Basic filters (okazyjnie przeglądając i inne zakładki).

Poza wyżej wymienionymi metodami jQuery udostępnia nam metody do poruszania się po drzewie DOM.

$elem.parent(selektor*) Pobiera rodzica. Paremetr selektor jest opbjonalny. Jeżeli go nie podamy, zostanie pobrany dowolny rodzic elementu. Jeżeli go podamy, zostanie pobrany rodzic tylko gdy pasuje do selektora.
$elem.parents(selektor*) idzie w górę drzewa aż natrafi na rodzica który pasuje do selektora
$elem.closest(selektor*) idzie w górę drzewa aż natrafi na rodzica który pasuje do selektora
$elem.siblings() pobiera braci elementu
$elem.next(selektor*) pobiera następny element jeżeli pasuje do selektora. Można też pominąć ten parametr, wtedy pobierze dowolny następny element
$elem.nextUtil(selektor*) Pobiera wszystkie następne elementy dopóki nie natrafi na elemnt pasujący do selektora. Jeżeli nie podamy selektora, wszystkie następne elementy zostaną zwrócone
$elem.prev(selektor*) pobiera poprzedni element jeżeli pasuje do selektora. Można też pominąć ten parametr, wtedy pobierze dowolny poprzedni element
$elem.prevUntil(selektor*) Pobiera wszystkie poprzednie elementy dopóki nie natrafi na elemnt pasujący do selektora. Jeżeli nie podamy selektora, wszystkie poprzednie elementy zostaną zwrócone
$elem.children() pobiera bezpośrednie dzieci elementu

Na szczególną uwagę zasługują metody parents() i closest(). Metoda parents() zwraca pierwszego napotkanego rodzica, który pasuje do podanego selektora. Sprawdzanie takie rozpoczyna się od rodzica danego elementu (czyli nigdy nie zostanie zwrócony element, na którym została wywołana ta metoda).
Metoda closest() działa całkiem podobnie, ale rozpoczyna swoją podróż z uwzględnieniem danego elementu:


<div class="div module">
    <div>
        <div>
            <div class="div btn"></div>
        </div>
    </div>
</div>

$('.btn').parents('.div'); //zwróci zewnętrzny .div.module
$('.btn').closest('.div'); //zwróci wewnętrzny .div.btn

Zdarzenia w jquery

Aby przypiąć nasłuch zdarzenia do pobranego lub utworzonego elementu korzystamy z metody będącej nazwą zdarzenia, lub skorzystać z metody on().


$('input#guzik').click(function() {
    console.log('test');
})

//lub

$('input#guzik').on('click', function() {
    console.log('test');
})

Zwróć uwagę jak w drugim listingu z tej strony zmieniamy css dla elementu. Możemy to zrobić na dwa sposoby:


$element.css('background-color', 'red');
$element.css('color', 'blue');
$element.css('padding', 10);

//lub podając obiekt

$element.css({
    color : 'red',
    'background-color' : 'blue',
    padding : 10
});

Podobna zadada tyczy się też eventów. Możemy je deklarować pojedynczo, ale i zbiorczo podając obiekt z odpowiednimi metodami:


$element.on('click', function() {...})
$element.on('mouseover', function() {...})

//lub

$element.on({
    'click', function() {
        ...
    },
    'mouseover' : function() {
        ...
    }
});

Aby odwołać się do obiektu wywołującego, wskazujemy go instrukcją this. To już wiemy z rozdziału o eventach.

Domyślnie this wskazuje na element, który wywołał dane zdarzenie. Ten element jest w czystym javascript. Aby na takim obiekcie wykonywać operacje za pomocą jQuery, wystarczy nasze this podstawić pod jquery, stosując konstrukcję $(this). Dzięki temu uzyskujemy obiekt jquery wskazujący na wywołujący element.


<a class="link" href="http://jakis_adres.com">Pokaż href</a>
<a class="link" href="http://jakis_inny_adres.com">Pokaż href</a>

$('a.test1').on('click', function(e) {
    e.preventDefault();

    this.css({'color': 'black'}); //błąd! - this.css is not a function

    $(this).css({'color': 'black'}); //wszystko ok

    console.log( $(this).attr('href') ); //pobieramy atrybut href
})

Pokaż href Pokaż href

Aby odpiąć dane zdarzenie, skorzystamy z metody off(). Działa ona praktycznie tak samo jak removeEventListener, czyli jeżeli chcemy odpiąć jakąś funkcję, powinniśmy przekazać ją jako parametr:


function showSomething() {
    console.log('Kliknięto');
}

$('.link').on('click', showSomething);
$('.link').off('click', showSomething);

W jQuery w przeciwieństwie do JS możemy też odpinać funkcje anonimowe. Wystarczy przy ich podpinaniu podać dla zdarzenia nazwę:


$('.link').on('click', function(e) {
    e.preventDefault();
    console.log('Kliknięto 1');
});

$('.link').on('click.eventToDelete', function(e) {
    e.preventDefault();
    console.log('Kliknięto 2');
});

//usuwam event o nazwie eventToDelete
$('.link').off('click.eventToDelete');

Pokaż href Pokaż href

Pokazywanie i ukrywanie elementów

jQuery udostępnia nam kilka metod służących do pokazywania i ukrywania elementów na stronie.

Pierwszymi z nich są show() która pokazuje i hide() która ukrywa dany element.


$('#someDiv').show();
$('#someDiv').hide();

Nie myl powyższego z:


$('#someDiv').css('display', 'block');
$('#someDiv').css('display', 'none');

Pamiętaj, że element może mieć różne stylowanie i wcale nie jest powiedziane, że będzie miał wyświetlanie typu block.

Płynne pokazywanie i ukrywanie elementów

Aby płynnie pokazywać lub ukrywać elementy, skorzystamy z metod:

Nazwa metody Co robi
$element.fadeIn(szybkosc*, funkcjaZwrotna*) płynnie pokazuje element
$element.fadeIn(szybkosc*, funkcjaZwrotna*) płynnie ukrywa element
$element.fadeToggle(szybkosc*, funkcjaZwrotna*) płynnie pokazuje lub ukrywa element

Opcjonalny parametr szybkość określa szybkość zanikania. Możemy go podać słownie za pomocą słów "slow", "medium", "fast", ale najczęściej podaje się go w formie milisekund.
Parametr funkcjaZwrotna to funkcja, która zostanie wykonana po zakończeniu zanikania:


$(".test1").fadeOut();
$(".test2").fadeIn(300);
$(".test2").fadeIn(function() {
    console.log("Zakończono animację");
});

Przykład użycia:


<div class="module">
    <div class="module-content" style="display:none">
        Lorem ipsum dolor sit amet...
    </div>
    <button class="module-toggle button">Pokaż/schowaj content</button>
</div>

$('.module-toggle').on('click', function() {
    const $btn = $(this);

    $(this).prev().fadeToggle();
})

Zauważ, że pobrany element podstawiłem pod zmienną. Dzięki temu gdy kilka razy owwołujemy się do danego obiektu, każdrorazowo JS nie musi przeszukiwać drzewa DOM.

Znak $ przy nazwie zmiennej $btn to tylko konwencja nazewnicza - ułatwienie dla nas.
Dzięki temu wiemy, że dana zmienna jest obiektem porabranym lub stworzonym za pomocą jQuery, i możemy na nim wywoływać wszystkie metody tej biblioteki (find, next, eq, css itp).

Płynne zwijanie i rozwijanie elementów

Bardzo podobnymi w użyciu są metody służące do zwijania/rozwijania elementów:

Nazwa metody Co robi
$element.slideDown(szybkosc*, funkcjaZwrotna*) płynnie rozwija element
$element.slideUp(szybkosc*, funkcjaZwrotna*) płynnie zwija element
$element.slideToggle(szybkosc*, funkcjaZwrotna*) płynnie zwija lub rozwija element

$(".test1").slideUp();
$(".test2").slideDown(300);
$(".test3").slideToggle(function() {
    console.log('Zakończono animację');
});

Przykład użycia:


<div class="module">
    <div class="module-content" style="display:none">
        Lorem ipsum dolor sit amet...
    </div>
    <button class="module-toggle button">Pokaż treść</button>
</div>

$('.module-toggle').on('click', function() {
    const $btn = $(this);

    $(this).prev().slideToggle(function() {
        //this tutaj wskazuje na element zwijany

        //sprawdzam czy tekst po zwinięciu/rozwinięciu jest widoczny
        if ($(this).is(':visible')) {
            $btn.text('Ukryj treść');
        } else {
            $btn.text('Pokaż treść');
        }
    });
})

Podstawowe właściwości elementów

Po pobraniu elementu (lub stworzeniu) dostajemy dostęp do jego licznych właściwości.

Tworzenie nowych elementów

Aby utworzyć nowy element za pomocą jQuery wystarczy skorzystać z podobnej konstrukcji co w przypadku pobierania elementów ze strony:


const $div = $('<div class="module"></div>');

Tak jak w przypadku innych metod, tutaj też mamy kilka sposobów działania z nowymi elementami:


const $div = $('<div id="headCnt" class="page-header-cnt></div>')

//lub

const $div = $("<div>", {id: "headCnt", class: "page-header-cnt"});

Dołączanie elementów do strony

Aby dołączyć element do strony możemy skorzystać z jednej z kilku metod:

el.append(insertEl) Dołącza insertEl na końcu parenta
el.prepend(insertEl) Dołącza insertEl na początku parenta
el.before(insertEl) Dołącza insertEl przed danym elementem
el.after(insertEl) Dołącza insertEl za danym elementem

const $div = $('.test-append-cnt');

const $span1 = $('<span>Na końcu</span>');
$div.append($span1);

const $span2 = $('<span>Na początku</span>');
$div.prepend($span2);

const $span4 = $('<span>Za elementem</span>');
$div.after($span4);

const $span3 = $('<span>Przed elementem</span>');
$div.before($span3);

Usuwanie elementów ze strony

Aby usunąć element, skorzystamy z metody remove():


const $btn = $('.button');
$btn.remove();

Animacje w jQuery

W bardzo wielu przypadkach będziemy chcieli dane elementy strony animować. Możemy to zrobić za pomocą metody animate(properties, time, ease, fn). Najbardziej interesującymi nas parametrami są properties, do których przekazujemy obiekt składający się z właściwości i ich nowych wielkości:


<button type="button" class="button" id="testAnim">Animuj</button>
<div id="testAnimBlock" class="test-block">DIV</div>

$("#testAnim1").on('click', function(){
    $(this).animate({
        width: "500px",
        opacity: 0.4,
        fontSize: "3em",
        borderWidth: "10px"
    }, 1500);
});
Kliknij mnie

Parametr ease określa przebieg animacji.
jQuery posiada wbudowane dwa rodzaje przebiegu animacji: linear i swing. Aby skorzystać z innych rodzajów, powinniśmy skorzystać z dodatkowych pluginów do jQuery, np. tego: http://gsgd.co.uk/sandbox/jquery/easing/.


$("#testAnim2").on('click', function(){
    $(this).animate({
            height:200,
            width:400,
            opacity: 0.5
        },
        1000, //czas animacji
        'linear', //typ animacji
        function() { //funkcja zwrotna
            alert("koniec animacji");
        }
    );
});
Kliknij tutaj

W powyższych przykładach animowane właściwości ustawiłem na sztywno.
W jQuery można także zastosować konstrukcję -= lub += która odejmie lub doda odpowiednią wielkość od aktualnej:


$("#testAnim3").on('click', function(){
    $(this).animate({
        width: "+=" + 50,
        height: "+=" + 10,
        opacity: "-=" + 0.1,
        duration : 2000 //inny sposób deklaracji czasu trwania animacji
    });
});
Poklikaj tutaj szybko kilka razy

Poprawa animacji

Przyjrzyjmy się teraz pewnej sytuacji.
Mamy blok, po najechaniu na który chcemy płynnie zmienić jego rozmiary w zależności od tego czy kursor na nim jest czy nie. Po zjechaniu kursorem animacja powinna wrócić do początkowego stanu:


$('#testAnim4').on({
    'mouseover' : function() {
        $(this).animate({
            width: 300
        }, 800);
    },
    'mouseout' : function() {
        $(this).animate({
            width: 200
        }, 800);
    }
});
Pomęcz mnie

Problem pojawi się wtedy, gdy użytkownik zacznie się pastwić nad naszym elementem szybko najeżdżając i wyjeżdżając z niego kursorem. Kilka takich ruchów i animacja się zapętla, mimo, że już dawno jesteśmy kursorem w innym miejscu. Spowodowane jest to tym, że nasza animacja nie zdąży się do końca wykonać, a my chcemy odpalić ją ponownie.

Aby temu zapobiec musimy posiłkować się jedną z dwóch technik.

Pierwsza z nich polega na wykorzystaniu metody stop(), która zatrzyma odpaloną animację:


$("#testAnim5").on({
    'mouseover' : function() {
        $(this).stop().animate({width:300}, 500);
    },
    'mouseout' : function() {
        $(this).stop().animate({width:200}, 500);
    }
});
Pomęcz mnie

Druga metoda polega na sprawdzaniu czy dany element nie zakończył swojej animacji:


$("#testAnim6").on('click', function(){
    if (!$(this).is(':animated')) {
        $(this).animate({
            width: "+=" + 50,
            height: "+=" + 10,
            opacity: "-=" + 0.1,
            duration : 3000 //inny sposób deklaracji czasu trwania animacji
        });
    }
});
Poklikaj tutaj szybko kilka razy

Zauważ, że w przeciwieństwie do jednego z poprzednich przykładów teraz nawet jak będziesz szybko klikał, to po przerwaniu tego szaleństwa (...) div nie będzie kontynuował swojego rozrostu.

Powyższe dwa sposoby sprawdzają się w innych zastosowaniach. Metoda stop() lepiej sprawdza się w zdarzeniach mouseover i mouseout, natomiast sprawdzanie czy obiekt jest animowany lepiej stosować przy zdarzeniach click

Data

W lekcji o właściwościach elementów poznałeś właściwość dataset. Właściwość ta opiera się o customowe atrybuty data-...:


<button type="button" id="myButton" data-tooltip="Jakiś tekst" data-arrow-position="top">
    Kliknij mnie
</button>

const elem = document.querySelector('#myButton');

console.log(elem.dataset) //{tooltip: "Jakiś tekst", arrowPosition: "top"}
console.log(elem.dataset.tooltip); //Jakis tekst
console.log(elem.dataset.arrowPosition); //top

jQuery udostępnia nam metodę data(), która działa dość podobnie - ale nie do końca.

Otóż jQuery tworzy za naszymi plecami dla każdego elementu obiekt z danymi, który możemy wykorzystać do przechowywania dodatkowych danych związanych z danym elementem.

Obiekt ten początkowo tworzony jest w oparciu o atrybuty data-...:


$('#myButton').on('click', function() {
    console.log( $(this).data() ); //{arrowPosition: "top", tooltip: "Jakiś tekst"}
})

Różnica w stosunku do wersji JS jest taka, że po dodaniu do tego obiektu nowej właściwości, nie pojawia się ona jako atrybut elementu (w wersji JS się pojawia), a tylko zapisywana jest w obiekcie data. Aby to sprawdzić, kliknij i sprawdź poniższe przyciski w debugerze. W wersji JS zobaczysz, że właściwość dodana do dataset trafiła do atrybutów elementu.

Aby dodać nowe dane do metody data(), wystarczy podać ich nazwy i wartości w nawiasach. Jeżeli chcemy odczytać dane, wtedy podajemy tylko ich nazwy:


$btn.data() //odczytuje cały obiekt
$btn.data('position'); //odczytuje daną "position"
$btn.data('cat', 'Wielki kocur'); //ustawiam zmienną cat na "Wielki kocur"
$btn.data('bar', { myType: "test", count: 40 } )); //pod zmienną bar przechowuję dodatkowy obiekt
$btn.data('bar'); //odczytuję zmienną bar

Pytanie, które może się nasunąć to czemu jQuery poszło inną drogą niż JS? Ano z prostego powodu. Atrybuty data-... (a więc i cała właściwość dataset) dobrze się sprawdzają przy przetrzymywaniu prostych tekstów. Ale już trzymanie tam innych typów danych wcale fajne nie jest.