spudly.shuoink.com

the best way to predict the future is to implement it

Entries Comments


Using the XML DOM Without Writing 15,0000 Lines of Code

20 February, 2008 (22:17) | JavaScript

I’m a pretty big fan of using the XML DOM rather than innerHTML, simply because it seems to me to be better programming style. I hate hate hate, however, that it takes 15 lines of code to create one node. Today, I came up with an idea to solve the problem. I created a wrapper function for document.createElement() that takes either a string or an object literal as an argument. Then, based on the data in this argument, I create the needed element(s). Here’s a simple example that creates a text node:

var node = createElements("Hello World!");
document.body.appendChild(node);

Ok, so the above example is pretty useless, but what if you wanted to make that text into a link. the createElements() function allows us to pass an object literal with four items: tag, attributes, style, and children. “Tag” is, of course, the type of HTMLElement you want to create. “Attributes” is another object literal containing the attributes for the html tag (src, href, etc…). “Style” is an object literal containing the style attributes you want applied to the element, and last but certainly not least, “children” allows you to nest elements. It is an array of either strings or object literals, just like the ones you would pass into this function. Each of these children will be recursively created and appended to the main element. Here’s an example that creates a link to google:

var node = createElements({
	 tag: "a",
	 attributes: {
		href: "http://www.google.com"
	 },
	 style {
	    textDecoration: "none"
	 },
	 children: [ "Click Here!" ]
});
document.body.appendChild(node);

And here’s a more complex example that really shows off the nesting power of this javascript function:

var node = createElements({
   tag: "div",
   attributes: {
	  id: "google_link"
   },
   children: [
	  {
		 tag: "a",
		 attributes: {
			href: "http://www.google.com"
		 },
		 style: {
			textDecoration: "none",
			fontWeight: "bold"
		 },
		 children: [
			{
			   tag: "img",
			   attributes: {
				  src: "http://www.google.com/intl/en_ALL/images/logo.gif",
				  alt: "Google!",
				  title: "Google!",
				  border: "0"
			   },
			   style: {
				  width: "200px",
				  height: "200px"
			   }
			},
			{
			   tag: "br"
			},
			"Click here to go to Google!"
		 ]

	  }
   ]
});
document.body.appendChild(node);

The above example creates the following code:

<div id="google_link">
	<a style="text-decoration: none; font-weight: bold;" href="http://www.google.com">
		<img style="width: 200px; height: 200px;" title="Google!" alt="Google!" src="http://www.google.com/intl/en_ALL/images/logo.gif" border="0">
		<br />
		Click here to go to Google!
	</a>
</div>

As you can see, it’s quite a bit easier to use than typing mynode.setAttribute 20 times. Here’s the source code:

function createElements( args ) {
   var el;

   if( typeof(args) == "string" ) {

      el = document.createTextNode(args);

   } else if ( typeof(args) == "object" ) {

      el = document.createElement( args.tag );

      if ( args.attributes ) {
         for ( i in args.attributes ) {
            el.setAttribute(i, args.attributes[i]);
         }
      }
      if ( args.style ) {
         for ( i in args.style ) {
            el.style[i] = args.style[i];
         }
      }
      if ( args.children ) {
         for ( var i = 0; i < args.children.length; i++ ) {
            el.appendChild( createElements( args.children[i] ) );
         }
      }

   }
   return el;
}

« PHP Error Log Parser

 

Comments

Comment from spudly
Time: February 21, 2008, 8:02 am

Just a side note - If you were to write the above example without my method, you would have to write the code below, which is horribly ugly:

var div = document.createElement("div");
node.setAttribute("id", "google_link");
var a = document.createElement("a");
a.setAttribute("href", "http://www.google.com");
a.style.textDecoration = "none";
a.style.fontWeight = "bold";
var img = document.createElement("img");
img.setAttribute("src", "http://www.google.com/intl/en_ALL/images/logo.gif");
img.setAttribute("alt", "Google!");
img.setAttribute("title", "Google!");
img.setAttribute("boder", 0);
img.style.width = "200px";
img.style.height = "200px";
a.appendChild(img);
var br = document.createElement("br");
a.appendChild(br);
var text = document.createTextNode("Click here to go to Google!");
a.appendChild(text);
div.appendChild(a);

… whereas the example I gave could be condensed to:

var node = createElements({
   tag: "div", attributes: { id: "google_link" }, children: [
      {
         tag: "a",
         attributes: {href: "http://www.google.com"},
         style: {textDecoration: "none",fontWeight: "bold"},
         children: [
            {
               tag: "img",
               attributes: {
                  src: "http://www.google.com/intl/en_ALL/images/logo.gif",
                  alt: "Google!", title: "Google!", border: "0"
               },
               style: { width: "200px", height: "200px" }
            },
            { tag: "br" },
            "Click here to go to Google!"
         ]
      }
   ]
});

As you can see, there is a lot less typing involved…

Comment from NelsoN
Time: February 23, 2008, 10:50 pm

Due to several bugs in IE, your (otherwise very nice method) will fail on a bunch of the attributes you pass it..
http://webbugtrack.blogspot.com/2007/08/bug-242-setattribute-doesnt-always-work.html

E.g. if you try to create form elements (with a name) it will fail… setting the style will fail,… setting any event handlers will fail etc.

The good news is that only IE will fail. All other browsers are based on standards. :-)

Comment from spudly
Time: February 24, 2008, 8:51 am

Wow thanks for the info. I’ll have to give this another look, and I’ll post an update soon.

Comment from Jim
Time: February 29, 2008, 5:20 pm

I really like this. I don’t think it saves a lot of typing or lines of code, but it is MUCH more semantic (and I love anything that improves semantics).

As for IE, well, you just have to know to use all the proper attribute names that IE expects for when you execute in IE, and all the stuff Fx expects when in Fx, and all the stuff Opera expects when in Opera–you get the point. It has nothing to do with your code and has everything to do with the problem we have all faced in web dev–browser compatibility.

Well done,
Jim

Comment from spudly
Time: February 29, 2008, 6:49 pm

I’m actually working on a new version that works a little better with IE. It uses a proprietary IE technique for creating objects. I’m having a little trouble with table elements but other than that it is almost ready. Before I post it I’ll make sure to test it with all sorts of element types in multiple browsers. I think I’m pretty close tho…

Pingback from createElements() una función para ahorrarte mucho código | aNieto2K
Time: March 3, 2008, 1:17 am

[…] especial empeño en mejorar este punto ya que es algo que cada vez más usamos en nuestros scripts. createElements() es una función que facilita esta tarea sin necesidad de depender de un framework, 30 líneas que […]

Comment from krypton
Time: March 3, 2008, 3:08 am

Good work! Still share the opinion of Jim, it seems to me very clean and organized at the level of semantics, but not write less code. But still saves work. It continues the good work.

Comment from Jim
Time: March 3, 2008, 7:53 am

IRT Tables, just remember that IE and Fx vary between each other, and with themselves between raw HTML and DOM building, when it comes to the requirements for thead, tbody, and tfoot elements. It is ALWAYS safe, however, to ENSURE all TRs are within one of those blocks. So while this adds complication to your simple logic, you could look to see if the TR currently being built is inside one of them or not and put it there if it isn’t (I would default them to tbody myself).

Write a comment