Another option would be to use a recursive function:
// Accepts an element and a function
function childRecursive(element, func){
// Applies that function to the given element.
func(element);
var children = element.children();
if (children.length > 0) {
children.each(function (){
// Applies that function to all children recursively
childRecursive($(this), func);
});
}
}
Then you can make a function or three for setting the attributes and values of your yet-to-be-cloned form fields:
// Expects format to be xxx-#[-xxxx] (e.g. item-1 or item-1-name)
function getNewAttr(str, newNum){
// Split on -
var arr = str.split('-');
// Change the 1 to wherever the incremented value is in your id
arr[1] = newNum;
// Smash it back together and return
return arr.join('-');
}
// Written with Twitter Bootstrap form field structure in mind
// Checks for id, name, and for attributes.
function setCloneAttr(element, value){
// Check to see if the element has an id attribute
if (element.attr('id') !== undefined){
// If so, increment it
element.attr('id', getNewAttr(element.attr('id'),value));
} else { /*If for some reason you want to handle an else, here you go*/ }
// Do the same with name...
if(element.attr('name') !== undefined){
element.attr('name', getNewAttr(element.attr('name'),value));
} else {}
// And don't forget to show some love to your labels.
if (element.attr('for') !== undefined){
element.attr('for', getNewAttr(element.attr('for'),value));
} else {}
}
// Sets an element's value to ''
function clearCloneValues(element){
if (element.attr('value') !== undefined){
element.val('');
}
}
Then add some markup:
<div id="items">
<input type="hidden" id="itemCounter" name="itemCounter" value="0">
<div class="item">
<div class="control-group">
<label class="control-label" for="item-0-name">Item Name</label>
<div class="controls">
<input type="text" name="item-0-name" id="item-0-name" class="input-large">
</div>
</div><!-- .control-group-->
<div class="control-group">
<label for="item-0-description" class="control-label">Item Description</label>
<div class="controls">
<input type="text" name="item-0-description" id="item-0-description" class="input-large">
</div>
</div><!-- .control-group-->
</div><!-- .item -->
</div><!-- #items -->
<input type="button" value="Add Item" id="addItem">
And then all you need is some jQuery goodness to pull it all together:
$(document).ready(function(){
$('#addItem').click(function(){
//increment the value of our counter
$('#itemCounter').val(Number($('#allergyCounter').val()) + 1);
//clone the first .item element
var newItem = $('div.item').first().clone();
//recursively set our id, name, and for attributes properly
childRecursive(newItem,
// Remember, the recursive function expects to be able to pass in
// one parameter, the element.
function(e){
setCloneAttr(e, $('#itemCounter').val());
});
// Clear the values recursively
childRecursive(newItem,
function(e){
clearCloneValues(e);
}
);
// Finally, add the new div.item to the end
newItem.appendTo($('#items'));
});
});
Obviously, you don't necessarily need to use recursion to get everything if you know going in exactly what things you need to clone and change. However, these functions allow you to reuse them for any size of nested structure with as many fields as you want so long as they're all named with the right pattern.
There's a working jsFiddle here.