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 DESCRIZIONE
1 (elemento) nome TAG null Qualsiasi TAG
2 (attributo) nome attributo valore attributo attributo di un elemento
3 (testo) #text testo contenuto frammento di testo di un elemento
8 (commento) #comment testo di commento commento html
9 (documento) #document null oggetto radice
10 (DocumentType) DOCTYPE null specifica DTD
11 (frammento) #document-fragment null Uno o più nodi esterni al documento
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
o #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)
Internet Explorer, invece, ignora gli spazi:
H2
o #text (Lista bevande)
UL o LI
#text (birra) o LI
#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à tradizionalericavata 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 W3Cricavata 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.