DOM creation: good, bad, and ugly

Michael Geary | Wed, 2006-03-29 23:05
category

The Ajaxians are talking about a new $E function that is supposed to make it easier to create DOM elements. The example given creates the equivalent of this HTML code:

<div id="toolGroup_1" class="toolGroup">
   <div class="roundBarTop">
      <div class="leftEdge"></div>
      <div class="rightEdge"></div>
      <div class="heading">
         <a class="collapser"></a>
         Group Heading
      </div>
   </div>
</div>

using this code to do it:

var element=$E({
   tag:'div',
   className:'toolGroup',
   id:'toolGroup_1',
   children:{
      tag:'div',
      className:'roundBarTop',
      children:[{
         tag:'div',
         className:'leftEdge'
      },{
         tag:'div',
         className:'rightEdge'
      },{
         tag:'div',
         className:'heading',
         children:[{
            tag:'a',
            className:'collapser'
         },
            'Group Heading'
         ]
      }]
   }
});

That seems just a tad complicated! Let’s see how it would look using my DOM creator for jQuery and Prototype:

var element =
   $.DIV({ Class:'toolGroup', id:'toolGroup_1' },
      $.DIV({ Class:'roundBarTop' },
         $.DIV({ Class:'leftEdge' }),
         $.DIV({ Class:'rightEdge' }),
         $.DIV({ Class:'heading' },
            $.A({ Class:'collapser' }),
            'Group Heading'
         )
      )
   );

Ah, that is quite a bit simpler. It’s also more flexible. Suppose you want to save a reference to that A tag in the middle. There’s no way to do that with $E, but it can be as easy as this:

var atag;
var element =
   $.DIV({ Class:'toolGroup', id:'toolGroup_1' },
      $.DIV({ Class:'roundBarTop' },
         $.DIV({ Class:'leftEdge' }),
         $.DIV({ Class:'rightEdge' }),
         $.DIV({ Class:'heading' },
            atag = $.A({ Class:'collapser' }),
            'Group Heading'
         )
      )
   );
Submitted by Mario (not verified) on Thu, 2006-03-30 06:41.

wouldn’t the ‘atag’ be a global reference and isn’t that a bad thing?

Submitted by Michael Geary on Thu, 2006-03-30 07:42.

In the sample code, atag and element both have the same scope. Typically you would have this piece of code inside a function, in which case they would be local to that function.

Here is a practical example: Given a parent element, you want to insert a DIV inside it, and inside that an A element, with an onclick handler on the A. You want to avoid IE’s DOM Insertion Order memory leak, so you attach the onclick handler after inserting the DOM elements. A good way to do it with jQuery would be:

function addStuff( parent ) {
   var atag;
   parent.appendChild(
      $.DIV({ Class:"MyDiv" },
         atag = $.A({ Class:"MyLink", href:"http://www.example.com/" },
            "Click me"
         )
      )
   );
   atag.click( myClickHandler );
}
Submitted by Mario (not verified) on Thu, 2006-03-30 20:47.

I had no idea about that specific memory leak. In fact my project creates the child element then attatches it to the parent then attache its parent to the parent n so on, which is exactly how the memory leak is created. YIKES!

Thanks for your example 8)

Submitted by Michael Geary on Thu, 2006-03-30 22:00.

It’s OK to create and insert elements in the order you’re describing. In fact that is how they get created using the DOM plugin, with the child created first, appended to its parent, which then gets appended to its parent and so on.

According to the Microsoft article (and I’ve verified this), it only becomes a problem if you attach event handlers to a child node before inserting it into the DOM tree. If you insert the node into the document DOM first, then attach event handlers to it, you’re OK.

Strange but true, but then it is IE we’re talking about… :-)