La programmazione del DOM è una tecnica molto potente che può generare in modo dinamico nuovi contenuti lato client. Nello specifico di questo articolo ci soffermeremo innanzitutto su un caso particolare: la generazione dinamica di tabelle HTML. Infatti, nonostante la versatilità resa possibile dalle varie tecniche di manipolazione del DOM, è da notare che la generazione dinamica di tali elementi potrebbe avere un incremento di prestazioni qualora venisse implementata con oggetti nativi Javascript utilizzati al posto delle API del DOM.
Quanto tratteremo qui di seguito si pone quindi come obbiettivo quello di mettere in luce le eventuali differenze in termini di prestazioni durante la generazione di una tabella HTML utilizzando i due metodi cui si accennava. Descriverò poi una tecnica che permette di modificare dinamicamente gli stili CSS migliorando le prestazioni di applicazioni Javascript preposte alla modifica delle modalità di visualizzazione di un elemento HTML (proprietà style
).
Generazione di una tabella HTML
La minimizzazione dell'accesso al DOM può portare un significativo incremento di prestazioni e di velocità nella generazione di una tabella HTML.
Nell'esempio viene mostrato il codice per la generazione di una tabella HTML utilizzando prima Javascript e poi le API del DOM. Ecco il codice della parte che ci interessa più direttamente:
<script type="text/javascript">
function crea_tabella(righe,colonne,larghezza,altezza,caso){
switch(caso)
{
case("NO_DOM"):
//tempo stimato
var d = new Date();
var time_start=d.getMilliseconds();
//preparo la mia variabile che conterrà la tabella
var table="<table width='"+ larghezza +"' eight='"+ altezza +"' cellpadding='3' cellspacing='0' border='1'>";
// ciclo sul numero di righe passate alla funzione per comporre la tabella
for(var i=1;i<=righe;i++)
{
table+="<tr>";
// ciclo sul numero di colonne passate alla funzione per comporre la tabella
for(var j=1;j<=colonne;j++)
{
table+="<td style='font-size:10px;font-weight:700;font-family:Verdana;' nowrap>riga n°"+ i +" <br>colonna n°"+ j +"</td>";
}
table+="</tr>";
}
table+="</table>";
//una volta termita la creazione delle mia tabella accedo al dom per scriverla
document.getElementById('tab1').innerHTML=table;
var d_f = new Date();
var time_end=d_f.getMilliseconds();
var tempo_stimato=(time_end-time_start);
alert('Tempo esecuzione Javascript: ' + tempo_stimato + ' ms');
tempo_stimato=null;
time_start=null;
time_end=null;
break;
case("SI_DOM"):
//tempo stimato
var dom_d = new Date();
var time_start=dom_d.getMilliseconds();
var crea_table=document.createElement("TABLE");
//Inizializzo gli attributi per la tabella
crea_table.setAttribute('width',larghezza);
crea_table.setAttribute('height',altezza);
crea_table.setAttribute('cellpadding','3');
crea_table.setAttribute('cellspacing','0');
crea_table.setAttribute('border','1');
var crea_tbody=document.createElement("TBODY");
// ciclo sul numero di righe passate alla funzione per comporre la tabella
for(var i=1;i<=righe;i++)
{
crea_tr=document.createElement("TR");
// ciclo sul numero di colonne passate alla funzione per comporre la tabella
for(var j=1;j<=colonne;j++)
{
crea_td=document.createElement("TD");
crea_td.setAttribute("nowrap","nowrap");
crea_td.innerHTML="riga n°"+ i +" <br>colonna n°"+ j +"";
//aggiungo lo stile
crea_td.style.fontSize="10px";
crea_td.style.fontWeight=700;
crea_td.style.fontFamily="Verdana";
crea_tr.appendChild(crea_td);
}
crea_tbody.appendChild(crea_tr);
}
crea_table.appendChild(crea_tbody);
document.getElementById('tab2').appendChild(crea_table);
var dom_d_f = new Date();
var time_end=dom_d_f.getMilliseconds();
var tempo_stimato=(time_end-time_start);
alert('Tempo esecuzione DOM: ' + tempo_stimato + ' ms');
tempo_stimato=null;
time_start=null;
time_end=null;
break;
}
}
</script>
</head>
<body onLoad="crea_tabella('10','10','500','300','NO_DOM');crea_tabella('10','10','500','300','SI_DOM');">
Vediamo insieme alcuni passaggi chiave all'interno del codice.
In entrambi i casi di switch
, viene preparato attraverso il metodo getMilliseconds()
della classe Date
, una sorta di cronometro, tramite il quale verrà calcolato il tempo di esecuzione dei due casi.
Nel caso di 'NO_DOM
' viene predisposta una variabile stringa di nome 'table
', che sarà ad ogni ciclo di for
concatenata ad altre variabili che costruiranno la tabella, in base al numero di 'righe' e 'colonne' passate alla mia funzione. Una volta terminato tutto. verrà scritto il contenuto della variabile 'table
' nel livello 'tab1
', utilizzando il metodo innerHTML
.
Nel caso 'SI_DOM' gli elementi e gli attributi che compongono la tabella vengono creati usando il metodo createElement(tag elemento da creare)
per gli elementi e il metodo elemento.setAttribute(nome attributo,valore attributo)
per gli attributi degli elementi creati. Verrà quindi costruita una struttura ad albero, in cui l'elemento padre sarà <table>
, che conterrà un elemento figlio <tbody>
, che a sua volta sarà padre di <tr>
e cosi via, fino ad arrivare all'ultimo figlio.
A questo punto si deve ripercorrere l'albero in modo ricorsivo e per fare ciò viene usato il metodo appendChild('')
nel seguente modo:
elementoPadre.appendChild('elementoFiglio')
Terminata la costruzione della tabella, verrà scritta nel mio livello che ha come id='tab2'
.
La versione che utilizza stringhe Javascript viene eseguita dal browser più velocemente rispetto a quella che utilizza le API del DOM: tabella 10x10 esempio 1 16ms; esempio 2 : 47ms). Come si può notare, nel codice del primo esempio viene predisposta un variabile che conterrà il codice HTML di creazione della tabella e, così facendo, si accede al DOM per la scrittura un'unica volta, cioè solo quando la variabile conterrà tutto il codice che genera la tabella desiderata.
Nel secondo caso invece si accede al DOM ogni qual volta venga aggiungo un elemento della tabella. In questo modo il browser rimane impegnato più a lungo.
Il caso 'NO_DOM' a mio parere si potrebbe ulteriormente ottimizzare incrementando la velocità di elaborazione del codice. Per far ciò basterebbe memorizzare i vari elementi che compongono la tabella in un array e infine eseguire una join("")
per concatenare tutti gli elementi dell'array in un'unica stringa solo al momento della scrittura all'interno del div. Questo farebbe risparmiare parecchi cicli di CPU necessari per concatenare più stringhe tra loro.
Infine, una cosa molto interessante sono le discrepanze di velocità nella creazione di strutture DOM con browser diversi. Infatti, sembra che il motore di rendering del DOM di Firefox sia decisamente più veloce rispetto al suo rivale IE7 (Firefox tabella 10x10: 47ms; IE7 tabella 10x10: 81ms ).
Modifica di proprietà style
Esistono situazioni dove non è possibile concatenare più stringhe per costruire codice HTML, ad esempio quando è necessario creare o modificare il contenuto di un elemento.
Nell'esempio che segue vengono mostrate due differenti tecniche per la modifica dinamica del contenuto di un elemento HTML. Ancora una volta presentiamo la parte di codice che ci interessa:
<script type='text/Javascript'>
function stile_dom(id_elemento,caso)
{
switch(caso)
{
case('DOM'):
var elemento=document.getElementById('p_id');
elemento.style.fontSize="15px";
elemento.style.family="Verdana";
elemento.style.fontWeight="bold";
elemento.style.color="#006699";
elemento.innerHTML="Contenuto generato tramite accessi multipli al DOM";
break; case('TEXT'):
var elemento=document.getElementById('p_id');
with(elemento)
{
style.cssText="font-size:15px;font-family:Verdana;font-weight:bold;color:#006699";
elemento.innerHTML="Contenuto generato tramite un accesso al DOM";
}
break;
}
}
</script>
</head>
<body>
<p id='p_id'></p>
<input type='button' name='button_1' value='Multi DOM' onClick="stile_dom('p_id','DOM');">
<input type='button' name='button_2' value='One DOM' onClick="stile_dom('p_id','TEXT');">
Nel primo esempio la funzione cambia la formattazione di un elemento accedendo ai suoi singoli attributi tramite la proprietà style
; la seconda versione invece minimizza il numero delle chiamate alle API del DOM riducendo le referenze alla proprietà style
.
Questo tipo di proprietà style.cssText
permette al codice di modificare tutte le proprietà di ciascun attributo usando i fogli di stile in una volta sola.
Anche se molte fonti raccomandano di non utilizzare la proprietà Javascript With
, un suo corretto utilizzo potrebbe migliorare leggermente le prestazioni e facilitare la manutenzione di script complessi.
Concludendo, strategie di questo tipo hanno, a mio avviso, la prerogativa di migliorare le prestazioni di script complessi che prevedono un impiego sostenuto del DOM lato client. La logica conseguenza è un minor utilizzo di risorse e una risposta del browser nettamente più veloce.