Framework-uri JavaScript: Introducere in jQuery + Manipularea DOM cu jQuery


Daca pentru MooTools am inceput cu prezentarea modului in care sunt tratate/extiunse obiectele si am vazut cum ne ajuta in programarea functionala, atunci cand vorbim de jQuery, aceasta discutie este foarte scurta. Pe scurt: jQuery nu extinde obiectele native JavaScript – niciunul dintre acestea. In schimb, jQuery ofera propriile sale obiecte pe care sa lucram. Asta inseamna ca nu avem la dispozitie nici metode avansate de programare functionala, si trebuie sa ne descurcam cu Function.call si Function.apply.

In schimb, domeniul in care jQuery isi face treaba excelent, este manipularea DOM. Cel mai important element in programarea cu jQuery il reprezinta setul incapsulat de elemente DOM (eng. wrapped set).

In jQuery exista un singur obiect – care se numeste chiar jQuery – si care constituie namespace-ul pentru toate functiile puse la dispozitie de framework si modalitatea prin care se obtine acel set incapsulat de elemente.

Pentru a usura munca programatorului, exista un alias pentru obiectul jQuery, si anume $. Acesta nu trebuie insa confundat cu identificatorul $ din celelalte framework-uri. De exemplu, in Prototype si MooTools, $ este doar o functie. Cum spuneam prima data cand am vorbit de framework-uri JavaScript, jQuery intra in categoria acelor framework-uri care se inteleg bine cu altele. De aceea, daca vrem sa folosim intr-un proiect atat MooTools cat si jQuery, pentru a nu aparea conficte intre cele doua (de fapt, singurul conflict este $ – care este functie in MooTools, dar si alias-ul pentru jQuery), nu trebuie decat sa apelam metoda noConflict a lui jQuery:

jQuery.noConflict()

Totusi, pentru ca folosirea $-ului ca alias pentru jQuery a devenit atat de populara printre cei care folosesc framework-ul, exista un truc care permite folosirea sa chiar impreuna cu un alt framework care foloseste $:

jQuery.noConflict();
(function($) {
    // in aceasta functie $ este alias pentru jQuery
})(jQuery);
// in afara functiei, $ apartine celuilalt framework

Ceea ce am facut aici este foarte simplu: am creat o functie anonima care primeste un singur parametru, cu identificatorul $, apoi am apelat functia respectiva transmitandu-i ca parametru obiectul jQuery. Astfel, in interiorul functiei, $ == jQuery.

Vom folosi de acum inainte, de fiecare data cand vorbim de jQuery, $ ca alias pentru obiectul jQuery, avand in vedere ca putem folosi codul scris intr-o functie de genul celei de mai sus avand garantia ca $ nu va crea conflicte cu alte framework-uri. De asemenea, cand ne vom referi la metode care se aplica unui set, vom folosi $() pe post de set.

Cat despre extinderea de obiecte de catre noi, putem extinde obiectul jQuery in doua moduri:

  • Folosind $.fn.extend (jQuery.fn.extend), se pot adauga metode care se aplica setului de elemente. Metoda primeste ca parametru un hash in care cheile sunt nume metodelor adaugate, iar valorile sunt functii pentru care contextul este setul curent de elemente (asta inseamna ca in interiorul functiei, this se refera la setul de elemente asupra carora se aplica metoda). O conventie destul de stricta atunci cand se scrie cod jQuery este ca acest gen de metode trebuie sa aiba ca valoare intoarsa, setul asupra caruia a lucrat. Aceasta permite inlantuirea de metode, astfel ca vom ajunge sa facem chestii destul de complicate cu foarte putin cod. Util de stiut in acest sens este ca in cadrul functiei, this.each() intoarce automat setul, deci daca vrem sa iteram pe elementele din set pentru a le modifica (pe toate sau doar un subset), este suficient ca in corpul functiei sa avem un cod de genul:
$.fn.extend({
    metoda1: function() {
        return this.each(function(pozitie) {
            // contextul acestei functii este elementul curent al iteratiei
        });
    },
    metoda2: function() {
        return this.each(function(pozitie) {
        });
    }
});

$().each primeste ca parametru o functie care primeste un singur parametru, pozitia elementului in set, iar contextul functiei va fi elementul curent al iteratiei. Se pot emula efectele unor comenzi de genul continue sau break returnand din aceasta functie true, respectiv false. Daca aveti curiozitatea sa va uitati in codul sursa al framework-ului, veti observa ca jQuery.fn este doar un alias pentru jQuery.prototype. De aceea, daca doriti sa adaugati o singura metoda pentru setul jQuery, puteti folosi direct $.fn.metoda = function() { ... }

  • folosind $.extend, se adauga metode si proprietati obiectului jQuery. Functioneaza asemanator cu $.fn.extend.

Sa trecem acumla partea mai interesanta, aceea de manipulare DOM. Pentru a prelua elemente din DOM, apelam $ cu un parametru de tip string, care este un selector CSS3, plus cativa pseudo-selectori custom ai jQuery (daca va intereseaza cum puteti adauga pseudo-selectori custom in MooTools, acest articol arata cat de usor se poate face asta), si, optional, inca un parametru – care poate fi un element din DOM sau un obiect jQuery – acesta servind drept context – radacina DOM fata de care se fac potriviri pe selectorul transmis ca prim parametru. Lista completa de selectori este disponibila pe pagina de documentatie oficiala. Functia intoarce un “vector” (de fapt, o instanta de jQuery, pe care o vom numi de obicei set jQuery) de elemente DOM – atentie! elementele DOM din set sunt doar atat – elemente DOM, fara nicio metoda magica adaugata de jQuery; toate metodele pe care le ofera framework-ul sunt aplicabile unui set jQuery, nu unui element din acel set – care se potrivesc cu selectorul trimis ca argument. Deoarece este vorba de o instanta de jQuery, i se pot aplica toate metodele lui jQuery, inlantuit.

Nota. Daca v-ati obisnuit cu sintaxa MooTools de a obtine elemente din DOM, $ din jQuery este mai degraba echivalentul functiei $$ din MooTools, iar pentru a folosi $ din jQuery ca si $ din MooTools, este suficient sa specificati id-ul elementului ca selector:

$('id_element'); // MooTools

este echivalent cu

$('#id_element'); // jQuery

In plus, selectorii din jQuery sunt atat de puternici incat permit specificarea mai multor selectori diferiti in acelasi string (separati intre ei cu virgula):

$('p,a'); // Toate elementele de tip paragraf sau ancora din documentul curent

Cat despre traversarea DOM, avem o multime de metode pentru a face asta. Sa vedem:

  • $().eq intoarce un set de elemente cu un singur element: elementul de pe pozitia transmisa ca parametru. Poate va intrebati de ce, daca tot am spus mai sus ca $() intoarce un vector de elemente, care e diferenta intre $()[pozitie] si $().eq(pozitie). Raspunsul este: pentru setul acesta cu un singur element, sunt disponibile toate metodele pe care le ofera jQuery. Insa daca folosim $()[pozitie], acesta va fi doar un element DOM nativ al JS, cu nicio metoda jQuery disponibila pentru el. Atentie! Daca pozitia transmisa ca parametru nu este valida (este mai mica decat 0, mai mare sau egala cu numarul de elemente din set, sau nici macar nu este numar, este intors un set fara elemente).
  • $().filter primeste ca parametru un selector – si atunci intoarce din setul curent numai acele elemente care satisfac selectorul respectiv – sau o functie care se va executa pentru fiecare element din set in parte, ar trebui sa astepte ca singur parametru pozitia elementului curent in set, contextul fiind elementul curent (deci ne putem referi la elementul curent prin this) – si atunci sunt pastrate in set numai elementele pentru care functia a intors true.
  • $().is intoarce true daca cel putin un element din setul curent satisface selectorul transmis ca parametru, false altfel. Ar trebui folositi numai selectori simpli, altfel va intoarce intotdeauna true. Ce inseamna selectori simpli? In general, sa nu contina selectori care sa testeze descendenta sau vecinatatea de elemente (de ex., 'p a' testeaza descendenta: ancore care sunt descendenti de paragrafe; 'p + .special' – elementele care au clasa special care au inaintea lor un paragraf). Pe post de sau in selector este folosita virgula – $('p').is(':first-child,.special') s-ar traduce prin: “subsetul tuturor paragrafelor din document contine elemente care sunt primul descendent al parintelui lor, sau au clasa special?”. Un exemplu este disponibil aici (sursa, cu syntax-highlight, aici);
  • $().map nu are neaparat legatura cu traversarea DOM, ci mai degraba atunci cand lucram cu atribute, insa sa vedem ce face: primeste ca parametru o functie, care primeste ca parametri indexul elementului in set si elementul insusi (insa nu este necesar sa lucram cu acest al doilea parametru, pentru ca functia se executa in contextul elementului curent), si intoarce un “vector jQuery” (in sensul ca nu e instanta de Array, ci o instanta de jQuery, deci sunt valabile toate metodele lui jQuery pe acel vector, insa poate fi iterat ca orice vector) care poate fi folosit in cod.
  • $().not este opusul lui filter, in sensul ca intoarce subsetul elementelor care NU satisfac selectorul transmis ca parametru. Parametrul poate fi, pe langa selector, un element DOM (si atunci daca elementul era deja in set, va fi eliminat) sau un vector de elemente DOM, care vor fi eliminate (daca exista) din set.
  • $().slice se comporta exact ca Array.slice (intoarce elementele dintre doi indecsi ai setului) – insa lucreaza nu cu vectori, ci cu seturi jQuery
  • $().add primeste ca parametru un selector, un element DOM sau un vector de elemente DOM si intoarce un set care contine elementele din vechiul set, plus elementele din document care se potrivesc cu selectorul / elementul / vectorul de elemente DOM transmise ca parametru
  • $().children intoarce un subset cu descendentii directi ai elementelor din set. Optional poate primi un selector dupa care vor fi filtrati descendentii
  • $().contents intoarce toti descendentii elementelor din set (inclusiv nodurile de tip text – insa IE are un bug – printre acesti descendenti nu vor aparea nodurile text care contin doar spatii albe)
  • $().find primeste ca parametru un selector si intoarce descendentii fiecarui element din set care se potrivesc cu selectorul
  • $().next/$().previous intoarce un subset care contine, pentru fiecare element din set, fratele din dreapta/stanga. Optional, se poate trimite un selector dupa care se filtreaza acestia
  • $().nextAll/$().previousAll intoarce un subset cu toti fratii aflati la dreapta/stanga fiecarui element din set. Optional, se poate transmite ca parametru un selector dupa care vor fi filtrati
  • $().offsetParent intoarce un set cu primul stramos care are pozitionarea (css) relative sau absolute, al primului element care se potriveste (deci daca exista astfel de parinti pentru mai multe elemente din set, este intors doar primul)
  • $().parent intoarce un set cu parintii (directi) ai elementelor din set (si care nu contine duplicate – in cazul in care aveam in set doua elemente cu acelasi parinte, el va aparea o singura data in subset). Optional, parintii sunt filtrati dupa un selector transmis ca parametru
  • $().parents functioneaza la fel ca $().parent, insa intoarce stramosii elementelor din set
  • $().siblings intoarce un set cu toti fratii elementelor din set (unici), eventual filtrati dupa un selector

Si pentru ca am tot vorbit de inlantuire, exista o metoda $().end() care restaureaza setul la cel anterior (de fiecare data cand se face o filtrare/adaugare de elemente la un set, dupa ce am terminat cu lucrul pe acel subset, pur si simplu apelam metoda end() si revenim la setul initial). Aveti un exemplu aici.

Nota. Poate ca ati vazut ca exemplele de jQuery le-am pus pe excelentul site jsbin.com, si va intrebati de ce nu am facut la fel cu codul MooTools din articolele trecute. Raspunsul este ca jsbin foloseste versiunea 1.11 a MooTools, insa cea mai recenta versiune (si cea despre care am vorbit pana acum) este 1.2, care nu este compatibila cu cea anterioara. Altfel, recomand cu placere jsbin.

Inainte de a vorbi de modificarea elementelor DOM cu jQuery este necesar sa reamintim si sa retineti ca jQuery nu modifica prototipul pentru niciun obiect nativ al JavaScript. Asta inseamna ca daca vreti sa folositi la un moment dat un element din set, nu intreg setul, si sa aveti la dispozitie toate metodele de care vom vorbi in continuare, trebuie creat cu ajutorul functiei $ un set care sa contina doar acel element. De exemplu:

var hello = document.getElementById('hello');
alert(hello instanceof HTMLElement); // true
alert(hello instanceof jQuery); // false
var jQuery_hello = $(hello);
alert(jQuery_hello instanceof HTMLElement); // false
alert(jQuery_hello instanceof jQuery); // true
alert(hello == jQuery_hello); // false
alert(hello == jQuery_hello.eq(0)); //false
alert(hello == jQuery_hello[0]); // true

Asadar, modificarea elementelor DOM (in general, a atributelor si a CSS-ului) se face prin urmatoarele metode:

  • $().attr(atribut) primeste atribut ca parametru de tip string si intoarce valoarea respectivului atribut
  • $().attr(hash) – daca, in schimb, transmitem un singur parametru de tip Object, acesta trebuie sa aiba ca si chei nume de atribute. Efectul va fi ca atributele cu acelasi nume ca al cheilor din hash vor primi valorile cheilor respective.
  • $().attr(atribut, valoare) seteaza valoarea atributului atribut la valoarea valoare
  • $().attr(atribut, functie) seteaza valoarea atributului atribut la valoarea returnata de functie, pentru fiecare element din set. Functia primeste ca parametri indexul elementului curent in set si elementul insusi, iar contextul functiei este elementul curent (ceea ce face superfluu lucrul cu cel de-al doilea parametru)
  • $().addClass/removeClass/toggleClass adauga/sterge/(adauga sau sterge) clasa primita ca parametru dintre clasele elementului curent. in cazul toggleClass, clasa este adaugata daca nu exista deja printre clasele elementului, sau este stearsa daca exista. removeClass poate fi apelat si fara parametru, si atunci sunt sterse toate clasele elementului.
  • $().hasClass primeste ca parametru un nume de clasa si intoarce true sau false dupa cum elementul curent are/nu are clasa respectiva
  • $().html() (fara parametri) intoarce un string cu continutul innerHTML al primului element din set
  • $().html(valoare) seteaza innerHTML-ul fiecarui element din set la valoarea transmisa ca parametru
  • $().text() (fara parametru) intoarce un string reprezentand concatenarea tuturor nodurilor text, descendenti directi ai primului element din set
  • $().text(valoare) seteaza valoarea textului din fiecare element din set la valoarea trimisa. Asta inseamna ca fiecare caracter care ar avea semnificatie in HTML (<,>,”) este codat (&lt;, &gt;, &quot;), deci setarea textului nu va duce la inserarea de noi elemente ca si copii ai elementului curent (spre deosebire de $().html(valoare)). Un exemplu din care reiese aceasta diferenta este disponibil aici.
  • $().val() intoarce valoarea / vectorul de valori (daca este vorba de un multi-select) primului element de tip input (includem aici input,select,textarea si orice elemente cu care se pot culege informatii dintr-un formular) din set
  • $().val(valoare) seteaza atributul value la valoarea valoare pentru fiecare element din set (pentru elementele input, textarea), sau bifeaza toate checkbox-urile/butoanele radio sau selecteaza toate elementele option din select-uri care au atributul value egal cu parametrul transmis. Pentru cazul din urma, daca valoarea trimisa nu este string ci vector de stringuri, sunt bifate/selectate toate optiunile care au atributul value printre valorile din vector

Cat despre inserarea de noi elemente in DOM, iata ce ne pune la dispozitie jQuery (daca nu e specificat altfel, parametrii de tip string care vor fi inserati pot contine cod HTML care va rezulta in generarea de noi elemente):

  • $().html, $().text de care am vorbit mai sus folosesc nu numai la modificarea elementelor, ci si la inserare de elemente (vezi exemplul de mai sus, folosirea lui $().html).
  • $().append primeste un singur parametru de tip string, si insereaza continutul string-ului la sfarsitul fiecarui element din set
  • $().appendTo primeste ca parametru un selector si are ca efect adaugarea tuturor elementelor din setul curent la elementele din setul obtinut din selectorul transmis ca parametru. Daca selectorul transmis face match pe un singur element (selectorul nu trebuie sa fie id, important este ca rezultatul selectiei de elemente sa aiba un singur element), elementele din set sunt scoase din DOM si adaugate elementului care a facut match. Daca, insa, selectorul face match pe mai multe elemente, elementele din set raman in DOM, si in fiecare element care a facut match pe selector sunt adaugate clone ale elementelor din set. Exemplul aici.
  • $().prepend, $().prependTo se comporta la fel, doar ca setul va fi adaugat la inceput, nu la sfarsit.
  • $().before, $().after se comporta la fel ca $().append, doar ca adaugarea se va face nu in interiorul elementului, ci imediat inainte/dupa
  • $().insertBefore, $().insertAfter se comporta la fel ca $().appendTo, doar ca setul va fi adaugat inainte/dupa elementele care au facut match, nu in interior
  • $().wrap primeste ca parametru un string sau un element DOM. Daca este vorba de un string, este creata structura DOM aferenta si jQuery gaseste cel mai adanc descendent. Fiecare element din set este inserat intr-o clona a acestei structuri DOM, in acel element (sau in elementul DOM transmis ca parametru), apoi fiecare structura nou rezultata este inserata in DOM in locul vechiului element.
  • $().wrapAll incapsuleaza toate elementele din set intr-un element DOM nou creat sau in html-ul transmis ca parametru (asemanator cu $().wrap), elementele din set sunt scoase din DOM, iar fragmentul DOM nou creat inlocuieste primul element din set in ierarhia DOM a documentului
  • $().wrapInner incapsuleaza intr-un mod asemanator cu $().wrapAll, copii fiecarui element din set.
  • $().replaceWith inlocuieste fiecare element din set cu html-ul transmis ca parametru, sau cu clone ale unui element DOM transmis ca parametru, sau cu copii ale elementelor din vectorul jQuery transmis ca parametru (cu alte cuvinte, parametrul poate fi string, si atunci vorbim de html care inlocuieste elementul, un element DOM, sau un vector jQuery – un set obtinut de exemplu printr-un apel al $()).
  • $().replaceAll este la fel ca $().replaceWith, cu rolul parametrilor schimbat – ca parametru al $ avem un fragment de HTML, sau un element DOM, sau un set jQuery, si acestea vor inlocui elementele care se potrivesc cu selectorul transmis ca parametru al lui replaceAll
  • $().clone() intoarce un set cu clone ale elementelor din setul jQuery. Daca se apeleaza cu parametrul true: $().clone(true), elementele clonate vor avea copiati si ascultatorii de evenimente

Cat despre eliminarea de elemente DOM, avem la dispozitie:

  • $().empty() goleste elementele din set de toti descendentii lor
  • $().remove scoate din DOM toate elementele din set; eventual se poate transmite ca parametru un selector care va filtra elementele care vor fi scoase. Aceasta metoda intoarce un set jQuery cu elementele scoase din DOM.

Cam acestea – si nu putine -  sunt metodele pe care le pune la dispozitie jQuery pentru a manipula DOM-ul. Sper ca din aceasta intelegeti de ce este atat de popular acest framework.

Data viitoare, vorbim despre evenimente in jQuery.

, , , ,

blog comments powered by Disqus