views:

1242

answers:

2

Hi,

As the title says, what I am trying to do is to have jQuery add a number of extra fieldsets based on the value selected in a drop-down box. For example, when the page loads, the drop-down has a default value of 1, and the default fieldset is displayed. Now, if the user selects a different value in that drop-down, the number of fieldsets should be adjusted accordingly, through cloning (I suppose this would be the best solution). Here's what I have so far:

Drop-down box code ...

<select id="itemCount" name="itemCount">
 <option value="1">1 item</option>
 <option value="2">2 items</option>
 <option value="3">3 items</option>
 <option value="4">4 items</option>
 <option value="5">5 items</option>
 <option value="6">6 items</option>
</select>

... the jQuery change listener ...

$(document).ready(function() {
 $("#itemCount").change(function(){
  var itemCountVal = jQuery(this).val();
  $("#item_dup_1").fieldsManage(itemCountVal);
 });
});

... and the function itself (it is actually based on a jQuery plugin which I thought was a good starting point for what I need):

jQuery.fn.fieldsManage = function (number) {
  var clone,
  objTemplate = source.clone(true),
  source = jQuery(this),
  maxClones = number - 1,
  clones = [];

  if (clones.length < maxClones) {
    while (clones.length < maxClones) {
      clone = objTemplate.clone(true).insertAfter(clones[clones.length - 1] || source);
      clones.push(clone);
    }
  }

  if (clones.length > maxClones) {
    // Fieldsets removal function goes here.
  }
}

The object that is being cloned is something like <fieldset id="item_dup_1"><input><input><input></fieldset>. I don't think it is necessary to show the full code for it as well.

This works like a charm for the first change, but if the user changes the value again, that's when things go wrong, and instead of showing the correct number of fieldsets, it shows more. It looks like it is calculating the number of fieldsets needed starting from scratch, disregarding the fact that fieldsets have already been added, and this is what my problem actually is. I also have a function (not shown here just to keep the question clear and as short as possible) that asigns new IDs to the cloned fieldset to prevent duplicate IDs and which works without a hitch.

I am convinced that I am doing something wrong, but I have been banging my head against a wall with this for two days now, trying to find what it is with no luck whatsoever so, any help would be more than appreciated !

Thanks in advance !

A: 

Why are you cloning instead of just doing something like this (together with your current event listener)?

$(".divWhereTheseGo").empty();
var count = 0;
while (count < maxItems) {
   $(".divWhereTheseGo").append("<fieldset id=\"" + count + "\">your form info</fieldset>");
   count++;
}

The count in the fieldset is how you can handle the unique id issue.

Nate B
This is definitely a useful and interesting approach I haven't thought about, but I could see a problem with it in the form of a user selecting 6 as the fieldset count and then changing his mind and only wanting 4 fieldsets. How would I go about removing the last two fieldsets without doing an empty() on the container and thus having the user entering the data in the first 4 fieldsets allover again ? Is there an opposite to append() ?
FreekOne
Please disregard my last comment. Thinking more about it, I realised I could simply look for the last child of the container in which the generated fieldsets go and do a .remove() on it. Thanks a million !
FreekOne
+2  A: 

I don't like the solution of Nate B.

  • it needs an additional container element
  • recreates everything, thus loosing data already entered
  • creates a maintenance problem: html markup of the fieldset is duplicated (once in HTML, once as string for append)
  • creates invalid id's for the fieldsets (id's can't start with a number)

This does what you want and similar to what you did. Keeps only the ids in a data attribute on the original element (instead of keeping the whole clone object which could use massive amounts of memory). Keeps all values already entered. Doesn't recreate everything if you change from e.g. 3 to 5. Instead only generates two new fieldsets. Removes them in reverse order they where created.

jQuery.fn.fieldsManage = function (number) {
    var ele = $(this);
    var clones = ele.data("clones");
    clones = clones ? clones : new Array(ele.attr("id"));
    if (clones.length < number) {
        var clone;
        while(clones.length < number) {
            clone = ele.clone(true);
            var id = clones[0]+clones.length;
            clone.attr("id", id);
            $("#"+clones[clones.length-1]).after(clone);
            clones.push(id);
        }
    } else {
        while(clones.length > number) {
            $("#"+clones.pop()).remove();
        }
    }
    ele.data("clones", clones);
}

$(document).ready(function() {
    $("#itemCount").change(function() {
        $("#item_dup_1").fieldsManage(this.value);
    });
});
jitter
Brilliant, works like a charm ! That is sort of what I had in mind but got stuck along the way. Thank you, much appreciated.
FreekOne
Maybe this is common knowledge, but I found that if data has been entered in the input fields of the fieldset that is being cloned before changing the fieldset count, the data entered will be cloned as well. Doing a simple clone.find("input").each(function(){jQuery(this).val("")}); after the clone has been created will give you a clean clone to work with. Hope this will spare somebody a couple of minutes.
FreekOne
Well this is more or less common knowledge and also the way `clone` is supposed to work. The jQuery `clone` method internally uses the normal DOM method `cloneNode(true)`. Check here for a description on how it behaves http://www.w3.org/TR/DOM-Level-2-Core/core.html#ID-3A0ED0A4
jitter
Yes, now it makes sense. I can now see how this could come in handy if one would like to pre-populate cloned fields with default values - simply populate the object that's being cloned. Neat !
FreekOne