img

"Guida al "MODELLO OGGETTO DEL Documento"
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

immagine DOM

  1. L'oggetto document è l'elemento root (radice)
    degli altri elementi posti sotto di esso.

  2. L'elemento html è il parent (genitore)
    dei suoi sotto-indicati elementi figlio.

  3. Gli elementi head e body sono i children (figli)
    dell'elemento html.

  4. Gli elementi head e body sono tra di essi siblings (fratelli).

  5. L'elemento body è un ancestor (antenato)
    degli elementi-testuali e dell'elemento em.

  6. 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.innerHTMLil valore di testo x
x.nodeNameil nome di x
x.nodeValueil valore di x
x.parentNodeil nodo padre di x
x.childNodesi nodi figlio di x
x.attributesgli attributi dei nodi x

Nota: Nella lista di cui sopra, x è un oggetto nodo (elemento HtmL).

--------------------------------------------------------------------------------

Metodi HtmL DOM

METODODESCRIZIONE
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.