Guida introduttiva al Document Object Model
Transcript
Guida introduttiva al Document Object Model
Guida introduttiva al Document Object Model (© I.D. Falconi ~ 27.02.13) Il DOM (Document Object Model) è un modello che descrive come i diversi oggetti di una pagina web sono collegati tra loro. Tecnicamente è un’ API (Application Programming Interface), ovvero, un'interfaccia per la programmazione di applicazioni; in pratica è un insieme di funzioni, metodi e proprietà, che i programmi possono richiamare per interagire, in modo trasparente, col sistema sottostante. Il DOM è un'API definita dal W3C, indipendente dalla piattaforma, che descrive la struttura di un documento HTML (e XML), tramite il quale i costruttori di pagine web possono accedere a tutti gli elementi della pagina stessa. Questo, ovviamente, non significa che i produttori di browser lo rispettino. Da qui nascono i problemi relativi alla diversa interpretazione dei browser. Il DOM non è una parte di JavaScript. JavaScript è solo un modo per accedere al DOM, e non è l'unico. Cerchiamo ora di capire nel dettaglio come il DOM struttura una pagina attraverso un semplice esempio: <html> <head> <title>IL DOM</title> <head> <body> <p id="paragrafo1">Pagina di <span id=”evidenzia”>PROVA</span> Document Object Model</p> </body> </html> Il DOM rappresenta una generica pagina web (il documento) tramite un albero, secondo le relazioni che legano e collegano i vari elementi presenti nel documento stesso, evidenziandone le parentele e le caratteristiche: in sostanza ogni elemento presente nella pagina, ogni tag (come <title>), ogni testo (come "Pagina di") è un nodo. I nodi possono anche avere attributi e proprietà. Inoltre un nodo può contenere altri nodi, in questo caso si parla più propriamente di nodo elemento. Nell’esempio, il nodo elemento html ha due figli, head e body. L’elemento body ha un figlio, p, che a sua volta ha tre figli, due nodi di testo (“Pagina di “ e “ Document Object Model”), e un nodo elemento, span. Quest’ultimo ha come figlio un nodo di testo (“PROVA”). Il nodo elemento head ha invece un solo figlio, title, avente a sua volta come figlio il nodo di testo “IL DOM”. Tramite il DOM sarà possibile accedere e manipolare ogni nodo, aggiungerne di nuovi dinamicamente, ed eliminarne altri già presenti (queste possibilità, ovviamente, sono offerte da tutti i browser che supportano il DOM W3C). Prima di entrare nella descrizione dei principali metodi e proprietà del DOM, definiamo più esattamente la differenza tra elemento e nodo. Un nodo elemento (element) è contraddistinto da un tag. Esso può quindi contenere al suo interno altri elementi ( si pensi al tag TABLE, che può contenere le righe e le celle della tabella stessa, e a loro volta questi possono contenere altri elementi). Il nodo (node) ha un significato più ampio: oltre ad includere nella sua definizione tutti gli elementi, un nodo può essere anche un testo o un attributo. I nodi di testo, a differenza di tutti gli altri nodi, non possono avere attributi e non possono contenere altri nodi. TAB 1 - Principali tipi di nodo relativi ad HTML NodeType NodeName NodeValue 1 (elemento) nome TAG null 2 (attributo) nome attributo valore attributo #text testo contenuto 8 (commento) #comment testo di commento 9 (documento) #document null oggetto radice DOCTYPE null specifica DTD #document-fragment null Uno o più nodi esterni al documento 3 (testo) 10 (DocumentType) 11 (frammento) DESCRIZIONE Qualsiasi TAG attributo di un elemento frammento di testo di un elemento commento html TAB 2 - Proprietà degli oggetti nodo Proprietà tipo DESCRIZIONE nodeName stringa varia a seconda del tipo di nodo (TAB 1) nodeValue stringa varia a seconda del tipo di nodo (TAB 1) nodeType intero Costante che rappresenta ciascun tipo parentNode oggetto Riferimento al contenitore immediatamente più esterno childNodes collection tutti i nodi figli in ordine di codice sorgente firstChild oggetto riferimento al primo nodo figlio lastChild oggetto riferimento all’ultimo nodo figlio previousSibling oggetto riferimento al precedente nodo fratello nextSibling oggetto riferimento al successivo nodo fratello attributes NodeMap collection di nodi attributo L'OGGETTO DOCUMENT document è l'oggetto che contiene tutti gli elementi della pagina. In prima approssimazione, possiamo far corrispondere "document" al tag <html>. Più propriamente, corrisponde a tutto il codice della pagina, anche esterno al tag <html>, come ad esempio la definizione del DOCTYPE. Fatta questa premessa, esaminiamo i principali metodi di document, che si possono dividere tra: metodi per l’accesso agli elementi della pagina metodi per la creazione di nuovi elementi Accesso agli elementi della pagina A supporto di questa necessità il DOM fornisce 2 metodi: getElementById() getElementsByTagName() getElementById() - permette di recuperare l'elemento caratterizzato univocamente dal valore del proprio attributo ID. In particolare restituisce un riferimento all'elemento in questione. elemento=document.getElementById(idElemento) - idElemento è il valore (unico nella pagina) dell'attributo ID dell'elemento che si vuole recuperare. getElementsByTagName() - permette di recuperare l'insieme degli elementi caratterizzati dallo stesso tag, organizzati in array, nell'ordine in cui compaiono nel codice della pagina. listaElementi=document.getElementsByTagName(nomeTAG) - nomeTAG è il nome del tag di cui si vuole recuperare la lista. L'array può, ovviamente, essere scandito con la consueta sintassi, ovvero usando le parentesi quadre: listaElementi[indice]. Il W3C fornisce un metodo alternativo per scorrere le liste dei nodi: listaElementi.item(indice). Questa stessa sintassi si può applicare a tutti gli array di nodi che incontreremo. Si utilizzerà uno o l’altro dei due metodi getElementById e getElementsByTagName a seconda che si voglia recuperare un elemento particolare oppure una famiglia di elementi con le stesse caratteristiche. Creazione di nuovi elementi Passiamo ora ai metodi di document che si occupano di creare nuovi elementi della pagina: createElement() createTextNode() createElement() - permette di creare un nuovo elemento di qualunque tipo. Ritorna un riferimento al nuovo elemento creato. nuovoElemento = document.createElement(nomeTAG) - nuovoElemento è la variabile che conterrà il riferimento al nuovo elemento creato - nomeTAG è il nome del Tag di cui si vuole creare un nuovo elemento createTextNode() - permette di creare un nuovo nodo di testo. Questi sono nodi particolari, che non possono contenere altri nodi, né possono avere attributi, sono quindi nodi terminali (foglie). In particolare, questo metodo, restituisce un riferimento al nuovo nodo di testo creato. nodoTesto = document.createTextNode(testo) - nodoTesto è la variabile che conterrà il riferimento al nuovo nodo di testo - testo è la stringa di testo da inserire nel nuovo nodo In realtà il nodo non viene visualizzato: resterà nella memoria del browser fino alla chiamata di un opportuno metodo di inserimento nella pagina. Gestione dei nodi elemento I metodi per element, consentono per lo più di gestire e manipolare le caratteristiche di ogni singolo elemento, come recuperare, impostare e rimuovere gli attributi dell'elemento stesso. getElementsByTagName() - stesso metodo visto per document, con identica sintassi e semantica. In questo caso naturalmente ritorna la lista degli elementi contenuti all'interno di un certo elemento. Possiamo pensare all’elemento come una sottoradice e riapplicare il modello visto finora. setAttribute() - permette di creare un nuovo attributo per l'elemento specificato. Qualora l'attributo sia già presente, il metodo ne sovrascrive il valore. element.setAttribute(nomeAttributo,valoreAttributo) - nomeAttributo è la stringa col nome dell'attributo che deve essere inserito o modificato - valoreAttributo è la stringa col valore da assegnare all'attributo specificato getAttribute() - recupera il valore di un attributo dell'elemento. element.getAttribute(nomeAttributo) - nomeAttributo è la stringa col nome dell'attributo di cui si vuole recuperare il valore removeAttribute() - rimuove l'attributo passato come parametro. Qualora l'attributo abbia un valore di default, sarà questo il nuovo valore assunto dall'attributo. element.removeAttribute(nomeAttributo) - nomeAttributo è la stringa col nome dell'attributo che si vuole eliminare tagName - restituisce il nome del tag dell'elemento associato. nomeTag = element.tagName I nodi Come già anticipato, nella definizione di node ricadono non solo gli elementi, che possono a loro volta avere elementi figli o attributi, ma anche particolari componenti di una pagina, come il testo o i commenti. Questi ultimi, non potendo avere attributi né includere altri elementi, non sarebbero raggiungibili con i metodi visti finora. In questa sezione verranno mostrati metodi e proprietà proprie di ogni node, con i quali è possibile scorrere la struttura della pagina per recuperare e manipolare ogni nodo. Proprietà childNodes collection contenente l’insieme dei nodi figli. Un nodo figlio (child) è un nodo contenuto in quello considerato ed è quindi nel livello gerarchico immediatamente sottostante. Qualora il nodo non possegga figli, la proprietà restituisce un array vuoto. arrayFigli = node.childNodes firstChild primo figlio del nodo al quale è applicata. Corrisponde all'elemento di indice 0 di childNodes. Se il nodo non ha sottonodi restituisce null. nodoFiglio = node.firstChild // equivalente node.childNodes[0] lastChild ultimo figlio del nodo al quale è applicata. Corrisponde all' ultimo elemento di childNodes. Se il nodo non ha sottonodi restituisce null. nodoFiglio = node.lastChild // equivalente node.childNodes[node.childNodes.length-1] nextSibling nodo adiacente successivo a quello al quale è applicato. Se il nodo non ha "fratelli minori", la proprietà restituisce null. nodoSuccessivo = node.nextSibling previousSibling nodo adiacente precedente a quello al quale è applicato. Se il nodo non ha "fratelli maggiori", la proprietà restituisce null. nodoPrecedente = node.previousSibling parentNode nodo padre di quello al quale è applicato. Il nodo document è il solo all’interno della pagina a non avere padre e, per esso, la proprietà restituisce null. nodoPadre = node.parentNode Esistono però altri casi in cui il parentNode è nullo. Si pensi ad esempio al nodo di testo creato in precedenza. In generale, qualsiasi nodo, finché non sarà inserito nella struttura gerarchica della pagina, non avrà alcun nodo padre. nodeValue valore del nodo. Il valore di ritorno dipende dal tipo di nodo in questione. In particolare, per i tag il valore ritornato è null, mentre per i nodi di testo è il testo. In quest'ultimo caso la proprietà è read/write, cioè consente non solo di leggere il testo, ma anche di modificarlo. valore = node.nodeValue Metodi hasChildNodes permette di verificare se un nodo possegga figli oppure no. Restituisce un valore booleano relativo al risultato della verifica: se il nodo contiene altri nodi restituisce true altrimenti false. node.hasChildNodes() I prossimi metodi sono finalizzati ad inserire o eliminare in maniera mirata gli elementi dalla struttura gerarchica della pagina. appendChild inserisce un nuovo nodo alla fine della lista dei figli del nodo al quale è applicato. node.appendChild(nodo) nodo è il nodo che si vuole inserire insertBefore insersce un nuovo nodo nella lista dei figli del nodo al quale è applicato il metodo, appena prima di un nodo specificato. node.insertBefore(nodoDaInserire,nodoDiRiferimento) nodoDaInserire è il nodo che si vuole inserire nella lista dei figli di "node" nodoDiRiferimento è il nodo della lista dei figli di "node" prima del quale si vuole inserire il nuovo nodo. replaceChild inserisce un nuovo nodo al posto di un altro nella struttura della pagina. node.replaceChild(nuovoNodo,vecchioNodo) nuovoNodo è il nuovo nodo che si vuole inserire al posto del vecchio vecchioNodo è il nodo che si vuole rimpiazzare con il nuovo removeChild elimina e restituisce il nodo specificato dalla lista dei figli del nodo al quale è applicato. node.removeChild(nodoDaRimuovere) nodoDaRimuovere è il nuovo nodo che viene rimosso e restituito dal metodo Spesso può essere utile poter duplicare un nodo, con tutti i suoi attributi, e tutti i suoi figli senza dover ripercorrere tutti i passi che sono serviti per la sua creazione. A questo scopo si utilizza il seguente metodo: cloneNode duplica un nodo già esistente, offrendo la possibilità di scegliere se duplicare il singolo nodo, o anche tutti i suoi figli. Dopodiché il metodo ritorna il nodo clone. node.cloneNode(figli) figli (true|false) è un valore booleano che determina se clonare tutti i figli insieme al nodo al quale è applicato il metodo (true), oppure se clonare il solo nodo (false) Gestione degli spazi (CR LF TAB) nel DOM Alcuni browser considerano gli spazi come nodi di testo. Si consideri l’esempio che segue: <h2>Lista bevande</h2> <ul> <li>birra</li> <li>vino</li> </ul> Firefox, rappresenta il codice con la seguente struttura: H2 #text (Lista bevande) #text (line-break) UL o #text (line-break and tab) o LI #text (birra) o #text (line-break and tab) o LI #text (vino) o #text (line-break) o Internet Explorer, invece, ignora gli spazi: H2 o #text (Lista bevande) o LI o LI UL #text (birra) #text (vino) Possono insorgere complicazioni quando si utilizzano metodi che ricorrono alla relazione diretta fra i nodi, come firstChild o nextSibling. Per esempio, il primo figlio di <ul>, in IE è il primo <li>, mentre in FF è un nodo di testo contenente uno spazio. Le principali tecniche per evitare riferimenti errati sono: fare ricorso a riferimenti basati su collection: getElementsByTagName('li').item(0) invece che firstChild Saltare i nodi di testo contenenti spazi var item = list.firstChild; while(item.nodeName == '#text') { item = item.nextSibling; } Gestione eventi All’interno di un browser si verificano diversi tipi di eventi, ovvero, in generale, le azioni dell'utente sul documento. Alcuni di quelli più comuni riguardano il comportamento del mouse, della tastiera e degli elementi dei form. evento descrizione click dblclick click e doppio-click del mouse mouseover puntatore entra nell’area di un elemento mouseout puntatore esce dall’area di un elemento Keydown keypress keyup puntatore fasi successive della pressione di un tasto: mentre si preme, premuto, rilascio load immediatamente dopo il caricamento dell’elemento unload immediatamente prima della fase di scaricamento dell’elemento resize nella fase di ridimensionamento scroll nella fase scorrimento del contenuto di un elemento alcuni eventi sono trattati in maniera diversa dai vari browser. L’esecuzione di una determinata serie di azioni al verificarsi di un evento si ottiene progettando delle apposite funzioni dette tecnicamente event handlers e associandole all’evento. Tale operazione può essere compiuta in vari modi: modalità tradizionale ricavata l’id dell’elemento e definita la funzione, si associa la funzione all’evento (registrazione dell’evento) tramite dot notation elemento.onclick=event_handler esempio: <input type=”button” id=”test” name=”test” value=”TEST”> <script type=”text/javascript”> function avvisa() {alert(‘test OK’);} //event handler document.getElementById(‘test’).onclick=avvisa; </script> Occorre notare che l’evento click viene identificato tramite il parametro onclick . Questo semplice modo di gestire gli eventi è sufficiente per la maggior parte delle esigenze, ma esiste un metodo più complesso in grado di associare ad un evento più di una funzione. Modalità DOM W3C ricavata l’id dell’elemento e progettata la funzione, si associa la funzione all’evento (registrazione) tramite il metodo addEventListener dell’elemento. elemento. addEventListener(‘evento’,event_handler,cattura) esempio: <input type=”button” id=”test” name=”test” value=”TEST”> <script type=”text/javascript”> function avvisa() {alert(‘test OK’);} //event handler document.getElementById(‘test’). addEventListener('click', avvisa, false); </script> Il terzo parametro, booleano, specifica se l’evento deve essere catturato o se deve essere passato agli elementi contenitori. NOTA: tale metodo non è supportato dalle versioni di IE precedenti alla 9, per le quali Microsoft ha implementato il metodo attachEvent. elemento.attachEvent('onevento', event_handler); I due metodi hanno una sintassi diversa, ad esempio quello del W3C si riferisce all’evento con il suo nome e non con la sua proprietà (quindi senza il prefisso on), e prevede il terzo parametro. Per le versionisuccessive alla 9, il documento deve comunque riportare la specifica <!DOCTYPE html> Rimozione event handler Oltre ad aggiungere event handler ad un evento, possiamo toglierle: elemento.removeEventListener(‘evento’,event_handler,cattura) NOTA: tale metodo non è supportato dalle versioni di IE precedenti alla 9, per le quali Microsoft ha implementato il metodo detachEvent. elemento.detachEvent('onevento', event_handler); Un esempio di applicazione potrebbe essere l’attivazione “one-shot” di un event handler. Esempi applicativi DOM Aggiunta dinamica di un nuovo paragrafo in fondo alla pagina //crea in memoria un nuovo elemento <p> var nuovoPar=document.createElement("p"); // assegnazione id nuovoPar.setAttribute("id","newP"); // oppure: nuovoPar.id="newP"; // per aggiungere il testo, bisogna creare un nuovo nodo di testo e // appenderlo, come figlio, al paragrafo var nuovoTxt=document.createTextNode(“paragrafo creato dinamicamente”); nuovoPar.appendChild(nuovoTxt); // Il nuovo elemento paragrafo è pronto per essere inserito nel documento. // scegliamo di aggiungerlo in fondo alla pagina, come ultimo figlio del <body> var pagina=document.getElementsByTagName("body"); pagina[0].appendChild(nuovoPar); // oppure: document.body.appendChild(nuovoPar); Aggiunta dinamica di una nuova tabella (2 righe, 4 colonne) // riferimento a body var body = document.getElementsByTagName("body")[0]; // crea un elemento <table> e un elemento <tbody> var tbl = document.createElement("table"); var tblBody = document.createElement("tbody"); // crea le righe for (var r = 0; r < 2; r++) { // crea l’elemento <tr> var row = document.createElement("tr"); for (var c = 0; c < 4; c++) { // crea un elemento <td> e un nodo di testo, appende il nodo di testo // alla cella e appende la cella in fondo alla riga var cell = document.createElement("td"); var cellText = document.createTextNode("cella riga "+r+", colonna "+c); cell.appendChild(cellText); row.appendChild(cell); } // appende la riga in fondo a tbody tblBody.appendChild(row); } // appende tbody alla tabella tbl.appendChild(tblBody); // appende la tabella al body body.appendChild(tbl); // imposta id a “tab1” tbl.setAttribute("id", "tab1"); Aggiunta dinamica di una nuova riga in fondo ad una tabella esistente var tab= document.getElementById("tab1"); var tbody=tab.firstChild; var lastRow=tbody.lastChild; var nuovaRiga=lastRow.cloneNode(true); for (var i=0;i<nuovaRiga.childNodes.length;i++) nuovaRiga.childNodes[i].firstChild.nodeValue=’X’; tbody.appendChild(nuovaRiga);