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.

See Demo

innerHTML

Disadvantages

Advantages

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.

License

BSD License

References

$C equivalents

Comments

Anonymous at 16 Nov, 2006 05:54
instead of non-alpha replacement (which kills heading tags), i suggest you use one non_alphanumeric symbol, followed by any symbol...

/[^a-zA-Z0-9].*$/

Kodeci.
Reply to this.
Binny V A at 17 Nov, 2006 06:12
You are right - I had completely forgotten about the heading tags. However I cannot just remove the numbers - they are essential for the multiple tags hack. So I changed the regexp to /_\d*$/.

This makes the use of '_' mandatory if you want to use the hack - like this...

{
li_1:"One",
li_2:"Two",
li_3:"Three",
}
Reply to this.
Kodeci at 17 Nov, 2006 10:25
why limit the seperator to '_' and suffix to numbers for those greedy control freaks (me), who love to see '$' or might find naming the variables useful...

{ 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

Reply to this.
Binny V A at 31 Dec, 1969 03:59
No problem - just use another regexp for that - something like this...
/[_$#.].*$/

> 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.
Reply to this.
Anonymous at 29 Nov, 2006 12:54
$C({ul:[{li: 'First Item'},
{li: 'Second item'}....

I think thats a better solution than matching _\d*
Reply to this.
Binny V A at 05 Dec, 2006 04:29
Yes, that would be much better. I am working on a new version of this script.
Reply to this.
Binny V A at 02 Jan, 2007 05:30
The code is updated. Now you can create lists without resorting to that ugly hack.

Thanks for all the suggestions.
Reply to this.
DWJ at 02 Apr, 2007 12:26
Hi,

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...
Reply to this.
aconway at 21 Apr, 2008 08:16
What about functions?

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] );
}
}
Reply to this.
Comment


Comment




Comment Formating : HTML tags a, strong, em, b, i, code, pre, p and br allowed. Other tags will be shown as code(< will become &lt;). Urls, Line breaks will be auto-formated.