Salut și salut înapoi la seria "JavaScript și DOM". Ultima dată când am acoperit câteva baze de date JavaScript și am atins diferite aspecte ale modelului Object Document, inclusiv cum să accesăm nodurile și să traversăm prin DOM. Astăzi vom analiza modul de manipulare a elementelor din DOM și vom discuta modelul evenimentului browserului.
În ultima lecție am abordat pașii implicați în accesarea unei colecții de noduri DOM sau a unui nod singular DOM. Magia reală apare atunci când manipulați anumite proprietăți rezultând în ceea ce este cunoscut sub numele de "comportament".
Fiecare nod DOM are o colecție de proprietăți; majoritatea acestor proprietăți oferă abstracții pentru anumite funcționalități. De exemplu, dacă aveți un element de paragraf cu un ID de "intro", puteți schimba cu ușurință culoarea acelui element prin DOM API:
document.getElementById ('intro'). style.color = '# FF0000';
Pentru a ilustra natura obiectului / proprietății acestui API, ar putea fi mai ușor de înțeles dacă îl despărțim prin atribuirea fiecărui obiect unei variabile:
var myDocument = document; var myIntro = myDocument.getElementById ('intro'); var myIntroStyles = myIntro.style; // Și acum, putem seta culoarea: myIntroStyles.color = '# FF0000';
Acum, dacă avem o referință la obiectul "stil" al paragrafului, putem adăuga alte stiluri CSS:
myIntroStyles.padding = '2px 3px 0 3px'; myIntroStyles.backgroundColor = '#FFF'; myIntroStyles.marginTop = '20px';
Folosim doar nume de proprietate CSS de bază aici. Singura diferență este că în cazul în care ați găsi în mod normal o linie ('-'), textul este camel. Deci, în loc de "margin-top" folosim "marginTop". Următoarele, de exemplu, nu ar fi lucra și ar produce o eroare de sintaxă:
myIntroStyles.padding-top = '10em'; // Produce o eroare de sintaxă: // - Caracterul '-' este operatorul minus în JavaScript. // - În plus, nu există un astfel de nume de proprietate.
Proprietățile pot fi accesate într-o manieră asemănătoare matricei. Deci, cu această cunoaștere am putea crea o mică funcție pentru a schimba orice stil al unui element dat:
funcția changeStyle (elem, proprietate, val) elem.style [property] = val; // Observați parantezele pătrate utilizate pentru a accesa proprietatea // Ați folosi pluginul de mai sus astfel: var myIntro = document.getElementById ('intro'); // Grab Intro paragraful changeStyle (myIntro, 'culoare', 'roșu');
Acesta este doar un exemplu - pentru a fi sincer, probabil că nu este o funcție foarte utilă, deoarece, din punct de vedere sintactic, este mai rapid să se utilizeze mijloacele convenționale arătate mai devreme (de ex.. elem.style.color = 'roșu').
Pe lângă proprietatea "stil" există o mulțime de alte elemente pe care le puteți folosi pentru a manipula anumite aspecte ale unui nod / element. De fapt, dacă ați instalat Firebug, ar trebui să încercați să "inspectați un element", apoi să faceți clic pe fila "DOM" (în mod normal, la dreapta sau sub panoul de vizualizare a elementelor) pentru a vedea toate proprietățile:
Toate proprietățile pot fi accesate utilizând notația convențională de puncte (de exemplu, Element.tabIndex). Nu toate proprietățile sunt tipuri de date primitive (șiruri de caractere, numere, Booleani etc.); proprietatea "stil", de exemplu, pe care am discutat-o mai devreme, este un obiect care conține propriile proprietăți. Multe dintre proprietățile unui element vor fi citite numai; ce vreau sa spun prin aceasta este ca nu le poti schimba valoarea. De exemplu, nu puteți modifica direct proprietatea "parentNode" a unui nod. Browserul va arunca de obicei o eroare dacă încercați să schimbați una dintre aceste proprietăți read-only: de ex. EROARE: "setarea unei proprietăți care are doar un getter". Este ceva ce trebuie să fie conștient de ...
O cerință comună este de a modifica conținutul într-un element. Există câteva modalități diferite de a face acest lucru. Cea mai ușoară cale este de a folosi proprietatea 'innerHTML', cum ar fi:
var myIntro = document.getElementById ('intro'); // Înlocuirea conținutului curent cu conținut nou: myIntro.innerHTML = 'Conținut nou pentru uimitor paragraf!'; // Adăugarea la conținutul curent: myIntro.innerHTML + = '... ceva mai mult conținut ...';
Singura problemă cu această metodă este că nu este specificată în niciun standard și nu se află în specificația DOM. Dacă nu sunteți deranjați de asta, atunci mergeți mai departe și folosiți-o. În mod normal, este mult mai rapid decât metodele convenționale DOM, pe care le vom acoperi în continuare.
Atunci când creați conținut prin DOM API, trebuie să fiți conștienți de două tipuri diferite de noduri, un nod de elemente și un nod de text. Există multe alte tipuri de noduri, dar aceste două sunt singurele importante pentru moment.
Pentru a crea un element folosiți metoda "createElement" și pentru a crea un nod de text utilizați metoda "createTextNode", ambele sunt afișate mai jos:
var myIntro = document.getElementById ('intro'); // Vrem să adăugăm conținut la paragraful: var someText = 'Acesta este textul pe care vreau să îl adaug "; var textNod = document.createTextNode (someText); myIntro.appendChild (textNode);
Aici folosim metoda "appendChild" pentru a adăuga noul nostru nod text la paragraf. A face acest lucru durează puțin mai mult decât metoda non-standard innerHTML, dar este important să cunoașteți ambele căi, astfel încât să puteți lua decizia corectă. Iată un exemplu mai avansat folosind metodele DOM:
var myIntro = document.getElementById ('intro'); // Vrem să adăugăm o nouă ancoră la paragraful: // Mai întâi, vom crea noul element de ancorare: var myNewLink = document.createElement ('a'); // myNewLink.href = 'http://google.com'; // myNewLink.appendChild (document.createTextNode ('Vizitați Google')); // Vizitați Google // Acum îl putem adăuga la paragraful: myIntro.appendChild (myNewLink);
Există, de asemenea, o metodă "insertBefore" DOM, care este destul de explicativă. Folosind aceste două metode ('insertBefore' & 'appendChild') putem crea propria noastră funcție 'insertAfter':
// 'Target' este elementul deja în DOM // 'Bullet' este elementul pe care doriți să îl inserați funcția insertAfter (target, bullet) target.nextSibling? target.parentNode.insertBefore (bullet, target.nextSibling): target.parentNode.appendChild (marcaj); // Folosim un operator ternar în funcția de mai sus: // Formatul său: CONDITION? EXPRIMARE DACĂ ESTE TRECUT: EXPRESIE ÎN CAZ DE FALSE;
Funcția de mai sus verifică existența următorului frate al țintă în DOM, dacă există, atunci va introduce nodul "bullet" înainte de următorul frate al țintă, altfel va presupune că țintă este ultimul copil al unui element și astfel este bine să atașați glonțul ca pe un copil al părintelui. API-ul DOM nu ne oferă nici o metodă "insertAfter" deoarece nu este nevoie - putem să o creăm singuri.
Este destul de mult mai mult să înveți despre manipularea elementelor din DOM, dar cele de mai sus ar trebui să fie o bază suficientă pe care să o poți construi.
Evenimentele de browser se află în centrul oricărei aplicații web și în majoritatea îmbunătățirilor JavaScript. Prin aceste evenimente, definim când se va întâmpla ceva. Dacă aveți un buton în documentul dvs. și aveți nevoie de o validare a formularului pentru a avea loc atunci când este făcut clic pe acesta, atunci veți folosi evenimentul "clic". Mai jos este o prezentare generală a majorității evenimentelor standard ale browserului:
Notă: După cum am discutat ultima oară, DOM și limbajul JavaScript sunt două entități separate. Evenimentele de browser fac parte din API-ul DOM, nu fac parte din JavaScript.
Există o mulțime de mai multe evenimente de a alege de la. Cele prezentate mai sus sunt cele principale pe care le veți întâlni frecvent în codul JavaScript. Rețineți că unele dintre ele au diferențe subtile de browser încrucișat. De asemenea, rețineți faptul că multe browsere implementează evenimente proprietare, de exemplu, există numeroase evenimente specifice Gecko, cum ar fi "DOMContentLoaded" sau "DOMMouseScroll" - puteți citi mai multe despre acestea aici: https://developer.mozilla.org / ro / Gecko-Specific_DOM_Events
Am acoperit evenimentele reale, dar nu am discutat încă despre procesul de atașare a unei funcții la un eveniment. Aici se petrece magia. Evenimentele enumerate mai sus vor apărea indiferent dacă ați scris sau nu JavaScript, deci pentru a-ți valorifica puterea, trebuie să înregistrați "manipulatori de evenimente" - acesta este un termen fantezist pentru a descrie o funcție care este utilizată pentru a gestiona un eveniment. Iată un exemplu simplu, utilizând de bază modelul de înregistrare a evenimentului (cunoscut și ca "înregistrarea tradițională a evenimentului"):
Înregistrarea evenimentului de bază:
// JavaScript: var myElement = document.getElementById ('my-button'); // Această funcție va fi manipulatorul evenimentului nostru: buton funcționalClick () alert ('Ați făcut clic pe butonul!'); // Aceasta este partea de înregistrare a evenimentului: myElement.onclick = buttonClick;
Avem un buton HTML cu un ID al butonului "my-button" și l-am accesat utilizând comanda "document.getElementById". Apoi, creăm o nouă funcție, care ulterior este atribuită proprietății DOM onclick a butonului. Cam despre asta e!
Modelul de "înregistrare a evenimentului de bază" este la fel de simplu ca și cel obținut. Prefixați evenimentul pe care îl urmăriți cu 'on' și accesați-l ca proprietate a oricărui element cu care lucrați. Aceasta este, în esență, versiunea neobișnuită de a face ceva de genul acesta (pe care nu îl recomand):
Manipularea inline a evenimentului (folosind atributele HTML) este foarte obositoare și face site-ul dvs. mult mai greu de întreținut. Este mai bine să utilizați JavaScript neobișnuit și să îl conțin în toate fișierele ".js" respective, care pot fi incluse în document ca / când este necesar. În timp ce suntem în legătură cu subiectul JavaScript neobișnuit, aș dori să corectez concepția greșită comună conform căreia bibliotecile precum jQuery fac posibilă "codarea în mod discret" - acest lucru nu este adevărat. Când utilizați jQuery, este la fel de ușor să faceți lucrurile în mod greșit. Motivul pentru care nu ar trebui să utilizați gestionarea inline a evenimentului este exact același motiv ca motivul pentru care nu ar trebui să aplicați stilurile CSS inline (folosind).
Înregistrarea avansată a evenimentului:
Nu lăsați acest nume să vă inducă în eroare, doar pentru că se numește "avansat" nu înseamnă că este mai bine să îl utilizați; de fapt, tehnica discutată mai sus ("înregistrarea evenimentului de bază") este perfect potrivită în majoritatea timpului. Folosind tehnica de bază are totuși o limitare cheie; nu puteți lega mai mult de o funcție la un eveniment. Acest lucru nu este atât de rău de fapt, deoarece puteți apela orice număr de alte funcții din interiorul acelei singure funcții, dar dacă aveți nevoie de mai mult control, atunci există o altă modalitate de a înregistra utilizatorii, introduceți "modelul de înregistrare a evenimentelor avansate".
Acest model vă permite să legați mai mulți manipulatori la un eveniment unic, ceea ce înseamnă că mai multe funcții se vor executa la apariția unui eveniment. În plus, acest model vă permite să eliminați cu ușurință oricare dintre agenții de procesare a evenimentelor legate.
Strict vorbind, există două modele diferite în această categorie; W3C și Microsoft. Modelul W3C este susținut de toate browserele moderne în afară de IE, iar modelul Microsoft este acceptat numai de IE. Iată cum ați folosi modelul W3C:
// FORMAT: target.addEventListener (tip, funcție, useCapture); // Exemplu: var myIntro = document.getElementById ('intro'); myIntro.addEventListener ("clic", introClick, false);
Și aici este același lucru, dar pentru IE (modelul Microsoft):
// FORMAT: target.attachEvent (tip "on" +, funcție); // Exemplu: var myIntro = document.getElementById ('intro'); myIntro.attachEvent ("onclick", introClick);
Iată funcția "introClick":
funcția introClick () alert ('Ați făcut clic pe paragraf!');
Din cauza faptului că niciun model nu funcționează în toate browserele, este o idee bună să le combinați pe ambele într-o funcție personalizată. Iată o funcție foarte adițională "addEvent", care funcționează cu browser încrucișat:
funcția addEvent (elem, type, fn) if (elem.attachEvent) elem.attachEvent (tipul "on" +, fn); întoarcere; dacă (elem.addEventListener) elem.addEventListener (type, fn, false);
Funcția verifică pentru proprietățile "attachEvent" și "addEventListener" și apoi utilizează unul dintre modelele care depind de acel test. Ambele modele fac posibilă și eliminarea procesatorilor de evenimente, așa cum se arată în această funcție "removeEvent":
funcția removeEvent (elem, type, fn) dacă (elem.detachEvent) elem.detachEvent ('on' + type, fn); întoarcere; dacă (elem.removeEventListener) elem.removeEventListener (type, fn, false);
Ați folosi funcțiile de mai jos:
var myIntro = document.getElementById ('intro'); addEvent (myIntro, 'click', functie () alerta ('ATI CLICK ME !!!'););
Observați că am trecut o funcție fără nume ca al treilea parametru. JavaScript ne permite să definim și să executăm funcții fără a le numi; funcțiile de acest tip se numesc "funcții anonime" și pot fi foarte utile, mai ales când trebuie să treci o funcție ca parametru la o altă funcție. Am putea pune funcția "introClick" (definită mai devreme) ca al treilea parametru, dar uneori este mai convenabil să o faceți cu o funcție anonimă.
Dacă doriți ca o acțiune să aibă loc la un eveniment, doar prima dată când faceți clic pe el, puteți face ceva de genul acesta:
// Rețineți că am definit deja funcțiile addEvent / removeEvent // (pentru a le folosi trebuie să fie incluse) var myIntro = document.getElementById ('intro'); addEvent (myIntro, 'click', oneClickOnly); funcția oneClickOnly () alertă ("WOW!"); removeEvent (myIntro, 'click', oneClickOnly);
Îndepărtăm mânerul de îndată ce evenimentul este declanșat pentru prima dată. Nu am reușit să folosim o funcție anonimă în exemplul de mai sus, deoarece trebuie să păstrăm o referință la funcție ('oneClickOnly') pentru a putea ulterior să o eliminăm. Acestea fiind spuse, este posibil să se realizeze cu o funcție anonimă nesemnată:
addEvent (myIntro, 'click', functie () alert ('WOW!'); removeEvent (myIntro, 'click', arguments.callee););
Suntem destul de încrezători prin referirea la proprietatea "calle" a obiectului "argumente". Obiectul "argumente" conține toți parametrii trecuți ai oricărei funcții și conține, de asemenea, o referință la funcția însăși ("callee"). Procedând astfel, eliminăm complet necesitatea de a defini o funcție numită (de exemplu, funcția "oneClickOnly" afișată mai devreme).
În afară de diferențele sintactice evidente dintre implementarea W3C și Microsoft, există și alte discrepanțe care merită notate. Atunci când legați o funcție de un eveniment, funcția ar trebui să fie rulată în contextul elementului și astfel cuvântul cheie "acest" în cadrul funcției ar trebui să facă referire la element; utilizând fie modelul de înregistrare a evenimentului de bază, fie modelul avansat al W3C, acesta funcționează fără vină, dar implementarea Microsoft nu reușește. Iată un exemplu despre ce faceți voi ar trebui să să poată face în cadrul funcțiilor de gestionare a evenimentelor:
funcția myEventHandler () this.style.display = 'none'; // Funcționează corect, 'this' se referă la elementul: myIntro.onclick = myEventHandler; // Funcționează corect, 'this' se referă la elementul: myIntro.addEventListener ('click', myEventHandler, false); // NU functioneaza corect, 'this' se refera la obiectul Window: myIntro.attachEvent ('onclick', myEventHandler);
Există câteva modalități diferite de evitare / remediere a acestei probleme. Cea mai ușoară opțiune este de a utiliza modelul de bază - nu există aproape nici o incoerență între browser-uri atunci când se utilizează acest model. Dacă, totuși, doriți să utilizați modelul avansat și aveți nevoie de cuvântul cheie "acest" pentru a face referire la elementul corect, atunci ar trebui să aruncați o privire asupra unora dintre funcțiile "addEvent" mai larg adoptate, în special John Resig sau Dean Edward's nu folosiți chiar modelul avansat, superb!).
Un aspect important al manipulării evenimentului pe care trebuie să-l discutăm este ceva numit "Obiect de eveniment". Ori de câte ori legați o funcție de un eveniment, adică ori de câte ori creați un handler de evenimente, funcția va fi trecută printr-un obiect. Acest lucru se întâmplă nativ, astfel încât să nu trebuiască să luați nicio măsură pentru ao induce. Acest obiect de eveniment conține o varietate de informații despre evenimentul care tocmai a avut loc; conține, de asemenea, metode executabile care au diferite efecte comportamentale asupra evenimentului. Dar, fără îndoială, Microsoft și-a ales propriul mod de a implementa această "facilitate"; Browserele IE nu transmit acest obiect de eveniment, ci trebuie să îl accesați ca proprietate a obiectului ferestrei globale; aceasta nu este într-adevăr o problemă, este doar o neplăcere:
funcția myEventHandler (e) // Observați argumentul "e" ... // Când această funcție este apelată, ca rezultat al evenimentului // ardere, obiectul evenimentului va fi transmis (în agenți compatibili cu W3C) // Să facem ' e "prietenos cu browserul încrucișat: e = e || window.event; // Acum putem să ne referim în siguranță la "e" în toate browserele moderne. // Ne-ar lega funcția la un eveniment aici ...
Pentru a verifica existența obiectului "e" ("Obiectul evenimentului") folosim un operator OR (logic) care dictează în esență următoarele: dacă "e" este o valoare "falsă" (null, undefined, 0 etc) apoi atribuie 'window.event' la 'e'; altfel folosiți doar "e". Aceasta este o modalitate rapidă și ușoară de a obține obiectul real al Evenimentului într-un mediu încrucișat în browser. Dacă nu sunteți confortabil cu utilizarea operatorilor logici în afara unei instrucțiuni IF, atunci această construcție ar putea fi mai potrivită pentru dvs.:
dacă (! e) e = window.event; // Nu este necesară o declarație ELSE, deoarece "e" va fi deja definită în alte browsere
Unele dintre cele mai utile comenzi și proprietăți ale acestui obiect de eveniment sunt, din nefericire, implementate inconsecvent în toate browserele (și anume IE vs. toate celelalte). De exemplu, anularea acțiunii prestabilite a unui eveniment poate fi realizată folosind metoda "preventDefault ()" a obiectului eveniment, dar în IE se poate obține numai cu proprietatea 'returnValue' a obiectului. Deci, din nou, trebuie să folosim ambele pentru a acomoda toate browserele:
funcția myEventHandler (e) e = e || window.event; // Prevenirea acțiunii implicite a unui eveniment: if (e.preventDefault) e.preventDefault (); altceva e.returnValue = false;
Acțiunea prestabilită a unui eveniment este ceea ce se întâmplă în mod normal ca urmare a declanșării evenimentului respectiv. Când faceți clic pe un link de ancoră, acțiunea implicită este ca browserul să navigheze către locația specificată în atributul "href" al acelui link. Dar, uneori, veți dori să dezactivați această acțiune prestabilită.
Annoyance "returnValue" / "preventDefault" nu este pe cont propriu; multe alte proprietăți ale obiectului eveniment sunt implementate inconsecvent, astfel că acest model / else / sau verificarea este o sarcină necesară.
Multe dintre bibliotecile JavaScript de astăzi normalizează obiectul evenimentului, ceea ce înseamnă că comenzi precum "e.preventDefault" vor fi disponibile în IE, deși trebuie să rețineți că în spatele scenei proprietatea "returnValue" este încă utilizată.
Bubblerea evenimentelor, cunoscută și ca "propagarea evenimentului", este atunci când un eveniment este declanșat și apoi evenimentul "bulează" prin DOM. Primul lucru pe care trebuie să-l observați este că nu toate evenimentele bubble, dar pentru cei care fac, iată cum funcționează:
Evenimentul se declanșează pe elementul țintă. Evenimentul declanșează apoi pe fiecare strămoș al acelui element - evenimentul se bulează prin DOM până când ajunge la cel mai înalt element:
Așa cum se arată în graficul de mai sus, dacă se face clic pe o ancorare într-un paragraf, evenimentul de clic al ancorei se va declanșa mai întâi și apoi, după aceea, evenimentul clicurilor paragrafelor va declanșa etc. până când elementul corpului este atins (corpul este cel mai mare element DOM care are un eveniment de clic).
Aceste evenimente se vor declanșa în acea ordine, ele nu se produc în același timp.
Ideea de bubblare a evenimentului nu poate avea prea mult sens la început, dar în cele din urmă devine clar că este o parte fundamentală a ceea ce considerăm "comportament normal". Când legați un handler la evenimentul clic al paragrafului pe care îl așteptați să îl declanșeze ori de câte ori este făcut clic pe paragraf, nu? Ei bine, exact ceea ce asigură "bubbling-ul evenimentului" - dacă paragraful are mai mulți copii, (s,
Acest comportament înfundat poate fi oprit în orice moment în timpul procesului. Așadar, dacă doriți doar ca evenimentul să explodeze până la paragraf, dar nu mai departe (nu la nodul corpului), puteți utiliza o altă metodă utilă găsită în obiectul Eveniment, "stopPropagation":
funcția myParagraphEventHandler (e) e = e || window.event; // Stop eveniment de la bubble: dacă (e.stopPropagation) // browserele compatibile cu W3C: e.stopPropagation (); altfel // IE: e.cancelBubble = true; // Funcția va fi legată de evenimentul clic al paragrafului: // Folosind funcția addEvent custom: addEvent (document.getElementsByTagName ('p') [0], 'click', myParagraphEventHandler);
Să spunem, de exemplu, că aveți o masă masivă cu multe rânduri de date. Legarea unui manipulator de evenimente pentru fiecare eveniment
var myTable = document.getElementById ('my-table'); myTable.onclick = funcția () // Depășirea incompatibilităților browserului: e = e || window.event; var targetNode = e.target || e.srcElement; // Testați dacă a fost o TR care a fost făcută clic pe: if (targetNode.nodeName.toLowerCase () === 'tr') alert ('Ați făcut clic pe un rând de tabel!');
Delegarea evenimentelor se bazează pe bâzâitul evenimentului. Codul de mai sus nu ar funcționa dacă barbotarea a fost oprită înainte de a ajunge la nodul "tabel".
Am abordat cum să manipulăm elementele DOM și am discutat, într-o mare mulțime de adâncimi, modelul evenimentului browserului. Sper că ați învățat ceva astăzi! Ca de obicei, dacă aveți întrebări, vă rugăm să nu ezitați să întrebați.