TERZA PARTE
14. DOM (Modello a Oggetti del Documento)
Uno dei più significativi progressi associati a JavaScript è stato
il lavoro di standardizzazione intrapreso dal W3C
in collaborazione con tutti i maggiori produttori di browser,
volto alla creazione di un modello ad oggetti consistente.
Sebbene il BOM Browser Object Model di default,
fornisca un insieme di funzionalità abbastanza completo e soddisfacente,
le differenti implementazioni influenzate da un browser o da un altro,
hanno rallentato la diffusione su larga scala dell'uso di JavaScript.
Tutto ciò è cambiato con il rilascio della raccomandazione del DOM da parte del W3C,
e della sua DEFINIZIONE:
Il DOM (Document Object Model)
È una interfaccia indipendente dal linguaggio e dalla piattaforma
che permette ai programmi e agli script di accedere e di modificare dinamicamente
- i contenuti, - la struttura e - lo stile dei documenti.
Il documento può essere ulteriormente processato e i risultati
reincorporati nella pagina originale.
15. Il Document Object Model (DOM)
Il Document Object Model è un'API (Interfaccia di Programmazione),
indipendente da una qualsiasi piattaforma o linguaggio,
attraverso cui è possibile accedere e modificare il contenuto, la struttura e lo stile
di un documento HtmL,
trasformando la pagina web statica in una applicazione Web interattiva.
È importante capire che gli elementi annidati di un documento HtmL sono rappresentati nel DOM come una
struttura ad albero di oggetti.
La rappresentazione ad albero di un documento HtmL contiene
nodi che rappresentano gli elementi HtmL,
come <body>
e <p>
, e nodi che rappresentano le stringhe di testo.
Un documento HtmL può contenere anche nodi che rappresentano commenti HtmL.
L'immagine che segue raffigura quanto appena detto
- L'oggetto
document
è l'elemento root (radice)
degli altri elementi posti sotto di esso.
- L'elemento
html
è il parent (genitore)
dei suoi sotto-indicati elementi figlio.
- Gli elementi
head
e body
sono i children (figli)
dell'elemento html
.
- Gli elementi
head
e body
sono tra di essi siblings (fratelli).
- L'elemento
body
è un ancestor (antenato)
degli elementi-testuali e dell'elemento em
.
- Gli elementi-testuali e gli elementi
h1
e p
sono i descendents (discendenti) dell'elemento body
.
Per il DOM, un documento HtmL è formato da una gerarchia di oggetti
disposti ad albero
laddove ogni oggetto, denominato nodo,
può rappresentare l'equivalente elemento HtmL (nodo elemento)
oppure un semplice testo (nodo testuale).
Questi nodi, sono delle interfacce dotate di proprietà e metodi
e implementano l'interfaccia Node
,
che fornisce a sua volta le proprietà e i metodi fondamentali
per attraversare e manipolare i relativi elementi dell'albero.
Uno degli aspetti più importanti del DOM è la possibilità di selezionare,
e dunque ottenere come oggetti JavaScript,
gli elementi HTML di cui è stato eseguita l'analisi e quindi visualizzati
all'interno del documento HtmL nel browser.
È possibile eseguire una query su un documento per ottenere uno o più elementi:
"con un
attributo id specificato;
"con un
attributo name specificato;
"con il
nome di tag specificato;
"con
la classe o le classi CSS specificate nel selettore.
--------------------------------------------------------------------------------
Proprietà HtmL DOM:
Proprietà | DESCRIZIONE |
x.innerHTML | il valore di testo x |
x.nodeName | il nome di x |
x.nodeValue | il valore di x |
x.parentNode | il nodo padre di x |
x.childNodes | i nodi figlio di x |
x.attributes | gli attributi dei nodi x |
Nota: Nella lista di cui sopra, x è un oggetto nodo (elemento HtmL).
--------------------------------------------------------------------------------
Metodi HtmL DOM
METODO | DESCRIZIONE |
x.getElementById(unID) | ottenere l'elemento con un ID specificato |
x.getElementsByTagName (nomeTag) | ottenere tutti gli elementi con un nome tag specificato |
x.appendChild(nodo) | inserire un nodo figlio di x |
x.removeChild(nodo)> | rimuovere un nodo figlio di x |
Nota: Nella lista di cui sopra, x è un oggetto nodo (elemento HtmL).
--------------------------------------------------------------------------------
La proprietà innerHTML
Il modo più semplice per ottenere o modificare il contenuto di un elemento è tramite la proprietà innerHTML.
innerHTML non è una parte della specifica W3C DOM. Tuttavia, è supportato da tutti i principali browser.
La proprietà innerHTML è utile per restituire o sostituire il contenuto di elementi HtmL
Esempio:
Il codice seguente ottiene innerHTML(testo) ovvero:
"Il testo contenuto nel paragrafo é: Ciao Mondo!"
dall'elemento <p>
con id = "intro"
<html>
<body>
<p id="intro">Ciao Mondo!</p>
<script>
txt = document.getElementById("intro").innerHTML;
document.write("<p>Il testo contenuto
nel paragrafo é: " + txt + "</p>");
</script>
</body>
</html>
Nell'esempio precedente, getElementById()
è un metodo,
mentre innerHTML
è una proprietà.
Selezione degli elementi di un documento
- Element
getElementById(elementId)
:
consente di ottenere un nodo elemento che ha l'attributo
id
uguale al parametro elementId
. In caso di ricerca positiva il metodo ritorna l'oggetto
elemento altrimenti ritorna il valore null
. È importante considerare che l'attributo id
assegnato a un elemento HTML dovrebbe essere univoco, ossia non dovrebbero esservi altri elementi con quello stesso
id
. Infatti, a riguardo, la specifica del DOM nel modulo core statuisce che se due o più elementi hanno il
valore dell'attributo id
uguale ciò che è ritornato non è definito (in ogni caso i browser ritornano il
primo elemento trovato).
- NodeList
getElementsByName(elementName)
:
consente di ottenere uno o più nodi elemento che hanno
l'attributo name
uguale a quello indicato dal parametro elementName
. In caso di ricerca positiva
il metodo ritorna un oggetto di tipo NodeList
, che è in pratica un array che contiene oggetti di tipo
Node
che rappresentano gli elementi trovati, altrimenti ritorna comunque un array di tipo NodeList
che è però vuoto (0
elementi). Per quanto concerne l'attributo name
ricordiamo che esso
dovrebbe essere utilizzato solo per i seguenti elementi HTML: form
, iframe
, input
,
keygen
, map
, object
, output
, select
, textarea
,
button
e fieldset
. In ogni caso, se usato anche per altri elementi, i browser inseriscono comunque
quelli trovati nell'array ritornato dal metodo.
- NodeList
getElementsByTagName(tagname)
:
consente di ottenere uno o più nodi elemento che hanno il
nome del tag uguale a quello indicato dal parametro tagname
. Il metodo ritorna un oggetto di tipo
NodeList
, laddove l'array può essere vuoto o può contenere gli elementi trovati con quel tag nell'ordine
cui sono stati definiti nel documento.
- NodeList
getElementsByClassName(classNames)
:
consente di ottenere uno o più nodi elemento che
hanno il valore dell'attributo class
uguale a quello indicato dal parametro classNames
. Il metodo
ritorna un oggetto di tipo NodeList
, laddove l'array può essere vuoto o può contenere gli elementi trovati
con il valore dell'attributo class
indicato nell'ordine in cui sono stati definiti nel documento. Per quanto
concerne l'attributo class
, esso può essere valorizzato con un identificatore oppure con più identificatori
separati dal carattere spazio ed è utilizzato per definire un insieme di elementi tra loro correlati (appartengono alla
stessa classe). È molto utilizzato per definire un insieme di elementi cui applicare una determinata regola CSS tramite il
selettore di classe. Infine, tale metodo, come il metodo getElementsByTagName
, può essere impiegato come
proprietà di un elemento.
- NodeList
querySelectorAll(selectors)
:
ritorna un oggetto di tipo NodeList
, laddove
ogni elemento dell'array è un elemento HTML che soddisfa i criteri selettivi impostati tramite il parametro
selectors
. Se non vi sono elementi trovati allora l'oggetto NodeList
sarà un array vuoto.
- Element
querySelector(selectors)
:
ritorna il primo elemento che soddisfa i criteri selettivi
impostati tramite il parametro selectors
oppure il valore null
se non è trovato alcun
elemento.
- Il metodo
querySelectorAll
effettua una query sul DOM e restituisce tutti gli oggetti recuperati.
Qualunque quantità di oggetti sia recuperata dalla query (nessuno, uno o più di uno).
il metodo querySelectorAll
restituisce una lista.
Quello che rende veramente speciali i metodi querySelector
e querySelectorAll
è il fatto che, per ricercare gli oggetti da recuperare, utilizzano i selettori Css3.
Gli esempi che sono mostrati nel codice qui sotto danno una precisa idea di quanto questi
metodi semplifichinola vita.
var obj, list;
obj = document.querySelector("#mainText");
// Recupera il primo oggetto con ID mainText
list = document.querySelectorAll("div");
// Recupera tutti div
list = document.querySelectorAll(".myCssClass");
// Recupera tutti i tag con classe CSS "myCssClass"
obj = document.querySelector("div");
// Recupera il primo tag div
list = document.querySelectorAll("div p");
// Recupera tutti i tag p contenuti in un tag div
list = document.querySelectorAll("div > p");
// Recupera tutti i tag p figli diretti di un tag div
list = document.querySelectorAll("table tr:nth-child(even)");
// Recupera le righe pari di una tabella
list = document.querySelectorAll("form input[disabled]");
// Recupera tutti gli elementi disabilitati
list = document.querySelectorAll("form input[type='checkbox']");
// Recupera tutti i checkbox
I metodi mostrati in questo esempio dimostrano come i metodi
querySelector
e querySelectorAll
coprano gran parte delle esigenze.
Spostamento tra gli elementi di un documento
Come già detto, per il DOM, gli elementi HTML sono rappresentati all'interno di un documento come un albero gerarchico di
nodi che hanno delle relazioni di parentela, un tipo, un valore e un nome. Al fine di “attraversare” tale albero per
spostarsi tra i suoi nodi possiamo utilizzare le seguenti proprietà sia sull'oggetto document
sia sui relativi
oggetti elemento o testo.
parentNode
: ritorna il nodo genitore del nodo cui tale proprietà è applicata.
childNodes
: ritorna un oggetto NodeList
, laddove l'array contiene i nodi figlio del nodo cui
tale proprietà è applicata.
firstChild
: ritorna il primo nodo figlio del nodo cui tale proprietà è applicata o il valore
null
se il nodo non ha figli.
lastChild
: ritorna l'ultimo nodo figlio del nodo cui tale proprietà è applicata o il valore
null
se il nodo non ha figli.
nextSibling
: ritorna il nodo fratello che segue immediatamente il nodo cui tale proprietà è applicata o il
valore null
se il nodo non ha fratelli.
previousSibling
: ritorna il nodo fratello che immediatamente precede il nodo cui tale proprietà è
applicata o il valore null
se il nodo non ha fratelli.
Le proprietà appena illustrate tengono conto non solo dei nodi elemento ma anche dei nodi di tipo testo. Ciò implica che i
new line e gli spazi bianchi scritti tra gli elementi HTML siano considerati come nodi testo e pertanto riportati durante la
valutazione delle proprietà.
Per consentire dunque di spostarsi solo tra i nodi di tipo elemento il W3C ha scritto la specifica denominata Element
Traversal Specification, con la quale ha definito l'interfaccia ElementTraversal
con le seguenti proprietà.
firstElementChild
e lastElementChild
: come firstChild
e lastChild
ma
tiene conto solo dei nodi figlio di tipo elemento.
nextElementSibling
e previousElementSibling
: come nextSibling
e
previousSibling
ma tiene conto solo dei nodi fratello di tipo elemento.
childElementCount
: ritorna un valore numerico riportante il numero di nodi figlio di tipo elemento del nodo
dove la proprietà è applicata.
Modifica dell'albero dei nodi
Una caratteristica decisamente potente del DOM è che consente di creare, inserire, cancellare e sostituire i nodi di un
albero gerarchico di un documento HTML. Possiamo infatti utilizzare i seguenti metodi, proprietà dell'oggetto
document
.
Element createElement(DOMString tagName)
: crea un nuovo elemento del tipo indicato dal parametro
tagName
e lo ritorna come oggetto di tipo Element
.
Text createTextNode(DOMString data)
: crea un nodo di tipo testo avente come contenuto quello indicato dal
parametro data
e lo ritorna come oggetto di tipo Text
.
Node importNode(Node importedNode, boolean deep)
: importa un nodo di un documento esterno, di cui il
parametro importedNode
, in modo che possa essere inserito, come copia, nel documento corrente. Il parametro
deep
se vale true
copia ricorsivamente anche gli elementi figlio del nodo da importare, mentre se
vale false
copia solo il nodo stesso ma senza i suoi figli (shallow copy). Tale metodo ritorna un
oggetto di tipo Node
che è la copia dell'oggetto importedNode
, che non viene comunque rimosso
dal suo documento originario.
Node adoptNode(Node source)
: importa un nodo di un documento esterno, di cui il parametro
source
, in modo che possa essere inserito nel documento corrente. Nel compiere tale operazione in automatico
importa anche tutti i suoi nodi figlio e rimuove il nodo source dal documento esterno.
Abbiamo poi i seguenti metodi che sono invocati come proprietà di un nodo.
Node appendChild(Node newChild)
: inserisce il nodo figlio newChild
come ultimo figlio del nodo
cui tale metodo è invocato e lo ritorna come oggetto di tipo Node
. Se newChild
è già presente lo
stesso viene prima rimosso e poi reinserito come last child.
Node insertBefore(Node newChild, Node refChild)
: inserisce il nodo newChild
prima del nodo
refChild
e lo ritorna come oggetto di tipo Node
. Il metodo deve essere invocato su un nodo che è
un genitore di refChild
, che deve essere un suo figlio esistente, altrimenti se refChild
è
null
newChild
sarà inserito come ultimo figlio. Se newChild
è già presente lo
stesso viene prima rimosso e poi reinserito prima del nodo indicato.
Node removeChild(Node oldChild)
: rimuove da un nodo genitore il figlio indicato dal parametro
oldChild
e lo ritorna come oggetto di tipo Node
.
Node replaceChild(Node newChild, Node oldChild)
: sostituisce da un nodo genitore il nodo indicato dal
parametro oldChild
con il nodo indicato dal parametro newChild
e ritorna il nodo sostituito come
oggetto di tipo Node
.
Manipolazione degli attributi degli elementi
Gli elementi HTML hanno degli attributi che permettono di specificare opzioni o dettagli supplementari e sono formati da un
nome e un valore, che sono replicati come proprietà degli elementi oggetti del DOM.
La “sincronizzazione” tra un attributo di un elemento HTML e una proprietà di un oggetto elemento del DOM avviene
secondo le seguenti regole.
- Se un attributo è formato da una sola parola e indipendentemente se è scritto in minuscolo o maiuscolo, la relativa
proprietà è scritta in minuscolo.
- Se un attributo è formato da due o più parole, la relativa proprietà è scritta ponendo l'iniziale della prima
parola in minuscolo e l'iniziale delle successive parole in maiuscolo (per esempio, l'attributo
readonly
diventa la proprietà readOnly
).
- Se un attributo ha un nome che è uguale a una parola chiave del linguaggio JavaScript la relativa proprietà viene
prefissa con
html
(per esempio, l'attributo for
dell'elemento label
o
dell'elemento output
diventa htmlFor
). A questa regola fa eccezione l'attributo
class
che diventa la proprietà className
.
- A seconda del tipo di valore inseribile in un attributo (stringa, booleano, numerico e così via) la relativa proprietà
imposterà od otterrà un valore dello stesso tipo.
DOMString getAttribute(DOMString name)
: ritorna come stringa il valore dell'attributo di cui il nome
fornito dal parametro name
.
void setAttribute(DOMString name, DOMString value)
: assegna il valore di cui il parametro value
all'attributo di cui il parametro name
. Se l'attributo è già presente allora il suo valore sarà
sostituito da quello di cui value
, altrimenti sarà creato un nuovo attributo con il nome name
e il
valore value
.
void removeAttribute(DOMString name)
: rimuove l'attributo di cui il nome name
e se lo stesso
non esiste il metodo non ha alcun effetto.
boolean hasAttribute(DOMString name)
: ritorna un valore booleano che indica se l'attributo di cui il
parametro name
è esistente nell'elemento corrente.
Nodi di tipo attributo
La manipolazione degli attributi di un elemento può avvenire anche utilizzandoli come nodi di tipo
attributo. In questo caso l'interfaccia Node
mette a disposizione la proprietà attributes
, che
ritorna un oggetto live di tipo NamedNodeMap
che è implementato come una collezione a sola lettura di nodi,
contenente come elementi tutti gli attributi trovati, di tipo Attr
, per un determinato nodo. Ogni attributo è
dunque un oggetto di tipo Attr
che può essere gestito con le sue proprietà name
, che ritorna il
nome dell'attributo, e value
, che ritorna o imposta il valore dell'attributo. La collezione di tipo
NamedNodeMap
può essere utilizzata con la bracket notation inserendo tra parentesi quadre un indice
numerico oppure il nome di un attributo.
Infine, l'interfaccia Element
fornisce i metodi
Attr getAttributeNode(DOMString name)
,
Attr setAttributeNode(Attr newAttr)
e Attr removeAttributeNode(Attr oldAttr)
per manipolare gli attributi di un elemento come oggetti di tipo attributo.