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 JavaScript 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

Manipulacja stylami i klasami

Po pobraniu elementu (lub stworzeniu) dostajemy dostęp do jego licznych właściwości i metod. Aby zmienić style elementu skorzystamy z metody css, którą można używać na 2 sposoby:


$element.css('background-color', 'red');
$element.css('color', 'blue');
$element.css('padding', 10); //px zostaną dodane automatycznie

//lub podając obiekt

$element.css({
    color : 'red',
    'background-color' : 'blue',
    padding : 10 //px zostaną dodane automatycznie
});

Dodatkowo mamy możliwość manipulowania klasami danego elementu:


$element.addClass("nazwa_klasy"); //dodaje klasę css elementowi
$element.removeClass("nazwa_klasy"); //usuwa klasę css elementowi
$element.hasClass("nazwa_klasy"); //sprawdza czy dany element ma klasę
$element.toggleClass("nazwa_klasy"); //włącza/wyłącza klasę

Jeżeli chcemy dodać lub usunąć więcej klas, podajemy je po spacji:


$element.addClass("big important");
$element.removeClass("big important");

Wybrane metody i właściwości

Poza manipulacją stylami, mamy dostęp do wielu metod i właściwości dla takich elementów. Poniżej lista tych najczęściej używanych:

$element.attr(nazwa) pobiera lub ustawia atrybut o danej nazwie. Jeżeli atrybutu nie ma, zwraca undefined
$element.removeAttr(nazwa) usuwa atrybut o danej nazwie

const $img = $("img");

const src = $img.attr("src"); //pobiera atrybut src z grafiki

if ( $img.attr("alt") ) { //jeżeli nie ma atrybutu alt
    $img.removeAttr("tooltip"); //usuwam atrybuty
    $img.attr("data-text", $img.attr("alt")); //dodaję pojedynczy atrybut

    $img.attr({ //możemy ustawiać kilka atrybutów na raz
        alt: "Lorem ipsum dolor",
        title: "Lorem ipsum dolor"
    });
}

const $link = $("a");

const href = $link.attr("href")); //pobiera atrybut href z linka
$link.removeAttr("tooltip"); //ustawiam atrybut tooltip

if (href.startsWith("http")) {
    $link.attr("target", "_blank"); //dodaję nowy atrybut
}
$element.prop(nazwa) pobiera lub ustawia daną właściwość. Jeżeli właściwości nie ma, zwraca undefined
$element.removeProp(nazwa) usuwa daną właściwość

const $input = $("input:radio"); //pobieramy input radio
if ($input.is(":checked")) {
    $input.prop("disabled", true); //ustawiam właściwość
}

W powyższym przykładzie działam na właściwościach elementu. Jeżeli nie pamiętasz czym się różnią atrybuty od właściwości elementu, ">służę pomocą.

$element.text() pobiera lub ustawia tekst elementu (równoznaczne z textContent)
$element.html() pobiera lub ustawia html elementu (równoznaczne z innerHTML)

const text = $element.text(); //pobieram text w elemencie
$element.html(text); //ustawiam nowy html na tekst bez znaczników html
$element.val() Ustawia lub pobiera wartość value

$input = $("input:text");
$input.val(); //pobieram value
$input.val("nowa wartość"); //ustawiam value

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');
})

Podobnie jak przy metodzie .css() i .attr() także przy zdarzeniach 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 - wskazuje na dany element w czystym JavaScript
    $(this) - wskazuje na dany element w jQuery

})

$('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 JavaScript 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.

Powyższe metody automatycznie określają jakie wyświetlanie ma dany element.

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 JavaScript 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ść');
        }
    });
})

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();

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 JavaScript jest taka, że po dodaniu do tego obiektu nowej właściwości, nie pojawia się ona jako atrybut elementu (w wersji JavaScript się pojawia), a tylko zapisywana jest w obiekcie data. Aby to sprawdzić, kliknij i sprawdź poniższe przyciski w debugerze. W wersji JavaScript 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ż JavaScript? 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.