CreateDOM - $C() - Create DOM Stuctures Easily
$C() is a function that provides a much easier alternative to W3C DOM's createElement function. You can create DOM sturctures very easily with it. Everyone who have tried their hand at creating a DOM structure using the W3C method knows that it is not easy. It is a piece of cake using innerHTML. But when we try it using W3C methods like document.createElement, we will find that it takes ten times more time. So I have created a small function(>50 lines) to do this in a much easier way.
innerHTML
Disadvantages
- It is not standard. It's a property that IE introduced that the other browsers also used.
- innerHTML is just a string. DOM is not a string, it's a hierarchal structure. The two are not supposed to mix.
- One has to escape all the '"' characters in the string - after all the escaping the string becomes rather ugly - and long. Add a lot of plus(+) signs(for string concatenation) to this and we get a unmaintainable mess.
Advantages
- Much simpler and easier than the W3C DOM Model.
- Can be used to great effect with AHAH - this can't be done using the W3C model.
- Faster than the W3C DOM Model
I will never say that you should not use innerHTML(even PPK says that using innerHTML is OK). But if you want to avoid using it, this script will make it easier.
Code
function $C(dom,id) {
if(!id) var id = "";//The ID argument is optional
//Most *necessary* HTML tags - make sure not to include any tags that is also a valid attribute name - eg 'cite'
var valid_tags = ",p,div,span,strong,em,u,img,pre,code,br,hr,a,script,link,table,tr,td,h1,h2,h3,h4,h5,h6,sup,sub,"+
"ul,ol,li,dd,dl,dt,form,input,textarea,legend,label,fieldset,select,option,blockquote,";//Begining and ending commas are intentional - don't remove them
var html = new Array();
var non_alapha = new RegExp(/_\d*$/);
for(var tag in dom) {
var child = false;
if(isNaN(tag)) { //Associative array
var attributes = dom[tag];
} else { //It's a list
var tagname = "";
var attributes = "";
for(var tagname in dom[tag]) {
attributes = dom[tag][tagname];
}
tag = tagname;
}
tag = tag.replace(non_alapha,"");//Remove the numbers at the end
var ele = document.createElement(tag);
//If the given attribute is a string, it is a text node
if(typeof(attributes) == "string") child = document.createTextNode(attributes);
else if(attributes) {//If it an array...
for(var att in attributes) {
var value = "";
if(isNaN(att)) { //Associative array
value = attributes[att];
} else { //It's a list
for(var index in attributes[att]) {
value = attributes[att][index];
}
att = index;
}
att = att.replace(non_alapha,"");//Remove the numbers at the end - to solve the problem of non unique indexes
if(valid_tags.indexOf(","+att+",") != -1) { //If the attribute is a valid tag,
//Find the dom sturcture of that tag.
var node = new Object;
node[att] = value;
ele.appendChild($C(node,""));// :RECURSION:
}
else if(att == "text") child = document.createTextNode(value);//The text in the tag
//else ele.setAttribute(att,value);
}
}
if(child && attributes) ele.appendChild(child);//Append the child if it exists
html.push(ele);
}
if(!id) {//If no node is given, return the created node.(Exits the function)
if(html.length == 1) return html[0];
return html;
}
//If a node/id was given, append the created elements to that element.
var node = id;
if(typeof id == "string") node = document.getElementById(id);//If the given argument is an id.
for(var i=0;el=html[i],i<html.length;i++) node.appendChild(el);
}
Sample
$C({
div:{
'class':'newly-added',//Class must be quoted - its a keyword
code:'This is a code',
br:'',
a:{
href:'http://www.bin-co.com/',
text:'This is a link'
},
hr:'',
span:{
'class':'finally',
text:'Thats all folks.'
}
}
},"insert-here");
This code will create something like....
<div class="newly-added">
<code>This is a code</code>
<br />
<a href="http://www.bin-co.com/">This is a link</a>
<hr />
<span class="finally">Thats all folks.</span>
</div>
Problems
This is a new script - so I have not much problems with it. But I found one - and it is a biggie. You can't use the same tag twice inside an element. I am using a associative array, so the index will be overwritten. This is very troubling if you want to create a list. I have solved this problem - use a list(numerical array in such cases). An example is given below...
Problem Code
$C({ul:{
li:"First Item",
li:"Second Item",
li:"Third Item",
}},"insert-here");
Solution
$C({ul:[
{li:"First Item"},
{li:"Second Item"},
{li:"Third Item"},
]},"insert-here");
Thanks to all the readers who suggested this idea.

Comments
/[^a-zA-Z0-9].*$/
Kodeci.
/_\d*$/.This makes the use of '_' mandatory if you want to use the hack - like this...
{ label$firstname: { 'for' : 'firstname'}, input$firstname : { id: 'firstname', name:'firstname', value: 'Kodeci'}}or more useful, if the seperator meant someting... like $ sets the id property, _ sets the className.
(ignore me still) $C is a beautiful function i just wish it could update like domEL
/[_$#.].*$/
> if the seperator meant someting... like $ sets the id property, _ sets the className.
Or maybe # means id and '.' means class(like in CSS). Very cool concept. Give me some time and I will implement it.
{li: 'Second item'}....
I think thats a better solution than matching _\d*
Thanks for all the suggestions.
seems a nice function, bu there on firefox the attribute setting doesn't work :S.
Lookin @ the code the "attributes[att]" makes me think this is a function just for IE...
The lack of being able to add a function was driving me crazy!
Otherwise, I love your code.
You might want to add this snippet into your code
else if(typeof(value) == "function") {
var type = att.replace(/^on(.+)$/,"$1");
if (ele.addEventListener)
ele.addEventListener( type, value, false );
else if (ele.attachEvent) {
ele["e"+type+value] = value;
ele[type+value] = function() { ele["e"+type+value]( window.event ); }
ele.attachEvent( "on"+type, ele[type+value] );
}
}
that coexists with mootools
I may use parts of moo
so I should not rewrite code that already exists
Its fine Object.prototype+moo
Object.Implement({
f:function(){}//Implement similar implement moo
})
my english its poor sorry
the Scriptaculous Builder object does a very similar thing.
Just a second possibility.
It's been a while since I made changes to your code, Binny... and I can't recall all the deltas needed for implementing it for SVG. But it was fairly straightforward. (It's at the tail end of raintraipsing.com/laplaya/rt_svg_manip.js)
Thanks for the use of your code. Feel free, of course, to make use of any positive modifications we may have implemented. Any & all suggestions/comments are appreciated!
-- Gwen
/* A create element helper that can add id, className and innerHTML directly. License: Public domain -- feel free to personalise! :)
* @param name TagName + '#' + id + '.' + className + '.' + className ...
* @param html innerHTML to be added on the new element (or null to avoid using innerHTML)
* @return new Element
* Example: var link2 = dce('a#link2', 'linktext 2');
*/
function dce(name,html) {
var arr = name.replace(/([$_.#])/g,',$1').split(/,/);
if (!arr.length) return document.createElement(name);
var elt = document.createElement(arr[0]);
for (var i=1; i<arr.length; i++) {
var x=arr[i];
if (x.match(/^[$#]/)) elt.id = x.substring(1,x.length);
else if (x.match(/^[_.]/)) elt.addClass( x.substring(1,x.length) );
}
if (html) elt.innerHTML = html;
return elt;
}
a, strong, em, b, i, code, pre, pandbrallowed. Other tags will be shown as code(< will become <). Urls, Line breaks will be auto-formated.