Clone fieldset

Clone-o-Matic: Example | Download

Recently, I had the need to be able to asynchronously duplicate portions of a form, arbitrarily based on what the user might want to do. The requirements were such that the script needed to be reusable multiple times per page. After having read the book DHTML Utopia by Stuart Langridge, I knew that something along the lines of his free beer example would be needed.

The example in the book was simple, duplicating a single input text field by constructing the various aspects using DOM scripting. To do this for large chunks of a form would be tedious, and would tie my JavaScript to the specific functionality of the form, necessitating slight rewrites for each instance. Likewise, an innerHTML dump would keep it confined in the *.js file.

So, I thought the cloneNode method might be a better way to go. Initially, after some searching around, I happened upon the solution offered on Quirksmode, Extending Forms. He has a clever way of doing it, with an invisible template, so to speak. This is cloned and set to display: block so that the subsequent duplicates are usable. It also uses an empty span with an ID as the insertion point in the document. It increments a counter so ID / name will stay unique within the form, lest duplicate data be submitted.

This was the launching point for my idea. Yet, I wanted something more modular and reusable, without the need for unusable hidden templates or extraneous span's. I believe that what I have come up with is a nice plug-and-play method. Essentially, it looks for the nearest parent fieldset, clones it, adds a suffix + counter, then inserts the copy after the original.

The initial fieldset contains both an Add and Delete button, with deletion hidden by CSS. When the copy is made, a class of duplicate is added, causing the Delete button to appear, and the Add button to disappear. I also use it to re-color the fieldset, giving a visual indicator of the cloning.

You might notice that the clone and delete functions are called directly with an onclick event. Before you harp on that being too intrusive, I should mention that it was done this way because traversing through to find class names didn't account for the elements created after page load. So, rather than iterate through during each cloning, I went for the quicker route of just cloning the function call to cloneMe() and deleteMe() in the fieldset itself.

With JavaScript off, they are simply dead links pointing to an unnamed anchor of #, but could just as easily be hooked up to a server-side environment that would load a new fieldset with a page refresh. Either way, the potential is there for the same essential functionality, offering instantaneous feedback for those with JavaScript, and degrading gracefully in situations without it.

If you so desire, you can also nest fieldsets. The JavaScript is done in such a way that the suffix of :N (with N being the counter) is found, and then stripped out, so a new counter can be added. That way, if you don't end up with the problem of foo2 + 3 = foo23. I chose the delimiter of [:] because it is valid as part of an ID / name in XHTML, while still being unlikely enough not to conflict with any typical naming scheme using underscores or dashes.

I'm sure it can probably be improved upon, and that is of course why I'm releasing it - so that it can be a starting point for others faced with similar problems to solve. I want to thank Ara Pehlivanian for helping make the script self-referential, and Jonathan Snook for his ideas on incrementing a counter for nested fieldsets. Also, thanks to Jeremy Keith for his insertAfter method, described in his book DOM Scripting. I guess that wraps it up!