Donnerstag, Oktober 26, 2006

JavaScript DOM - einfach mit "graft()"

Ich habe im verlinkten Artikel eine sehr nützliche Funktion gefunden um DOM-Elemente aus simplem JSON-Pseudo-HTML Code erstellen zu lassen - graft(). Damit kann man sich endlich diese nervigen Code-Wüsten sparen, die nötig sind um selbst die simpelsten HTML Elemente per JavaScript zu generieren.
Hier ein kleines Beispiel:

var tbl = document.createElement('table');
tbl.setAttribute("border",1);
tbl.setAttribute("cellpadding",0);
tbl.setAttribute("cellspacing",0);
var tr = document.createElement("tr");

var td = document.createElement("td");
td.setAttribute('valign','bottom');
td.appendChild(document.createTextNode("Graft()"));
tr.appendChild(td);

td = document.createElement("td");
td.appendChild(document.createTextNode("makes"));
tr.appendChild(td);

td = document.createElement("td");
td.appendChild(document.createTextNode("this"));
tr.appendChild(td);

td = document.createElement("td");
td.setAttribute('style','background-color:#ff0;');
td.appendChild(document.createTextNode("easier"));
em = document.createElement("em");
em.setAttribute('style','font-size:32px;');
em.appendChild(document.createTextNode(" to do.."));
td.appendChild(em);
tr.appendChild(td);

tbl.appendChild(tr);

document.getElementById("mycontentdiv").appendChild(tbl);

Alles das ist nötig um eine simple Tabelle mit einer einzigen Zeile zu erstellen. Nicht nur das es eine Menge Code ist, darüberhinaus ist es auch noch sehr unübersichtlich.
Um nun das gleiche mit der graft() Funktion zu erledigen muss man nur folgendes tun:


graft(document.getElementsByTagName("BODY")[0],
["table",
{border:1,cellpadding:0,cellspacing:0},
['tbody',
['tr',
['td',
{valign:'bottom'},
"Graft()"
],
['td',
"makes"
],
['td',
"this"
],
['td',
{style:{backgroundColor:'#ff0'}},
"easier",
["em",
{style:{fontSize:'32px'}},
" to do.."
]
]
]
]
]
);
Ist doch wesentlich übersichtlicher, und rein optisch auch näher an HTML.

Achja und hier ist übrigens die Funktion die das alles bewerkstelligt:


// from prototype.js - needed for extending the style object
Object.extend = function(destination, source) {
for (property in source) {
destination[property] = source[property];
}
return destination;
}

function graft(parent, t, doc) {

// Usage: graft( somenode, [ "I like ", ['em',
// { 'class':"stuff" },"stuff"], " oboy!"] )

doc = (doc || parent.ownerDocument || document);
var e;

if(t == undefined) {
throw console.log( "Can't graft an undefined value");
} else if(t.constructor == String) {
e = doc.createTextNode( t );
} else if(t.length == 0) {
e = doc.createElement( "span" );
e.setAttribute( "class", "fromEmptyLOL" );
} else {
for(var i = 0; i < t.length; i++) {
if( i == 0 && t[i].constructor == String ) {
var snared;
snared = t[i].match( /^([a-z][a-z0-9]*)\.([^\s\.]+)$/i );
if( snared ) {
e = doc.createElement( snared[1] );
e.setAttribute( 'class', snared[2] );
continue;
}
snared = t[i].match( /^([a-z][a-z0-9]*)$/i );
if( snared ) {
e = doc.createElement( snared[1] ); // but no class
continue;
}
// Otherwise:
e = doc.createElement( "span" );
e.setAttribute( "class", "namelessFromLOL" );
}
if( t[i] == undefined ) {
throw console.log("Can't graft an undefined value in a list!");
} else if( t[i].constructor == String || t[i].constructor == Array ) {
graft( e, t[i], doc );
} else if( t[i].constructor == Number ) {
graft( e, t[i].toString(), doc );
} else if( t[i].constructor == Object ) {
// hash's properties => element's attributes
for(var k in t[i]) {
// support for attaching closures to DOM objects
if(typeof(t[i][k])=='function'){
e[k] = t[i][k];
} else {
if (k == "class") {
e.className = t[i][k];
} else if (k == "style") {
Object.extend(e.style, t[i][k]);
} else {
e.setAttribute( k, t[i][k] );
}
}
}
} else {
throw console.log( "Object " + t[i] + " is inscrutable as an graft arglet." );
}
}
}
parent.appendChild( e );
return e; // return the topmost created node
}
Ich habe im Vergleich zur Urversion noch zwei kleine Fixes eingebaut bzgl. der "class" und "style" Attribute.

Viel Spaß damit!