Ciao e bentornati alla serie "JavaScript e DOM". L'ultima volta abbiamo trattato alcune nozioni di base su JavaScript e abbiamo toccato vari aspetti del modello di oggetti del documento, incluso come accedere ai nodi e attraversare il DOM. Oggi parleremo di come manipolare gli elementi all'interno del DOM e discuteremo il modello di eventi del browser.
Nell'ultima lezione abbiamo trattato i passaggi necessari per accedere a una raccolta di nodi DOM o un nodo DOM singolare. La vera magia si verifica quando poi si manipolano determinate proprietà risultanti in ciò che è ampiamente noto come "comportamento".
Ogni singolo nodo DOM ha una collezione di proprietà; la maggior parte di queste proprietà fornisce astrazioni a determinate funzionalità. Ad esempio, se si dispone di un elemento di paragrafo con un ID di "intro", è possibile modificare facilmente il colore di tale elemento tramite l'API DOM:
document.getElementById ('intro'). style.color = '# FF0000';
Per illustrare la natura dell'oggetto / proprietà di questa API, potrebbe essere più facile capire se lo suddividiamo assegnando ciascun oggetto a una variabile:
var myDocument = document; var myIntro = myDocument.getElementById ('intro'); var myIntroStyles = myIntro.style; // E ora possiamo impostare il colore: myIntroStyles.color = '# FF0000';
Ora che abbiamo un riferimento all'oggetto 'style' del paragrafo possiamo aggiungere altri stili CSS:
myIntroStyles.padding = '2px 3px 0 3px'; myIntroStyles.backgroundColor = '#FFF'; myIntroStyles.marginTop = '20px';
Stiamo solo usando i nomi di proprietà CSS di base qui. L'unica differenza è che dove normalmente si trova un trattino ('-') il testo è camel-cased. Quindi invece di 'margin-top' usiamo 'marginTop'. Il seguente, per esempio, no lavoro e produrrebbe un errore di sintassi:
myIntroStyles.padding-top = '10em'; // Produce un errore di sintassi: // - Il carattere '-' è l'operatore meno in JavaScript. // - Inoltre, non esiste questo nome di proprietà.
È possibile accedere alle proprietà in modalità array. Quindi, con questa conoscenza potremmo creare una piccola funzione per cambiare qualsiasi stile di un dato elemento:
function changeStyle (elem, property, val) elem.style [proprietà] = val; // Notare le parentesi quadre usate per accedere alla proprietà // Dovresti usare il plugin di cui sopra in questo modo: var myIntro = document.getElementById ('intro'); // Grab Intro paragraph changeStyle (myIntro, 'color', 'red');
Questo è solo un esempio: ad essere onesti, probabilmente non è una funzione molto utile in quanto, sintatticamente, è più rapido utilizzare i mezzi convenzionali illustrati in precedenza (ad es.. elem.style.color = 'rosso').
Oltre alla proprietà 'style' ci sono molti altri che puoi usare per manipolare determinati aspetti di un nodo / elemento. Infatti, se hai installato Firebug dovresti provare a "ispezionare un elemento", quindi fare clic sulla scheda "DOM" (normalmente a destra o sotto il pannello di visualizzazione degli elementi) per visualizzare tutte le sue proprietà:
È possibile accedere a tutte le proprietà utilizzando la notazione dot convenzionale (ad esempio Element.tabIndex). Non tutte le proprietà sono tipi di dati primitivi (stringhe, numeri, booleani ecc.); la proprietà 'style' per esempio, che abbiamo discusso in precedenza, è un oggetto che contiene le sue proprietà. Molte delle proprietà di un elemento saranno leggibili solo; quello che intendo con questo è che non puoi cambiare il loro valore. Ad esempio, non è possibile modificare direttamente la proprietà 'parentNode' di un nodo. Generalmente il browser genera un errore se si tenta di modificare una di queste proprietà di sola lettura: ad es. ERRORE: "impostazione di una proprietà che ha solo un getter". È solo qualcosa di cui essere a conoscenza ...
Un requisito comune è quello di modificare il contenuto all'interno di un elemento. Ci sono diversi modi per farlo. Di gran lunga il modo più semplice è usare la proprietà 'innerHTML', come questa:
var myIntro = document.getElementById ('intro'); // Sostituzione del contenuto corrente con nuovi contenuti: myIntro.innerHTML = 'Nuovo contenuto per il Stupefacente paragrafo!'; // Aggiunta al contenuto corrente: myIntro.innerHTML + = '... qualche altro contenuto ...';
L'unico problema con questo metodo è che non è specificato in nessuno standard e non è nelle specifiche DOM. Se non ti preoccupi di questo, vai avanti e usalo. Normalmente è molto più veloce dei metodi DOM tradizionali, che tratteremo in seguito.
Quando si crea il contenuto tramite l'API DOM è necessario essere a conoscenza di due diversi tipi di nodi, un nodo elemento e un nodo di testo. Ci sono molti altri tipi di nodi, ma questi due sono gli unici importanti per ora.
Per creare un elemento si usa il metodo 'createElement' e per creare un nodo di testo si usa il metodo 'createTextNode', entrambi sono mostrati di seguito:
var myIntro = document.getElementById ('intro'); // Vogliamo aggiungere del contenuto al paragrafo: var someText = 'Questo è il testo che voglio aggiungere'; var textNode = document.createTextNode (someText); myIntro.appendChild (TextNode);
Qui stiamo usando il metodo 'appendChild' per aggiungere il nostro nuovo nodo di testo al paragrafo. Farlo in questo modo richiede un po 'più del metodo innerHTML non standard, ma è comunque importante conoscere entrambi i modi in modo da poter prendere la decisione giusta. Ecco un esempio più avanzato che utilizza i metodi DOM:
var myIntro = document.getElementById ('intro'); // Vogliamo aggiungere un nuovo ancoraggio al paragrafo: // In primo luogo, creiamo il nuovo elemento di ancoraggio: var myNewLink = document.createElement ('a'); // myNewLink.href = 'http://google.com'; // myNewLink.appendChild (document.createTextNode ('Visit Google')); // Visita Google // Ora possiamo aggiungerlo al paragrafo: myIntro.appendChild (myNewLink);
Esiste anche un metodo DOM "insertBefore" che è abbastanza auto-esplicativo. Usando questi due metodi ('insertBefore' e 'appendChild') possiamo creare la nostra funzione 'insertAfter':
// 'Target' è l'elemento già presente nel DOM // 'Bullet' è l'elemento che vuoi inserire function insertAfter (target, bullet) target.nextSibling? target.parentNode.insertBefore (bullet, target.nextSibling): target.parentNode.appendChild (bullet); // Usiamo un operatore ternario nella funzione precedente: // Il suo formato: CONDITION? ESPRESSIONE SE VERO: ESPRESSIONE SE FALSA;
La funzione sopra controlla l'esistenza del prossimo fratello del bersaglio all'interno del DOM, se esiste allora inserirà il nodo 'bullet' prima del fratello successivo del target, altrimenti assumerà che il target sia l'ultimo figlio di un elemento e quindi va bene aggiungere il proiettile come figlio del genitore. L'API DOM non ci fornisce alcun metodo 'insertAfter' perché non ce n'è bisogno: possiamo crearlo da soli.
C'è ancora molto da imparare sulla manipolazione di elementi all'interno del DOM, ma quanto sopra dovrebbe essere una base sufficiente su cui costruire.
Gli eventi del browser sono al centro di qualsiasi applicazione Web e della maggior parte dei miglioramenti di JavaScript. È attraverso questi eventi che definiamo quando qualcosa sta per accadere. Se hai un pulsante nel documento e hai bisogno di convalida di un modulo per essere eseguito quando fai clic su di esso, utilizzerai l'evento "clic". Di seguito è riportata una panoramica della maggior parte degli eventi del browser standard:
Nota: come abbiamo discusso l'ultima volta, il DOM e il linguaggio JavaScript sono due entità separate. Gli eventi del browser fanno parte dell'API DOM, non fanno parte di JavaScript.
Ci sono molti altri eventi tra cui scegliere. Quelle mostrate sopra sono le principali che incontrerete frequentemente nel codice JavaScript. Essere consapevoli del fatto che alcuni di essi hanno sottili differenze tra i browser. Inoltre, tieni presente che molti browser implementano eventi proprietari, ad esempio ci sono alcuni eventi specifici di Gecko, come "DOMContentLoaded" o "DOMMouseScroll". Puoi leggere ulteriori informazioni su questi qui: https://developer.mozilla.org / it / Gecko-Specific_DOM_Events
Abbiamo coperto gli eventi reali ma non abbiamo ancora discusso il processo di collegamento di una funzione a un evento. Qui è dove avviene la magia. Gli eventi elencati sopra si verificheranno indipendentemente dal fatto che tu abbia scritto o meno JavaScript, quindi per sfruttare il loro potere devi registrare "gestori di eventi", questo è un termine di fantasia per descrivere una funzione utilizzata per gestire un evento. Ecco un semplice esempio usando il di base modello di registrazione degli eventi (noto anche come "registrazione tradizionale degli eventi"):
Registrazione degli eventi di base:
// JavaScript: var myElement = document.getElementById ('my-button'); // Questa funzione sarà il nostro gestore di eventi: function buttonClick () alert ('Hai appena fatto clic sul pulsante!'); // Questa è la parte di registrazione degli eventi: myElement.onclick = buttonClick;
Abbiamo un pulsante HTML con un ID di "my-button" e ci siamo acceduti utilizzando il comando "document.getElementById". Quindi stiamo creando una nuova funzione che verrà successivamente assegnata alla proprietà DOM "onclick" del pulsante. Questo è tutto ciò che c'è da fare!
Il modello di "registrazione degli eventi di base" è semplice come si ottiene. Puoi anteporre l'evento che stai cercando con "on" e accedervi come proprietà di qualsiasi elemento con cui stai lavorando. Questa è essenzialmente la versione non invadente di fare qualcosa del genere (che non consiglio):
La gestione degli eventi in linea (utilizzando gli attributi HTML) è molto invadente e rende il tuo sito Web molto più difficile da mantenere. È preferibile utilizzare JavaScript non invadente e includere tutto nei rispettivi file ".js" che possono essere inclusi nel documento come / quando necessario. Mentre siamo in tema di JavaScript non invadente vorrei correggere l'idea sbagliata che le librerie come jQuery rendono "possibile codificare in modo discreto" - questo non è vero. Quando usi jQuery è altrettanto facile fare le cose nel modo sbagliato. Il motivo per cui non dovresti usare la gestione degli eventi in linea è esattamente lo stesso del motivo per cui non dovresti applicare gli stili CSS in linea (usando).
Registrazione eventi avanzata:
Non lasciare che questo nome ti fuorvii, solo perché si chiama "avanzato", non significa che sia meglio usarlo; infatti, la tecnica di cui abbiamo discusso in precedenza ("registrazione degli eventi di base") è perfettamente adatta per la maggior parte del tempo. L'uso della tecnica di base ha però un limite chiave; non è possibile associare più di una funzione a un evento. In realtà non è così male, perché puoi semplicemente chiamare un numero qualsiasi di altre funzioni all'interno di quella singola funzione, ma se hai bisogno di più controllo allora c'è un altro modo per registrare i gestori, inserisci il "modello di registrazione eventi avanzato".
Questo modello consente di associare più gestori a un singolo evento, vale a dire che più funzioni verranno eseguite quando si verifica un evento. Inoltre, questo modello consente di rimuovere facilmente qualsiasi gestore di eventi associato.
A rigor di termini, ci sono due diversi modelli in questa categoria; il W3C e Microsoft. Il modello W3C è supportato da tutti i browser moderni tranne IE e il modello Microsoft è supportato solo da IE. Ecco come useresti il modello del W3C:
// FORMAT: target.addEventListener (type, function, useCapture); // Esempio: var myIntro = document.getElementById ('intro'); myIntro.addEventListener ('click', introClick, false);
Ed ecco lo stesso, ma per IE (il modello di Microsoft):
// FORMAT: target.attachEvent ('on' + tipo, funzione); // Esempio: var myIntro = document.getElementById ('intro'); myIntro.attachEvent ('onclick', introClick);
Ed ecco la funzione 'introClick':
function introClick () alert ('Hai fatto clic sul paragrafo!');
A causa del fatto che nessuno dei due modelli funziona su tutti i browser, è una buona idea combinarli entrambi in una funzione personalizzata. Ecco una funzione 'addEvent' molto semplice, che funziona su browser:
function addEvent (elem, type, fn) if (elem.attachEvent) elem.attachEvent ('on' + type, fn); ritorno; if (elem.addEventListener) elem.addEventListener (type, fn, false);
La funzione verifica le proprietà 'attachEvent' e 'addEventListener' e quindi utilizza uno dei modelli dipendenti da quel test. Entrambi i modelli consentono di rimuovere anche i gestori di eventi, come mostrato in questa funzione "removeEvent":
function removeEvent (elem, type, fn) if (elem.detachEvent) elem.detachEvent ('on' + type, fn); ritorno; if (elem.removeEventListener) elem.removeEventListener (type, fn, false);
Useresti le funzioni in questo modo:
var myIntro = document.getElementById ('intro'); addEvent (myIntro, 'click', function () alert ('YOU CLICKED ME !!!'););
Si noti che abbiamo passato una funzione senza nome come terzo parametro. JavaScript ci consente di definire ed eseguire funzioni senza nominarle; funzioni di questo tipo sono chiamate "funzioni anonime" e possono essere molto utili, specialmente quando è necessario passare una funzione come parametro ad un'altra funzione. Avremmo potuto semplicemente inserire la nostra funzione 'introClick' (definita in precedenza) come terzo parametro, ma a volte è più conveniente farlo con una funzione anonima.
Se vuoi che un'azione si verifichi su un evento solo la prima volta che viene cliccato puoi fare qualcosa del genere:
// Nota che abbiamo già definito le funzioni addEvent / removeEvent // (Per poterle utilizzare devono essere incluse) var myIntro = document.getElementById ('intro'); addEvent (myIntro, 'click', oneClickOnly); function oneClickOnly () alert ('WOW!'); removeEvent (myIntro, 'click', oneClickOnly);
Stiamo rimuovendo il gestore non appena l'evento viene attivato per la prima volta. Non siamo stati in grado di utilizzare una funzione anonima nell'esempio sopra perché abbiamo dovuto conservare un riferimento alla funzione ('oneClickOnly') in modo che potessimo in seguito rimuoverlo. Detto questo, è effettivamente possibile ottenere con una funzione anonima (anonima):
addEvent (myIntro, 'click', function () alert ('WOW!'); removeEvent (myIntro, 'click', arguments.callee););
Qui siamo piuttosto sfacciati facendo riferimento alla proprietà "callee" dell'oggetto "arguments". L'oggetto 'arguments' contiene tutti i parametri passati di QUALSIASI funzione e contiene anche un riferimento alla funzione stessa ('callee'). In questo modo eliminiamo completamente la necessità di definire una funzione con nome (ad esempio, la funzione 'oneClickOnly' mostrata in precedenza).
A parte le ovvie differenze sintattiche tra il W3C e l'implementazione di Microsoft ci sono altre discrepanze che vale la pena notare. Quando si associa una funzione a un evento, la funzione deve essere eseguita nel contesto dell'elemento e pertanto la parola chiave "this" all'interno della funzione dovrebbe fare riferimento all'elemento; utilizzando il modello di registrazione degli eventi di base o il modello avanzato del W3C questo funziona senza errori, ma l'implementazione di Microsoft non riesce. Ecco un esempio di ciò che tu dovrebbero essere in grado di fare all'interno delle funzioni di gestione degli eventi:
function myEventHandler () this.style.display = 'none'; // Funziona correttamente, 'this' fa riferimento all'elemento: myIntro.onclick = myEventHandler; // Funziona correttamente, 'this' fa riferimento all'elemento: myIntro.addEventListener ('click', myEventHandler, false); // NON funziona correttamente, 'this' fa riferimento all'oggetto Window: myIntro.attachEvent ('onclick', myEventHandler);
Ci sono diversi modi per evitare / risolvere questo problema. L'opzione di gran lunga più semplice è quella di utilizzare il modello di base: non vi sono quasi incoerenze tra browser quando si utilizza questo modello. Se, tuttavia, si desidera utilizzare il modello avanzato e si richiede la parola chiave "this" per fare riferimento correttamente all'elemento, si dovrebbe dare un'occhiata ad alcune delle funzioni addEvent più ampiamente adottate, in particolare John Resig o Dean Edward (il suo doesn utilizzare anche il modello avanzato, eccellente!).
Un aspetto importante della gestione degli eventi che dobbiamo ancora discutere è qualcosa chiamato "Oggetto evento". Ogni volta che si associa una funzione a un evento, ad esempio ogni volta che si crea un gestore di eventi, alla funzione verrà passato un oggetto. Questo accade in modo nativo, quindi non è necessario intraprendere alcuna azione per indurlo. Questo oggetto evento contiene una varietà di informazioni sull'evento che si è appena verificato; contiene anche metodi eseguibili che hanno vari effetti comportamentali sull'evento. Ma, non sorprende, la Microsoft ha scelto il proprio modo di implementare questa "caratteristica"; I browser IE non passano questo oggetto evento, ma devi accedervi come una proprietà dell'oggetto globale della finestra; questo non è davvero un problema, è solo una seccatura:
function myEventHandler (e) // Notare l'argomento 'e' ... // Quando viene chiamata questa funzione, come risultato dell'evento // firing, l'oggetto evento verrà passato (in agenti conformi a W3C) // Facciamo ' e 'cross-browser friendly: e = e || window.event; // Ora possiamo tranquillamente fare riferimento a 'e' in tutti i browser moderni. // legheremmo la nostra funzione a un evento quaggiù ...
Per verificare l'esistenza dell'oggetto "e" (l'oggetto "Event") utilizziamo un operatore OR (logico) che stabilisce in pratica quanto segue: se "e" è un valore "falsy" (null, undefined, 0 ecc.) quindi assegnare 'window.event' a 'e'; altrimenti basta usare 'e'. Questo è un modo semplice e veloce per ottenere il vero oggetto Event in un ambiente cross-browser. Se non ti senti a tuo agio nell'usare operatori logici al di fuori di un'istruzione IF, allora questo costrutto potrebbe adattarti maggiormente:
if (! e) e = window.event; // Nessuna istruzione ELSE è necessaria poiché 'e' sarà // già definito in altri browser
Alcuni dei comandi e delle proprietà più utili di questo oggetto evento sono, purtroppo, implementati in modo incoerente attraverso i browser (vale a dire IE rispetto a tutti gli altri). Ad esempio, è possibile annullare l'azione predefinita di un evento utilizzando il metodo 'preventDefault ()' dell'oggetto Event ma in IE può essere raggiunto solo utilizzando la proprietà 'returnValue' dell'oggetto. Quindi, di nuovo, dobbiamo usare entrambi per poter ospitare tutti i browser:
function myEventHandler (e) e = e || window.event; // Prevenzione dell'azione predefinita di un evento: if (e.preventDefault) e.preventDefault (); else e.returnValue = false;
L'azione predefinita di un evento è ciò che normalmente accade a seguito dell'attivazione di quell'evento. Quando fai clic su un collegamento di ancoraggio, l'azione predefinita è che il browser passi alla posizione specificata nell'attributo "href" di quel collegamento. Ma a volte ti consigliamo di disabilitare questa azione predefinita.
Il fastidio 'returnValue' / 'preventDefault' non è da solo; molte altre proprietà dell'oggetto Event sono implementate in modo incoerente quindi questo if / else / o il modello di controllo è un'attività richiesta.
Molte delle librerie JavaScript odierne normalizzano l'oggetto evento, il che significa che comandi come "e.preventDefault" saranno disponibili in IE, anche se, dovresti notare che dietro le quinte la proprietà "returnValue" è ancora in uso.
Il bubbling degli eventi, noto anche come "propagazione degli eventi", è quando un evento viene attivato e quindi quell'evento "bolle" attraverso il DOM. La prima cosa da notare è che non tutti gli eventi sono in bolla, ma per quelli che lo fanno, ecco come funziona:
L'evento si attiva sull'elemento target. L'evento poi esplode su tutti gli antenati di quell'elemento - l'evento esplode attraverso il DOM fino a raggiungere l'elemento più in alto:
Come mostrato nell'immagine sopra, se si fa clic su un'ancora all'interno di un paragrafo, l'evento click dell'ancora si attiva per primo e poi, dopo che l'evento click del paragrafo si attiva ecc. Fino a quando viene raggiunto l'elemento body (il corpo è l'elemento DOM più alto che ha un evento click).
Questi eventi spareranno in questo ordine, non si verificano tutti contemporaneamente.
L'idea del bubbling degli eventi potrebbe non avere molto senso all'inizio, ma alla fine diventa chiaro che è una parte fondamentale di ciò che consideriamo un "comportamento normale". Quando si associa un gestore all'evento click del paragrafo che si aspetta che si attivi ogni volta che si fa clic sul paragrafo, giusto? Bene, questo è esattamente ciò che assicura "l'evento che bolle", se il paragrafo ha più figli, (S,
Questo comportamento di bubbling può essere fermato in QUALSIASI momento durante il processo. Quindi, se vuoi solo che l'evento rispecchi il paragrafo, ma non oltre (non al nodo del corpo), puoi utilizzare un altro metodo utile trovato nell'oggetto Event, "stopPropagation":
function myParagraphEventHandler (e) e = e || window.event; // Interrompe l'evento da bubbling up: if (e.stopPropagation) // Browser compatibili con W3C: e.stopPropagation (); else // IE: e.cancelBubble = true; // La funzione verrebbe associata all'evento click del paragrafo: // Usando la nostra funzione addEvent personalizzata: addEvent (document.getElementsByTagName ('p') [0], 'click', myParagraphEventHandler);
Diciamo, ad esempio, che hai una tabella enorme con molte righe di dati. Associare un gestore di eventi click ad ogni singolo
var myTable = document.getElementById ('my-table'); myTable.onclick = function () // Gestione delle incompatibilità del browser: e = e || window.event; var targetNode = e.target || e.srcElement; // Prova se era un TR su cui è stato fatto clic: if (targetNode.nodeName.toLowerCase () === 'tr') alert ('Hai fatto clic su una riga della tabella!');
La delega degli eventi si basa sul bubbling degli eventi. Il codice precedente non funzionerebbe se il bubbling fosse interrotto prima di raggiungere il nodo 'table'.
Abbiamo spiegato come manipolare gli elementi DOM e abbiamo discusso, in modo molto approfondito, del modello di eventi del browser. Spero che tu abbia imparato qualcosa oggi! Come al solito, se avete domande, non esitate a chiedere.