01 Jun

html2dom

a quickie. I’m currently working on a shopping cart application using AJAX. To make the designers’ lives less miserable, I need to write it in such a way that it can be written using templates for just about everything.

That would be fine in PHP, but it’s more difficult than you might think, when using plain ol’ JavaScript.

For one thing, the templates are written in HTML, but my JavaScript works in Objects. The thing is, that HTML is “flat”, as in it’s just a string. I needed to be able to convert a string of HTML into a DOM object before I could do anything with it.

Here is the basic html2dom function I came up with:

function html2dom(str,parent){
	var lastIndex=str.lastIndexOf('<')+1;
	if(!lastIndex)lastIndex=str.length;
	var obj,substr,indentlevel=0,charat=0,tracechar=0,subelstart=0,subelend=0,end=0;
	while(charat<lastIndex){
		if(str.charAt(charat)=='<'){ // element
			end=str.indexOf('>',charat+1);
			substr=str.substring(charat,end+1);
			switch(str.charAt(charat+1)){
				case '?':case '!':{
					obj=document.createElement('!');
					break;
				}
				default: {
					var tName=substr.substring(1,substr.length).replace(/[ \/\>].*/,'');
					obj=document.createElement(substr.substring(1,substr.length).replace(/[ \/\>].*/,''));
					var parameters=[];
					if(tName.length+3<substr.length){
						var paramstr=substr.substring(tName.length+2,substr.length-1);
						if(paramstr.charAt(paramstr.length-1)=='/')paramstr=paramstr.substring(0,paramstr.length-1);
						var i=0,name='',value='',stage=0,inquotes=0;
						while(i<paramstr.length){
							switch(stage){
								case 0:{ // name
									if(paramstr.charAt(i)=='='){stage=1;}
									else{name+=paramstr.charAt(i);}
									break;
								}
								case 1:{ // value
									if(paramstr.charAt(i)=='"')inquotes=!inquotes;
									else{
										if(paramstr.charAt(i)==' ' && !inquotes){
											if(name!='')obj.setAttribute(name,value);
											stage=0;
											name='';
											value='';
										}
										else{value+=paramstr.charAt(i);}
									}
								}
							}
							i++;
						}
						if(name!='')obj.setAttribute(name,value);
					}
					if(str.charAt(end-1)!='/'){ // not self-closing
						indentlevel=1;
						subelstart=end+1;
						tracechar=end;
						while(indentlevel){
							subelend=str.indexOf('<',tracechar+1);
							end=str.indexOf('>',subelend+1);
							if(str.charAt(subelend+1)=='/')indentlevel--;
							else if(str.charAt(end-1)!='/')indentlevel++;
							tracechar=end;
						}
						obj=html2dom(str.substring(subelstart,subelend),obj);
					}
				}
			}
			charat=end+1;
		}
		else{ // text
			end=str.indexOf('<',charat+1)-1;
			if(end<1)end=str.length;
			obj=document.createTextNode(str.substring(charat,end));
			charat=end+1;
		}
		parent.appendChild(obj);
	}
	return parent;
}

To call this, you would first create an element, then call this function with the HTML string you want converted. The function then returns your element, with the DOM attached to it.

var wrapper=document.createElement('div');
html2dom('<span class="color:red">test</span>',wrapper);
document.getElementsByTagName('body')[0].appendChild(wrapper);

It expects correct XHTML, and parameters must be enclosed in quotations, but I’m sure all webdevs write code like that 😉

I have more work to do so that it interprets my template codes, but the above is a working html2dom function (couldn’t find one when I went googling an hour or so ago).