Shorter DOMscript via cloning vs. generating elements

cloned sheepI just looked through some of my older scripts and other people’s code and realised that a lot of time we bloat our methods by repeating the same functionality.

This is especially true when it comes to generating a lot of HTML via the DOM.

We keep repeating the same document.createElement and setAttribute lines over and over. Wouldn’t it be more efficient to create those once when initialising our script and then cloning them?

Say for example we need to generate three links that are dependent on JavaScript (and we should generate these via JS).

A normal method to do this would be:


// 30 lines of code
createLinks:function(){
var newul,newli,newa;
newul=document.createElement('ul');
newli=document.createElement('li')
// sheep #1
newa=document.createElement('a');
newa.appendChild(document.createTextNode('baa#1'));
newa.href='#';
dolly.addEvent(newa,'click',dolly.baa1);
newa.onlick=function(){return false;} // Hello Safari
newli.appendChild(newa);
newul.appendChild(newli);
// sheep #2
newli=document.createElement('li')
newa=document.createElement('a');
newa.appendChild(document.createTextNode('baa#2'));
newa.href='#';
dolly.addEvent(newa,'click',dolly.baa2);
newa.onlick=function(){return false;} // Hello Safari
newli.appendChild(newa);
newul.appendChild(newli);
// sheep #3
newli=document.createElement('li')
newa=document.createElement('a');
newa.appendChild(document.createTextNode('baa#3'));
newa.href='#';
dolly.addEvent(newa,'click',dolly.baa3);
newa.onlick=function(){return false;} // Hello Safari
newli.appendChild(newa);
newul.appendChild(newli);
// herd them
document.body.appendChild(newul);
},

Now, if part of the initialisation method would be to generate a dummy link inside an LI, we could reuse by cloning it:


makeClones:function(){
dolly.li=document.createElement('li');
dolly.a=document.createElement('a');
dolly.a.appendChild(document.createTextNode('baa!'));
dolly.a.setAttribute('href','#');
dolly.li.appendChild(dolly.a);
},

By cloning this dummy li and referencing the link via firstChild and the link text via firstChild.firstChild we can save 9 lines of code, which still means a 4 line win if add the createClones method:


// 21 lines + 5 lines createClones
createFlock:function(){
var newli,newa,newul;
newul=document.createElement('ul');
// sheep #1
newli=dolly.li.cloneNode(true);
dolly.addEvent(newli.firstChild,'click',dolly.baa1);
newli.firstChild.onlick=function(){return false;} // Hello Safari
newli.firstChild.firstChild.nodeValue='baa#1';
newul.appendChild(newli);
// sheep #2
newli=dolly.li.cloneNode(true);
dolly.addEvent(newli.firstChild,'click',dolly.baa2);
newli.firstChild.onlick=function(){return false;} // Hello Safari
newli.firstChild.firstChild.nodeValue='baa#2';
newul.appendChild(newli);
// sheep #3
newli=dolly.li.cloneNode(true);
dolly.addEvent(newli.firstChild,'click',dolly.baa3);
newli.firstChild.onlick=function(){return false;} // Hello Safari
newli.firstChild.firstChild.nodeValue='baa#3';
newul.appendChild(newli);
// herd them
document.body.appendChild(newul);
},

Further reduction could be achieved by moving the event handling and text changing to another helper method:


dressClone:function(clone,func,name){
dolly.addEvent(clone.firstChild,'click',func);
clone.firstChild.onlick=function(){return false;} // Hello Safari
clone.firstChild.firstChild.nodeValue=name;
},

Which means the main method for generating the links will be 23 lines in total (including the helper methods):


// 15 lines + 5 lines + 3 lines
createWoolyFlock:function(){
var newli,newa,newul;
newul=document.createElement('ul');
// sheep #1
newli=dolly.li.cloneNode(true);
dolly.dressClone(newli,dolly.baa1,'baa#1');
newul.appendChild(newli);
// sheep #2
newli=dolly.li.cloneNode(true);
dolly.dressClone(newli,dolly.baa2,'baa#2');
newul.appendChild(newli);
// sheep #3
newli=dolly.li.cloneNode(true);
dolly.dressClone(newli,dolly.baa3,'baa#3');
newul.appendChild(newli);
// herd them
document.body.appendChild(newul);
},

The benefits are the following:

  • We have one centralised spot for defining the links initially – if for example we needed to add a class at a later state we’d do it in one method instead of scanning the whole object of where to apply the changes
  • Generating a new LI is now 3 lines of code instead of 7, which multiplies with the amount of links
  • We don’t need to re-define a lot of variables or create new ones every time we add a link.

The example script of cloning vs. generating works on MSIE 6, Firefox, Opera 8 and Safari.

Is there any problem or bad effect I am overlooking? How is the performance of cloning vs. generating anew every time we need a link?

  • http://verens.com/archives/2006/01/09/clonenode-caching/ » cloneNode caching « klog

    [...] ion encapsulating the original code. I was reading through my blogs today, and came across this article, which discusses the use of element.cloneNode() instead of groups of document.createEleme [...]

  • http://www.webstandards.org/2006/01/06/shorter-domscripting-via-cloning-vs-generating-new-elements/ Shorter DOMScripting via Cloning vs. Generating New Elements? – The Web Standards Project

    [...] te them once and clone them via cloneNode() later on. Please check the relevant post about cloning vs. generating elements and give the test code a spin to see if you can find any bad issues I [...]

  • http://q42.nl/ Sjoerd Visscher

    This is silly. Creating functions for repeating code makes code shorter, cloneNode doesn’t. I’m not sure how you count the lines of code, but if you replace the 3 blocks of 8 lines in createLinks with a function call, you win about 3*8 – 3 – 8 = 13 lines, keeping the total below 20 lines.

  • http://www.hesido.com Emrah Baskaya

    I have to agree with Sjoerd:

    function insertLi(targetUL,elName,textN,lnk) {
    var lN = document.createElement('li');
    var lil = document.createElement(elName);
    lil.appendChild(document.createTextNode(textN));
    lil.href = lnk;
    lN.appendChild(lil); targetUL.appendChild(lN);
    return lN;
    }

    Then everytime you’d just say:
    insertLi(myUL,’a’,’my link text’,’my href’)
    and so on…

    And you could possibly add different elements too.

  • http://icant.co.uk Chris

    I am aware of the option of doing that in a function, but the question still stands: Is the cloneNode() method not a better way as it just clones an existing object rather than creating a new one?

    The benefit of a method instead of cloning is that you could set the event handlers in the method, too. Cloned nodes don’t clone event handlers it seems.

    However, I am not a big fan of methods or functions with lots of parameters.

  • janos erdelyi

    if you are creating a massive number of nodes of the same type, you should use cloneNode();

    it’s not a matter of lines, it’s a matter of performance. and since javascript is run by the client, thinking of their performace is a nice thing to do.

  • http://verens.com/ Kae Verens

    That sparked an idea in my head. Thanks! I’ve used this idea to improve the performance of my own newEl() function:
    http://verens.com/archives/2006/01/09/clonenode-caching/

  • http://www.vivabit.com/bollocks/ Dan Webb

    This kind of thing is something I’ve been thinking about recently. I’ve always found creating elements and attaching attributes to be really tedious for all but the most simple cases. You just end up with lots setAttributes and appendChilds repeated over and over again so I’ve been investigating ways to generate node structures really easily and (althought its in a pretty raw state I’ve come up with this so far:

    http://svn.vivabit.net/external/codedump/javascript/DomBuilder/

    It’s straight out of my svn code dump with a small test page. See if you can work it out from the code. I think it’s really promising. Still I’ll post about it soon on vivabit.com/bollocks.

    Just thought you might be interested. I do think this is an area worth alot of investigation though so nice one.

  • http://www.nickfitz.co.uk/ Nick Fitzsimons

    I too am “...not a big fan of methods or functions with lots of parameters”. A good design pattern for avoiding that particular code smell is Parameter Object. I’ll have to write something up about using that pattern in JS… when I get the time.

  • DayneO

    I have found this method (cloneNode) to be a life saver when dealing with multirow tables that need add/remove functionality. The columns can get quite complex with nested tables/complex layouts etc. and doing DOM construction in javascript is simply unmanageable.

    The cloneNode functionality allows me to draw up a “template” row at design time using a WYSIWYG editor and my AddRow function simply clones that node at run-time.

    It saved me hours and hours in coding and maintenance. Some of my other colleagues are still using the old method, and they battle all the time.

blog comments powered by Disqus
Christian Heilmann's blog – Wait till I come! is the blog of Christian Heilmann , a developer evangelist living and working in London, England. Download vcard.

Feed me, Seymour: Entries (RSS) and Comments (RSS).