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);