8 giugno 2011

javascript manipolare DOM, codice browser compatibile

Indice dei tutorials: http://gheryd.blogspot.com/2011/06/javascript-gwt-tutorials.html

Indice
  • Manipolazione di base
  • Ottimizzare le performances
  • Operazione con gli attributi
  • Aggiungere html
  • Gestire eventi
  • Gestire le classi e le proprietà di stile
  • Mostrare/nascondere un elemento
  • Select
  • CheckBox
  • Caricare uno script
  • Caricare un css
  • Templates


Introduzione
In javascript, ogni elemento grafico inserito nella pagina HTML è rappresentato da un oggetto accessibile tramite l'oggetto "document".
L'oggetto "document" è una proprietà dell'oggetto "window" e viene popolato con una struttura ad albero durante il caricamento della pagina.
La struttura che viene così creata è il DOM, document object model, cioè una rappresentazione a oggetti di tutti i componenti della pagina HTML.
Se conoscete XML, vi saranno familiari i comandi in questo tutorial per manipolare questi oggetti che di base sono chiamati nodi (node). Ogni nodo può contenere dei nodi figli (child nodes).
L'oggetto "element" è un particolare tipo di nodo che rappresenta un tag html.
Esistono altri tipi di nodi, come per esempio gli attributi dell'elemento, oppure i commenti.
Anche il testo inserito all'interno di una tag sarà rappresentato nel DOM come nodo figlio.
Esistono delle "scorciatoie" che consentono di cercare elementi esistenti nella pagina, il più usato è sicuramente:
var el = document.getElementById("myId");
Il metodo restituisce un oggetto element che ha come attributo id = "myId".
Con javascript  possiamo modificare gli elementi esistenti cambiandone l'aspetto grafico per esempio, oppure crearne di nuovi e farli apparire dove ci è necessario come per esempio aggiungere una riga ad una tabella o un elemento in una lista.

Manipolazione di base
Creare un elemento:
var el = document.createElement("div");
Creare un elemento di testo:
var txt = document.createTextNode("Hello world!");
Ottenere un elemento dal suo id:
var el = document.getElementById("myId");
se l'elemento non è nella pagina el sarà nullo, verificare prima di accedere alle proprietà dell'elemento:
if( el ) {
    alert(" element exists" );
}
Nota: per accedere ad elementi gia esistenti, dovrete attendere la fine del caricamento della pagina come nell'esempio seguente:
<html>
<head>
<script type="text/javascript">
function init() {
    var el = document.getElementById("myDiv");
    ....
}
</script>
</head>
<body onload="init()">
<div id="myDiv">....</div>
</body>
</html>
Ottenere elementi in base al tag:
var els = document.getElementsByTagName("input");
for(var i=0; i<els.length; i++) {
   var el = els[i];
 ...
}
Ottenere il nome del tag
var tagname = el.tagName;
Aggiungere un elemento alla fine:
elParent.appendChild(elChild);
Inserire un elemento:
elParent.insertBefore(newEl, elChild);
Rimuovere un elemento:
elParent.removeChild(elChild);
Sostituire un elemento:
elParent.replaceChild(newChild, oldChild);
Inserire un elemento all'inizio:
elParent.insertBefore(elChild, elParent.childNodes[0]);
Ottenere la lista (array) degli elementi figli:
var nodes= elParent.childNodes;
Ottenere il primo figlio:
var node = elParent.firstChild;
Ottenere l'ultimo figlio:
var node = elParent.lastChild;
Ottenere il fratello successivo:
var node = el.nextSibling;
Ottenere il fratello precendente:
var node = el.previousSibling;
Sapere il tipo di nodo:
var type = node.nodeType;
il valore restituito è un numero, di solito interessa verificare se il tipo è relativo ad un element node o un text node come nell'esempio seguente:
if( type  == 1) {
   alert("is element");
   // node.nodeName = "DIV" //=tagName
   var el = node;
}else if(type ==3) {
   alert("is text");
   // node.nodeName = "#text"
   var text = node.nodeValue;
}
Cercare il primo elemento:
var firstEl = null;
var children = parentEl.childNodes;
for(var i=0; i<children.length; i++) {
   var node = children[i];
   if(node.nodeType==1) {
       firstEl = node;
       break;
   }
}
in modo analogo per cercare l'ultimo elemento:
var lastEl = null;
var children = parentEl.childNodes;
for(var i=children.length; i>=0; i--) { 
   var node = children[i];
   if(node.nodeType==1) {
       lastEl = node;
       break;
   }
}

Ottenere l'elemento padre:
var parentEl = el.parentNode


Ottimizzare le performances
Se dobbiamo aggiungere molti elementi figli come dell'esempio seguente:
var container = document.getElementById("idContainer");
for(var i=0; i<100; i++) {
    var child = document.createElement("p");
    child.appendChild( document.createTextNode("paragraph "+i) );
    container.appendChild(child);
}
per migliorare le performances in alternativa è possibile creare un frammento:
var  docFrag= document.createDocumentFragment();
for(var i=0; i<100; i++){
    var child = document.createElement("p");
    child.appendChild( document.createTextNode("paragraph "+i) );
    docFrag.appendChild(child);
}
container.appendChild(docFrag); 
è equivalente all'esempio precedente, ma molto più veloce perchè tutti gli elementi sono aggiunti alla pagina alla fine con un'unica istruzione.

In generale, è sempre meglio aggiungere elementi alla pagine verso la fine.
Per esempio, nel caso di creazione di una tabella:
var table = document.createElement("table");
container.appendChild(table);
var tbody = document.createElement("tbody");
table.appendChild(tbody);
for(var r=0;r<100; r++) {
    var tr = document.createElement("tr");
    tbody.appendChild(tr);
    for(var c=0;c<10;c++){
       var td = document.createElement("td");
       tr.appendChild(td);
       td.appendChild(document.createTextNode("cell "+r+","+c));
   }
}
aggiungendo la tabella subito nella pagina prima di popolarla di righe e colonne, rallenta parecchio l'esecuzione perchè costringe il browser a renderizzare all'istante la cella ad ogni ciclo del loop di popolamento.

Invece spostando alla fine l'istruzione evidenziata miglioriamo le prestazioni perchè la tabella è gia completa del suo contenuto, e la renderizzazione avviene in una sola volta.
var table = document.createElement("table");
var tbody = document.createElement("tbody");
table.appendChild(tbody);
for(var r=0;r<100; r++) {
    var tr = document.createElement("tr");
    tbody.appendChild(tr);
    for(var c=0;c<10;c++){
       var td = document.createElement("td");
       tr.appendChild(td);
       td.appendChild(document.createTextNode("cell "+r+","+c));
   }
}
container.appendChild(table);





Operazione con gli attributi
Settare il valore di un attributo di un elemento:
el.setAttribute("id", "myId");
oppure
el.id = "myId";
Ottenere il valore di un attributo
var id = el.getAttribute("id");
oppure
var id = el.id;
Rimuovere un attributo
el.removeAttribute("id");
oppure
el.id = null;
Ottenere la lista degli attributi:
var attrs = el.attributes,
var s = "";
for(var i=0; i<attrs.length; i++) {
    var attr = attrs[i];
    var name = attr.nodeName;
    var value = attr.nodeValue;
....
}
Può essere interessante aggiungere un attributo "custom" come nell'esempio seguente:
var textbox = document.getElementById("myInput");
textbox.valueDefault = textbox.value;
in questa istruzione ho aggiunto la proprietà "valueDefault" che non esiste normalmente; questa istruzione potrebbe essere eseguita sull'onload della pagina; sull'onclick di un bottone per  ripristinare il valore iniziale della textbox basta utilizzare la seguente istruzione:
textbox.value = textbox.valueDefault;


Aggiungere html
Settare html:
el.innerHTML = "<a href='#'>my link</a>"
tutti gli eventuali figli verranno rimossi.
Aggiungere html:
el.innerHTML += "<a href='#'>my link</a>"
Inserire html all'inizio:
el.innerHTML = "<a href='#'>my link</a>"+el.innerHTML;
A mio giudizio è meglio evitare di utilizzare innerHTML se non per aggiungere un semplice testo formattato:
el.innerHTML = "<b>title</b><br/><i>italic</i><br/>etc....";
Per i casi  come quello dell'esempio precedente del link preferisco operare nel modo seguente:
var link = document.createElement("a");
link.href = "#";
link.appendChild( document.createTextNode("my link") );
el.appendChild(link);
La proprietà innerHTML è utile per svuotare in maniera semplice un elemento:
el,innerHTML = "";
Importante: non aggiungete nel innerHTML attributi di eventi perchè non funzionano su ie:
el.innerHTML = "<a href='#' onclick='alert(clicked);'>my link</a>";
occorre crearsi l'elemento come dell'esempio precedente a aggiungere l'handler così:
link.onclick = function(event){ alert("clicked") }


Gestire eventi
Registrare un handler di un evento:
div.onclick = function(event){ alert("clicked") }

E' possibile specificare delle istruzioni direttamente dentro il codice HTML:
<input type="checkbox" name="myCheck" onClick="alert('checked: '+this.checked)" />
Il termine this si riferisce all'elemento.

Per maggiori dettagli sulla gestione degli eventi vedere il seguente post:
http://gheryd.blogspot.com/2011/05/javascript-funzioni-compatibili-per-gli.html


Gestire le classi e le proprietà di stile
Settare una classe di stile:
el.className = "myClass";
Aggiungere una classe di stile:
el.className += " "+"myClass2";
Sostituire una classe di stile con un'altra:
el.className = el.className.replace("oldClass", "newClass");
Rimuovere una classe di stile:
el.className = el.className.replace("myClass", "");
Verificare se ha una classe di stile:
var hasMyClass = el.className.indexOf("myClass")>-1;
if( hasMyClass  ) {
    alert("has class");
}
Ottenere un'array di classi di stile
var classes = el.className.split(" ");
for(var i=0; i<classes.length; i++) {
    var class = classes[i];
...
}
Settare proprietà di stile:
el.style.borderColor = "red";
el.style.fontSize = "10px";
el.style.textAlign = "right";
el.style.backgroundColor = "#faa";
el.style.border = "1px solid #000";
in generale la proprietà in javascript è uguale alla proprietà css senza trattini:
border-color -> el.style.borderColor
font-size -> el.style.fontSize
text-align -> el.style.textAlign
background-color -> el.style.backgroundColor

Cercare tutti gli elementi con una certa classe di stile
function getElementsByClass(classname, elContainer) {
if(!classname) return null;
 elContainer = elContainer || document.body;
var els = [];
 var findElClass = function(el) {
    var nodes = el.childNodes;
for(var i=0; i<nodes.length; i++) {
var node = nodes[i];
if(node.nodeType!=1) continue;
if(node.className.indexOf(classname)>-1) els.push(node);
findElClass(node);
}
}
findElClass(elContainer);
return els;
}


Mostrare/nascondere un elemento
Mostrare:
document.getElementById("myElement").style.display="";
Nascondere:
document.getElementById("myElement").style.display="none";


Select
var sel = document.getElementById("mySelect");
Ripuliamo la select dalle option esistenti:
sel.options.length = 0;
Popoliamo la select con un option
var opt = new Option("", "value");
try {
    sel.add(opt, null);
}catch(ex){
    sel.add(opt);
}
opt.innerHTML = "text";
l'ultima istuzione ci consente di usare html entities come per esempio "&egrave;" E' importante che l'assegnazione della  proprietà innerHTML sia successiva all'instruzione di inserimento della option all'interno della select.


CheckBox
Recuperarne il riferimento DOM:
var cb = document.getElementById("myCheckbox");
Se è in un form si può ottenere in base al suo attributo name:
var cb = document.getElementById("myForm").myCheckbox;
<form id="myForm">
    <input id="myCheckbox" name="myCheckbox" />
</form>
Modificarne lo stato:
cb.checked = true;
Verificare lo stato:
if(cb.checked) {
    alert("is checked");
}
Intercettarne il cambio di stato:
cb.onclick = function() {
     //TODO handle event
}


Caricare uno script
var script= document.createElement('script');
script.src = '/js/myscript.js';
script.type = 'text/javascript';
script.async = 'true'
var s = document.getElementsByTagName('script')[0];
s.parentNode.insertBefore(script, s);


Caricare un css
var css= document.createElement('link');
css.href = '/css/mycss.css';
css.rel= 'stylesheet';
css.type = 'text/css'
css.media = 'all';
document.getElementsByTagName("head")[0].appendChild(css);


Templates
Possiamo utilizzare una porzione di HTML adibita a template per creare altri elementi.
<div style="display:none">
<div id="template1">
<div id="#id#">
<span>#text#<span>
<div>
</div>
<div id="template2">
<div style="border:1px solid #CCC;background-color:#AAA">#block#</div>
</div>
</div>
<div id="container"></div>
Il codice HTML che utilizzeremo come template non sarà visibile in quanto è inserito in un div nascosto.
Con il seguente codice javascript di esempio aggiungiamo ad un div contenitore nuovi elementi basati sui nostri template:
var tpl1 = document.getElementById("template1").innerHTML;
var tpl2 = document.getElementById("template2").innerHTML;
var container = document.getElementById("container");
for(var i=0; i<10;i++) {
var block = block.replace("#text#", "item text "+i).replace("#id#", "item"+i);
var block2 = tpl2.replace("#block#", block);
container.innerHTML += block2;
}
La tecnica quindi consiste nel prendere il contenuto html di un template. Si sostituiscono le eventuali "chiavi" che abbiamo definito all'interno dei template con il testo che si vuole visualizzare e infine si aggiunge il codice html che si è preparato all'interno del div contenitore.
Il vantaggio di usare questo sistema è il fatto di non dover creare elementi grafici all'interno di codice javascript. La porzione di html che utilizziamo come template può essere modificata successivamente senza dover metter mano al codice javascript.

Nessun commento:

Posta un commento