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).

4 thoughts on “html2dom

  1. Why not use the HTML-Parser of your Browser? How about creating a hidden iframe-element and document.write() the HTML code in it. Then you can copy the node tree to your original node and remove the iframe.

  2. I don’t think that would solve the problem of attaching events to the elements. Another down-side of this is that you might end up mixing and matching innerHTML style and DOM style, which could lead to unpleasantness.

    I know, for example, that you cannot add this as the innerHTML of an object and expect it to work in IE;

    right-click on <span oncontextmenu=”alert(‘tada!’);” style=”text-decoration:underline”>these words</span> to get a pop-up

    I’m not sure if that works with document.write() in an iframe (oh, the complexity!), but I suspect it wouldn’t.

    Anyway, I think it’s more “neat” to simply call a function that handles it. πŸ˜‰

  3. It sould work with document.write() but I haven’t tried, so I can’t tell you for shure.

    Of course you can encapsulate stuff like that in a function. Then you simply call a function that handles it and BTW avoid problems with so called “Web-Designers” who can not write clean XHTML. The iframe is not only a lot faster than a self made interpreted HTML parser but also a lot more fault tolerant.

  4. That’s probably very true. FredCK said something very similar on the FCKeditor mailing list only yesterday. I would need to check that events are not mangled in transfers before converting the code to use IFrames, but if it turned out they work fine, then yes, it would be very simple to convert this. Thanks Christof.

Comments are closed.