Dopo aver letto l’interessante articolo di ImpressiveWebs su alcune tecniche javascript mi è venuta un’idea che ti piacerà. Ho deciso di integrarlo con le rispettive tecniche jquery ma, anziché limitarle esclusivamente all’ambito jquery cerco di mantenere comunque un legame con il puro codice javascript.
Per quale motivo? Innanzitutto perché è interessante vedere come da jquery si possa passare ai classici oggetti javascript e poi perché in alcuni casi si preferisce programmare in javascript senza rinunciare ad alcune potenzialità di jquery come, ad esempio, i selettori, spesso argomento di scontro (e confronto) fra i vari framework. Inoltre, come dice ImpressiveWebs, alcuni sviluppatori si trovano a dover lavorare a progetti già avviati nei quali non è possibile (o non è sicuro) utilizzare librerie come jquery, mootools, dojo, ecc.
Un ringraziamento speciale va a ImpressiveWebs che mi ha gentilmente autorizzato a tradurre buona parte dell’articolo orginale.
getElementById: selezionare un elemento conoscendo l’id
Sappiamo che quasi tutti i tag html hanno l’attributo id. Questo attributo serve a identificare univocamente un elemento all’interno del documento html. Non è possibile specificare lo stesso id per due elementi diversi e, se ciò accade, il w3c validator segnalerà l’errore.
Supponiamo di avere un tag html con l’id “my_element_id”:
<div id="my_element_id">Questo è un elemento generico</div>
La caratteristica fondamentale dell’id ci permette di selezionare esattamente un elemento che ha come id un certo valore. Non a caso infatti la funzione restituisce un solo elemento e non un array di elementi, come avviene per funzioni che effettuano selezioni più generiche.
var elemento = document.getElementById("my_element_id");
Con jquery è possibile fare lo stesso ma, al contrario della precedente, la funzione jquery prevede la restituzione di un array di elementi poiché viene utilizzata anche per selezioni più generiche. Quindi sarà necessario selezionare il primo, nonché l’unico, elemento dell’array restituito. L’elemento ottenuto è esattamente uguale a quello ottenuto con la funzione getElementById.
var elemento = $("#my_element_id")[0];
Ottenuto l’elemento che si fà?
Si possono utilizzare tutte le proprietà e le funzioni (o meglio, metodi) javascript previste dal quel tipo di oggetto. Ad esempio:
elemento.style.border = "1px solid #000";
elemento.disabled = "true";
Se, invece, preferisci utilizzare jquery dovrai ricorrere necessariamente alle proprietà e ai metodi definiti nella libreria jquery. Ad esempio:
$("#my_element_id").css("border",""1px solid #000"");
$("#my_element_id").attr("disabled","true");
ATTENZIONE. Spesso si sbaglia la scrittura della funzione scrivendo getElementByID anziché getElementById (notare la “d” minuscola).
Vantaggi. L’utilizzo di questa funzione impone la definizione di identificatori per i vari elementi che si vogliono gestire all’interno di funzioni javascript. Questo rappresenta un vantaggio poiché si tende a migliorare la scrittura del codice html. Un’altro possibile vantaggio è la maggior velocità della selezione per id rispetto alla selezione fra un’insieme di più elementi, che vedremo di seguito.
getElementByTagName: selezionare un gruppo di tag
Se per qualche motivo non puoi o non vuoi specificare un id per alcuni elementi, questa funzione ti viene in aiuto. L’unico argomento della funzione è il tag degli elementi da selezionare i quali verranno poi restituiti in un array.
Esempio. Supponiamo di avere una lista di link e di voler selezionare solo quelli che hanno l’attributo “target” uguale a “_blank” in modo da modificare il valore di questo attributo in “_top”.
<ul id="lista_5link">
<li><a href="link1.html" target="_blank">Primo link</a></li>
<li><a href="link2.html">Secondo link</a></li>
<li><a href="link3.html" target="_blank">Terzo link</a></li>
<li><a href="link4.html" target="_top">Quarto link</a></li>
<li><a href="link5.html" target="_self">Quinto link</a></li>
</ul>
Per selezionare i tag “<a>” con l’attributo “target” uguale a “_blank” dobbiamo innanzitutto ottenere un’array di tutti i tag “<a>” e successivamente modificare quelli che rispettano la nostra condizione.
var myLinks = document.getElementsByTagName("a");
for(i=0; i<myLinks.length; i++)
{
if (myLinks[i].getAttribute("target") == "_blank")
myLinks[i].setAttribute("target","_top");
}
Con jquery il tutto si riduce a:
$("a[target='_blank']").attr("target","_top");
Con “a” si chiede di selezionare tutti i tag “<a>” e con “[target='_blank']” si chiede che questi tag abbiano l’attributo “target” uguale a “_blank”. Infine con attr("target","_top"), che viene eseguito solo se è stato trovato il tag richiesto, si imposta l’attributo “target” con il valore “_top”.
Miglioramento. Per evitare che la ricerca dei tag “<a>” venga effettuata all’interno dell’intero documento, si può impostare l’id alla lista in modo da restringere la ricerca agli elementi interni alla lista.
Al tag “<ul>” ho precedentemente assegnato l’id “lista_5link”. Ora vediamo come restringere la ricerca.
// Seleziona l'elemento con l'id "lista_5link"
var oggettoLista = document.getElementById("lista_5link");
// Dell'elemento "oggettoLista" ricaviamo l'insieme dei tag <a>
var myLinks = oggettoLista.getElementsByTagName("a");
// Il ciclo for rimane uguale al precedente
for(i=0; i<myLinks.length; i++)
{
if (myLinks[i].getAttribute("target") == "_blank")
myLinks[i].setAttribute("target","_top");
}
Con jquery si aggiunge il vincolo “#lista_5link”:
$("#lista_5link a[target='_blank']").attr("target","_top");
/* Invece di $("a[target='_blank']").attr("target","_top"); */
Soluzione ibrida
// Seleziona gli elementi <a> con jQuery
var myLinks = $("#lista_5link a")
// Il ciclo for rimane uguale al precedente
for(i=0; i<myLinks.length; i++)
{
if (myLinks[i].getAttribute("target") == "_blank")
myLinks[i].setAttribute("target","_top");
}
Osservazione. Potresti dirmi “perché non assegniamo lo stesso id a tutti i tag <a>?“. Questa operazione non si può fare poiché l’id, per sua natura, deve necessariamente essere unico all’interno del documento, cioè solo un elemento può avere un certo id. Infatti, come spiegato prima, l’id deve servire a identificare univocamente un solo elemento all’interno del documento html.
Leggere e scorrere i nodi del documento
Cosa sono i nodi? Un nodo è un qualsiasi elemento, tag, contenitore, blocco autonomo (es. <br />, <hr />), frammento di testo. I nodi hanno una relazione genitore-figlio. Ad esempio, nel caso della lista illustrata precedentemente, <ul> è il genitore e <li> il figlio, che a sua volta è il genitore di <a>.
Ogni elemento è un nodo e come tale ha alcune proprietà e metodi che ci permettono di ricavare i suoi figli diretti (children), il suo genitore (parent), e di manipolarli in vari modi.
Codice html che utilizzeremo per tutti gli esempi
<div id="parent_div">
<p>Paragrafo1</p>
<p>Paragrafo2</p>
<a href="#">Link</a>
<p>Paragrafo3</p>
<h5>Titolo h5</h5>
</div>
Ottenere i nodi figli con childNodes e children
Le due proprietà, childNode e children, sono costituite da un array dei figli diretti.
La differenza fra le due consiste nel fatto che children ignora i nodi di tipo testo concentrandosi sugli elementi html e xml.
- La proprietà childNodes rileva come “figli del contenitore” tutti gli elementi: tag, elementi, testo, ecc. Come si può vedere in figura, l’array childNodes avrà 10 elementi: gli indici pari saranno gli elementi “testuali” e gli indici dispari i tag html. N.B. i blocchi evidenziati sono i figli “testuali”, quelli non evidenziati sono i figli “html”
- La proprietà children rileva come “figli del contenitore” solo i tag html. Quindi l’array children avrà solo 5 elementi: i tag contenuti nel div “parent_div”
Esempio. Supponiamo di voler assegnare a tutti i figli del div “parent_div” la classe “newclass”.
La proprietà children
// Otteniamo i figli diretti di "parent_div"
var div_children = document.getElementById("parent_div").children;
for(i=0;i<div_children.length;i++)
{
// Assegniamo la classe "newclass"
div_children[i].className="newclass";
}
ATTENZIONE. Ho riscontrato alcuni problemi legati a questa proprietà, ad esempio su Firefox. L’alternativa è proprio childNodes, da utilizzare con qualche piccolo accorgimento.
La proprietà childNodes e nodeType
Gli stessi risultati della proprietà children possono essere ottenuti con la proprietà childNodes, controllando il tipo di nodo che si sta leggendo grazie alla proprietà nodeType. Questa proprietà può avere 12 valori diversi (interi, da 1 a 12). La lista completa la puoi trovare qui: http://www.w3schools.com/Dom/dom_nodetype.asp . In particolare, poiché la proprietà children non considera i nodi testuali, controlliamo che nodeType non sia 3 (codice corrispondente ai nodi testuali).
// Otteniamo i figli diretti di "parent_div"
var div_children = document.getElementById("parent_div").childNodes;
for(i=0;i<div_children.length;i++)
{
// Controlliamo il tipo di nodo: noteType=3 se è un nodo testuale
if(div_children[i].nodeType==3) continue;
// Assegniamo la classe "newclass"
div_children[i].className="newclass";
}
Il risultato è identico al codice precedente in cui si utilizza la proprietà children.
Soluzione ibrida
// Selezione figli con jquery
var div_children = $("#parent_div").children();
// Ciclo for uguale al precedente
for(i=0;i<div_children.length;i++)
{
// Assegniamo la classe "newclass"
div_children[i].className="newclass";
}
N.B. la selezione con jquery ha lo stesso effetto della proprietà children, ovvero seleziona tutti i figli tranne quelli di tipo testuale.
Soluzione con jquery
// Selezione figli con jquery e assegnazione della classe "newclass"
$("#parent_div").children().addClass("newclass");
Con $("#parent_div").children() otteniamo i figli e con addClass("newclass") assegniamo la classe “newclass” ai figli trovati.
Ottenere il primo e l’ultimo nodo figlio con firstChild e lastChild
Normalmente per recuperare il primo o l’ultimo nodo figlio di un elemento si dovrebbero scrivere le seguenti righe di codice:
// Primo nodo figlio
var first_child = document.getElementById("parent_div").firstChild;
// Ultimo nodo figlio
var last_child = document.getElementById("parent_div").lastChild;
Problema. Come per la proprietà childNodes, anche queste leggono qualsiasi tipo di nodo. Quindi, se fra due elementi c’è un carattere di spaziatura o un tab, questo verrà considerato un nodo.
Soluzione. Con un semplice ciclo while possiamo leggere i successivi (o precedenti) nodi di tipo non testuale.
// Primo nodo figlio
var first_child = document.getElementById("parent_div").firstChild;
// Se il nodo letto è di tipo testuale leggiamo il successivo
while(first_child!=null && first_child.nodeType==3)
first_child = first_child.nextSibling;
// Ultimo nodo figlio
var last_child = document.getElementById("parent_div").lastChild;
// Se il nodo letto è di tipo testuale leggiamo il precedente
while(last_child!=null && last_child.nodeType==3)
last_child = last_child.previousSibling;
Osservazioni. Le condizioni del ciclo while controllano che il nodo sia stato trovato (cioé che sia diverso da null) e che il tipo sia testuale (cioé nodeType = 3). Se entrambe le condizioni sono verificate il nodo attuale viene rimpiazzato con il successivo (nel caso del primo nodo) o con il precedente (nel caso dell’ultimo nodo). Questo procedimento si ripete finché non ci sono più nodi figli o non viene trovato un nodo di tipo “non testuale”. La proprietà nextSibling permette di ottenere il nodo successivo; la proprietà previousSibling permette di ottenere il nodo precedente.
Soluzione ibrida con jquery
Poiché il metodo children() di jquery legge i nodi figlio di tipo “non testuale”.
Per ricavare il primo e l’ultimo nodo figlio si passa come argomento “:first” (per il primo nodo) e “:last” (per l’ultimo nodo).
// Primo nodo figlio
var first_child = $("#parent_div").children(":first");
// Ultimo nodo figlio
var last_child = $("#parent_div").children(":last");
Ottenere il genitore di un nodo con parentNode
Supponiamo di avere il seguente codice html:
<div id="parent_div">
<p id="child1">Paragrafo1</p>
<p id="child2">Paragrafo2</p>
<p id="child3">Paragrafo3</p>
</div>
Per ottenere il genitore dei tre paragrafi utilizzeremo la proprietà parentNode su uno dei tre paragrafi.
var parent_node1 = document.getElementById("child1").parentNode;
var parent_node2 = document.getElementById("child2").parentNode;
var parent_node3 = document.getElementById("child3").parentNode;
Le tre variabili (parent_node1, parent_node2, parent_node3) avranno lo stesso nodo: il div “parent_div”.
Soluzione con jquery
// parent() restituisce un array di un solo elemento
var my_node = $("#ciaociao").parent()[0];
Ottenere i “fratelli” dei nodi con nextSibling e previousSibling
Per ottenere il fratello di un nodo si possono utilizzare due funzioni:
- nextSibling, per leggere il fratello successivo
- previousSibling, per leggere il fratello precedente
Supponiamo di avere il seguente codice html:
<div id="parent_div">
<p id="child1">Paragrafo1</p>
<p id="child2">Paragrafo2</p>
<p id="child3">Paragrafo3</p>
</div>
Vogliamo ottenere il nodo corrispondente al paragrafo di id “child2″.
Problema. Anche queste proprietà selezionano i nodi di tipo “testuale”.
Soluzione. Inseriamo un ciclo while identico a quello scritto per le proprietà firstChild e lastChild.
// Ottenere il nodo "child2" partendo dal paragrafo "child1"
var node_child2_mod1 = document.getElementById("child1").nextSibling;
// Se il nodo letto è di tipo testuale leggiamo il successivo
while(node_child2_mod1!=null && node_child2_mod1.nodeType==3)
node_child2_mod1 = node_child2_mod1.nextSibling;
// Ottenere il nodo "child2" partendo dal paragrafo "child3"
var node_child2_mod2 = document.getElementById("child1").previousSibling;
// Se il nodo letto è di tipo testuale leggiamo il precedente
while(node_child2_mod2!=null && node_child2_mod2.nodeType==3)
node_child2_mod2 = node_child2_mod2.previousSibling;
Soluzione con jquery
Fortunatamente i metodi next() e prev() di jquery selezionano solo i nodi “non testuali” e restituiscono un set di nodi costituito di fatto da un solo nodo.
// Ottenere il nodo "child2" partendo dal paragrafo "child1"
var node_child2_mod1 = $("#child1").next()[0];
// Ottenere il nodo "child2" partendo dal paragrafo "child3"
var node_child2_mod2 = $("#child3").next()[0];
Attraversare i “fratelli” dei nodi con nextSibling e previousSibling
Supponiamo di voler modificare tutti i fratelli di un certo nodo. Per fare ciò è necessario scorrere tutti i nodi “fratelli” modificandoli in base alle nostre esigenze. In realtà l’attraversamento dei nodi fratelli l’abbiamo già fatto in occasione dei cicli while utilizzati per risolvere il problema della selezione dei nodi testuali.
Utiliziamo il codice html dell’esempio precedente.
// Otteniamo il nodo corrispondente al paragrafo "child1"
var node_child1 = document.getElementById("child1");
// Scorriamo tutti i fratelli di node_child1
while(node_child1!=null && node_child1.nodeType!=3)
{
// Leggiamo il fratello di node_child1 e lo inseriamo nella stessa variabile
node_child1 = node_child1.nextSibling;
// Esempio: applichiamo la classe "newclass"
node_child1.className="newclass";
}
Conclusioni
Queste sono alcune delle principali tecniche per selezionare e scorrere gli elementi html con il codice javascript “puro” e utilizzando il famoso framework jquery che, come avrai potuto notare, semplifica la scrittura di codice javascript e ne riduce drasticamente la lunghezza.
In queste situazioni preferisci utilizzare javascript puro o jquery? Hai avuto problemi durante l’utilizzo di queste tecniche? Si possono migliorare in qualche modo?
Crediti
- ImpressiveWebs – 10 Essential DOM Methods & Techniques for Practical JavaScript
- Libro. Javascript – La guida di Danny Goodman, Michael Morrison (McGraw-Hill)
- Foto1: Catch me if you can! di Josh n Jerry
- Foto2: http://www.freerepublic.com/focus/f-chat/2096935/posts
- Foto3: Node di dixit M
- Foto5: Climbing di docman


