Losing Values When Cloning Forms

The Web is ever changing, and this article is relatively ancient having been published 13 years ago. It is likely out of date or even blatantly incorrect in relation to modern better practices, so proceed at your own risk.

I’ve finally started development of a book recommendation widget for the musings and reviews on books I read section of my site. The general functionality is pretty simple: visitors have a few fields to complete with info about the book; upon submission, their recommendation is saved to a database; the new recommendation is shown to all and sundry in a “last recommendation” section; rinse & repeat. The whole no-JS needed, server-side scripting processing involved is simple, straight-forward and was quickly completed. Being a front-end developer, however, I want to make sure this can all be done in a smooth JS-enhanced way as well (for some nifty UX). That’s where I encountered yet another annoying JavaScript problem.

Each browser interprets “clone” differently

Due to differences in how each browser implemented cloneNode() in their JavaScript engines, there is an issue with values for form elements not persisting to the copy of an element. Inconsistencies like this are many of the reasons why I’m a fan of using a JavaScript library for most projects. In this case, because the library has usually worked out the issues and can handle copying without losing important data. Unfortunately, jQuery still has some issues to work out; it loses select and textarea values on clone(). Other inputs types don’t seem to have any issues, including hidden fields.

How this affected my widget

In order to display the submitted form data in the “last recommendation” section, I decided the best approach would be to copy the form, then replace each form element with an inline element containing the value based on type of element. It might not be the most elegant solution, but it seemed better than specifying each individual element by name and manipulating it that way.

So, step one: use clone() to copy the form (no need for cloning events and the like). Step two: do the replacement. Step three: realize that regardless of selection, the last recommendation always displays the first option in a select box. And an empty string for any text area.

Solutions

Honestly, my current solution is just a dirty hack. I only have two affected fields, so I explicitly copy the original values to where they need to go. I’m more concerned about getting this up and running, knowing that there won’t be much in the way of extension in the future. I am about 99% positive I won’t be adding any additional fields, at least.

For other projects though, this could be a major issue for scalability and ongoing maintenance, especially if there are multiple affected elements. A quick search around the Internet shows a lot of inelegant solutions. One that approaches a decent solution for jQuery is this solution at mddev.co.uk, although it only attacks the select issue (and later inputs as well with a bit overkill [see the comments], but no textarea). That approach could work with some modifications.

Have you seen this before? How’d you solve it/work around it?

3 thoughts on “Losing Values When Cloning Forms”

  1. If I'm not misunderstanding this, it seems like a problem I've run into before. The code below has worked for me in the past – just save the values of every field in the original form, then write them back to the new cloned form. It doesn't work for select "multiple" elements, but that could probably be fixed with another line of code.

    var form = jQuery("form");
    var new_form = form.clone();

    var fields = form.find(":input").serializeArray();

    jQuery.each(fields, function(i, field) {
    new_form.find(":input[name='"+field.name+"']").attr({value:field.value});
    });

    form.after(new_form); // add the cloned form to the DOM any way you want

    1. Hmm. That’s about what the author at mddev.co.uk did. So, it’s pretty much overkill for everything but select and textarea, but is about the only scalable solution I’ve seen/can think of at this point.

      Side-note, I always forget that the :input selector actually selects button/select/textarea in addition to input elements. Thanks for reminding me.

  2. Cheers for the mention, I’ve been meaning to go back to this and tidy it up. I need to check exact which input types copy over and which need to be done manually so I can narrow down the select to grab just the needed ones. I’ll have a look at the weekend and see if i can knock it into shape. The only issue I’d have to test is whether the overkill of using ‘:input’ is offset by using slower selectors like ‘:input[type=hidden]’ but I suspect that the answer will be: depends on the form.

Comments are closed.