17
Sep
2009

You know the situation. You have a form with 'x' number of text inputs. Eventually, you realize 'x' may not be enough for all users. But in the interest of keeping the page clean, you don't want to arbitrarily continue to add these elements.

What you really want to do is show a minimal amount initially, and then give the user the option of adding as many more (within reason) as that particular user might need. You might go about doing that by creating 100 fields and setting their CSS display attribute to "none", while adding a slick JavaScript function to allow the user to display as many as they need. And that would work, but...

Why create any DOM elements that aren't going to be used? Wouldn't it be better to dynamically create an element and add it to the DOM as the user needs it? (Hopefully you're nodding in affirmation) Great! Let's do it with jQuery.

First, you'll want to see it in action. I've put together a quick demo for you to check out. The rules of the demo are simple. You can add up to 5 names, and you must have at least one. Take a look and c'mon back so we can walk through the code.

Let's take a look at the code first, and then break it down. As with previous entries, you can copy and paste this code onto your own machine and run it, since it points to Google for the jQuery core files. Also be aware that the jQuery code is much leaner than what you see. Almost half of the lines of code are comments for your benefit.

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
                    "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
	<title></title>
	<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.3/jquery.min.js"></script>

	<script type="text/javascript">
		$(document).ready(function() {
			$('#btnAdd').click(function() {
				var num		= $('.clonedInput').length;	// how many "duplicatable" input fields we currently have
				var newNum	= new Number(num + 1);		// the numeric ID of the new input field being added

				// create the new element via clone(), and manipulate it's ID using newNum value
				var newElem = $('#input' + num).clone().attr('id', 'input' + newNum);

				// manipulate the name/id values of the input inside the new element
				newElem.children(':first').attr('id', 'name' + newNum).attr('name', 'name' + newNum);

				// insert the new element after the last "duplicatable" input field
				$('#input' + num).after(newElem);

				// enable the "remove" button
				$('#btnDel').attr('disabled','');

				// business rule: you can only add 5 names
				if (newNum == 5)
					$('#btnAdd').attr('disabled','disabled');
			});

			$('#btnDel').click(function() {
				var num	= $('.clonedInput').length;	// how many "duplicatable" input fields we currently have
				$('#input' + num).remove();		// remove the last element

				// enable the "add" button
				$('#btnAdd').attr('disabled','');

				// if only one element remains, disable the "remove" button
				if (num-1 == 1)
					$('#btnDel').attr('disabled','disabled');
			});

			$('#btnDel').attr('disabled','disabled');
		});
	</script>
</head>

<body>

<form id="myForm">
	<div id="input1" style="margin-bottom:4px;" class="clonedInput">
		Name: <input type="text" name="name1" id="name1" />
	</div>

	<div>
		<input type="button" id="btnAdd" value="add another name" />
		<input type="button" id="btnDel" value="remove name" />
	</div>
</form>

</body>
</html>

The Markup

Let's look at what's going on, starting with the form itself:

<form id="myForm">
	<div id="input1" style="margin-bottom:4px;" class="clonedInput">
		Name: <input type="text" name="name1" id="name1" />
	</div>

	<div>
		<input type="button" id="btnAdd" value="add another name" />
		<input type="button" id="btnDel" value="remove name" />
	</div>
</form>

Very straightforward markup. A single form with an "id" attribute. The form contains a div holding a text input and some label text. The div is assigned a class of "clonedInput". This is important. The name can be whatever you choose, but the fact that there is a particular class assigned to this div will be used in our jQuery code.

Next up is another div that holds 2 button elements, each with a unique id attribute. As you might have guessed, on click, one will add a new form element, and the other will remove a form element.

That's it for our markup. And therein lies the true beauty of jQuery. Our markup is clean and pristine. No need to litter it with hidden elements that we may not even need.

jQuery - The First Step

Moving to the jQuery code itself. Starting off very basic, the first thing we're going to do is disable the "remove name" button. Since the page initially renders with only one name field, we don't want to let the user remove that.

$(document).ready(function() {
	$('#btnDel').attr('disabled','disabled');
}

At it's most basic, jQuery adheres to the principle of "find something, do something". Right now, we're finding the element with the id "btnDel", and what we're doing with it is setting it's "disabled" attribute to "disabled".

jQuery - Adding a New Form Element

Next we're going to want to handle the click event for the "btnAdd" button:

$(document).ready(function() {
	$('#btnDel').attr('disabled','disabled');

	$('#btnAdd').click(function() {
		var num		= $('.clonedInput').length;	// how many "duplicatable" input fields we currently have
		var newNum	= new Number(num + 1);		// the numeric ID of the new input field being added

		// create the new element via clone(), and manipulate it's ID using newNum value
		var newElem = $('#input' + num).clone().attr('id', 'input' + newNum);

		// manipulate the name/id values of the input inside the new element
		newElem.children(':first').attr('id', 'name' + newNum).attr('name', 'name' + newNum);

		// insert the new element after the last "duplicatable" input field
		$('#input' + num).after(newElem);

		// enable the "remove" button
		$('#btnDel').attr('disabled','');

		// business rule: you can only add 5 names
		if (newNum == 5)
			$('#btnAdd').attr('disabled','disabled');
	});
}

Still using the "find something, do something" principle, but this time, once we find the element (in this case, the element with the id "btnAdd"), we're going to listen for a specific event (in this case, the click event), before we do something (in this case, create a new element and append it to the DOM).

var num	    = $('.clonedInput').length;
var newNum  = new Number(num + 1);
var newElem = $('#input' + num).clone().attr('id', 'input' + newNum);

First we're going to set a few variables:

  1. Using a jQuery selector, we're going to look for all elements with the class of "clonedInput" (remember this was the class assigned to the div that held the actual form element). jQuery returns an array of matching elements. We need to know how many there currently are, so we access the array's length property.
  2. The variable newNum is going to refer to the next element... the one we're going to create. So we increment the previous variable (num) by one.
  3. The variable newElem is a reference to a DOM element that we're going to create using jQuery's built in clone() method. Again, using a selector and the "find something, do something" principle, we're going to find the last of the clone-able inputs. As we only have one to start, its id (and name) attribute is "input1". We concatenate the variable num (created in item 1 above) to access that specific element. We clone() it, and then using method chaining, set its id attribute to be "input2" (subsequent elements would be "input3", "input4", and "input5").

Now that we've got our new <div> (complete with a unique ID), we need to manipulate the "name" and "id" attributes of the <input> within.

newElem.children(':first').attr('id', 'name' + newNum).attr('name', 'name' + newNum);

Remember that "newElem" refers to our newly created DOM element. It's a div that contains an input. The input is the first "child" element of the div, so it can be referenced via:

newElem.children(':first');

Next, we want to set the "id" attribute and "name" attribute. Recall that with the first text input, these attributes had the same value ("name1"). Using the "newNum" variable that was set above, we can easily set the "id" and "name" attributes of our new element to "name2".

Again, we're using method chaining. The script block above could have been written as:

newElem.children(':first').attr('id', 'name' + newNum);
newElem.children(':first').attr('name', 'name' + newNum);

But that's not quite as efficient as method chaining, and also a little more verbose. Chained methods do not have to be on the same line, and for the sake of legibility can be written as:

newElem.children(':first')
	.attr('id', 'name' + newNum)
	.attr('name', 'name' + newNum);

Now that we have our new element all set up, we need to insert it into the DOM. As of yet, it's not part of the page, it's just a jQuery variable.

$('#input' + num).after(newElem);

Remember that "num" refers to the current number of clone-able elements. If there's only one element on the page, num equals 1. Using a jQuery selector, we reference the last input, and using the appropriately named jQuery method after(), insert our new element (newElem) into the DOM.

Just 2 more steps and we're done with adding an element.

Because we just added a new element, we know for a fact that we have more than one. So we can now enable the "remove name" button. The first line of jQuery that we wrote today disabled it by setting the "disabled" attribute to "disabled". We just undo that by setting the value to an empty string.

$('#btnDel').attr('disabled','');

Finally, we need to enforce our business rule of ensuring a maximum of 5 elements.

if (newNum == 5)
	$('#btnAdd').attr('disabled','disabled');

Hopefully, you see what happened there. If the value of newNum (the variable representing the newly added DOM element) is 5, disable the "add another name" button. This is identical to disabling the "remove name" button, except for the selector.

jQuery - Removing a Form Element

Good news. We're halfway there. We can now add (up to 5) elements dynamically. But we need to allow the user to remove any elements that they added as well.

$('#btnDel').click(function() {
	var num	= $('.clonedInput').length;	// how many "duplicatable" input fields we currently have
	$('#input' + num).remove();		// remove the last element

	// enable the "add" button
	$('#btnAdd').attr('disabled','');

	// if only one element remains, disable the "remove" button
	if (num-1 == 1)
		$('#btnDel').attr('disabled','disabled');
});

To add a new element, we started out by listening for a click event on the element with the ID "btnAdd". Now, we're going to listen for a click event on the element with the ID "btnDel". Also similar to above, we want to start out by getting the total number of "clonedInput" elements:

var num	= $('.clonedInput').length;

And now that we know how many there are, we can safely remove the last one. jQuery makes this easy by providing a built-in remove() method:

$('#input' + num).remove();

Since we've just removed an element, we're guaranteed to have fewer than the 5 specified as the maximum. We can therefore safely make sure the "add another name" button is enabled:

$('#btnAdd').attr('disabled','');

Finally (yes, I know it's been a long time coming), we need to disable the "remove name" button if only one element remains:

if (num-1 == 1)
	$('#btnDel').attr('disabled','disabled');

Putting it all Together (One More Time)

That's it. If you've stuck with me this far, more power to you. I know it might seem complex, but if you take the time to really look at each line you'll see how straightforward it is. This entry ran a bit longer than I had anticipated, but I wanted to make sure to explain what was happening on a line-by-line basis. In reality, without comments "cluttering" the script, we're talking about under 20 lines of jQuery. Here it is again, the entire page, without comments. Hopefully that will help illustrate that it really is a pretty concise way of handling the need to dynamically add or remove elements from the DOM. Take another look and see if it isn't more clear than it was when you first looked at it above.

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
                    "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
	<title></title>
	<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.3/jquery.min.js"></script>

	<script type="text/javascript">
		$(document).ready(function() {
			$('#btnAdd').click(function() {
				var num		= $('.clonedInput').length;
				var newNum	= new Number(num + 1);

				var newElem = $('#input' + num).clone().attr('id', 'input' + newNum);
	
				newElem.children(':first').attr('id', 'name' + newNum).attr('name', 'name' + newNum);
				$('#input' + num).after(newElem);
				$('#btnDel').attr('disabled','');

				if (newNum == 5)
					$('#btnAdd').attr('disabled','disabled');
			});

			$('#btnDel').click(function() {
				var num	= $('.clonedInput').length;

				$('#input' + num).remove();
				$('#btnAdd').attr('disabled','');

				if (num-1 == 1)
					$('#btnDel').attr('disabled','disabled');
			});

			$('#btnDel').attr('disabled','disabled');
		});
	</script>
</head>

<body>

<form id="myForm">
	<div id="input1" style="margin-bottom:4px;" class="clonedInput">
		Name: <input type="text" name="name1" id="name1" />
	</div>

	<div>
		<input type="button" id="btnAdd" value="add another name" />
		<input type="button" id="btnDel" value="remove name" />
	</div>
</form>

</body>
</html>

While I do think it's concise, I also recognize that it might be intimidating when you're new to the jQuery library. If you have questions or comments, please don't hesitate to post them, and I'll do my best to answer.

jQuery concepts used:

Comments (40) | 16000 Views

Comments

Add Comment | Subscribe to Comments

  1. marc esher's Gravatar

    # Posted marc esher on 9/17/09 4:17 PM

    Charlie,
    this seems like one of those posts that a lot of people would look at and no one would comment on because they're starry eyed by your sheer brilliance. Consequently, I want to congratulate you on a fine post.

    Nice code formatting, too! I bet that was a pain to put into your blog, and for your additional attention to such formatting you should be commended.

  2. Charlie Griefer's Gravatar

    # Posted Charlie Griefer on 9/17/09 4:35 PM

    @Marc - heh. thanks :) This entry ended up being longer than I had expected, and I was just worried that it was information overload. Not much point in taking the time to compose a blog entry if folks aren't able to absorb it (and thus learn from it) in one sitting.

    The code formatting is SyntaxHighlighter (http://alexgorbatchev.com/wiki/SyntaxHighlighter). If anyone deserves kudos, it's Alex. He did a great job on it.

  3. TJ Downes's Gravatar

    # Posted TJ Downes on 9/17/09 5:38 PM

    Bookmarked. Good stuff Charlie, thanks!

  4. Mark Mandel's Gravatar

    # Posted Mark Mandel on 9/17/09 9:07 PM

    Is it wrong that I do it this way with jquery (written inline, so typos will exist):
    <cfsavecontent variable="formElem">
    {label}: <input type="text" name="{id}" id="{id}" />
    </cfsavecontent>

    <script type="text/javascript">
    formElemHTML = "#JSStringFormat(formElem)#";

    newElementHTML = formElemHTML.replace(/{label}/ig, "My fancy new label");
    newElementHTML = newElementHTML.replace(/{id}/ig, counter++);

    $("#parentElement").append(newElementHTML);
    </script>

    That's a really lazy showing of it, but you get the gist ;o)

    I just like writing the template in HTML in cfsavecontent, so I don't have to build the DOM node by node.

    Whatcha think?

  5. Charlie Griefer's Gravatar

    # Posted Charlie Griefer on 9/17/09 9:43 PM

    @Mark - Yeah, it's wrong :)

    It's actually a pretty cool approach, and one that I'd not thought of. Always interesting to see a different means to the same end. I'm not sure it's all that much simpler... your 4 lines are equivalent to 5 of mine (so yeah OK, 1 line simpler) :P

  6. GT's Gravatar

    # Posted GT on 9/19/09 6:28 PM

    Hey there Charlie,

    Thanks for this - it provides the foundation for what I'm after. I'm trying to write a 'stock picker' where users can choose their own criteria and then hit 'send' and get the data back.

    I just spent a couple of hours looking for a way to construct a 'multiple filter' on a mySQL database with an interface similar to the e-mail filter function in Thunderbird (or the data filters in Excel or OpenOffice Calc).. simple as pie in design, but getting the functionality to dynamically add new filters was a pain until I found your site.

    Goes to show how important search terms are: I started with "multiple form filters mySQL" and wound up finding the right universe by the time I got to "jquery dynamically add remove elements"; took me eight searches before I even got onto the right track.

    Cheers


    GT

  7. Phil Webb's Gravatar

    # Posted Phil Webb on 9/24/09 12:21 PM

    Charlie,

    First off, amazing code! Very clever. I would like to add multiple text fields in each div. It displays and works correctly but when I submit it only one of the fields comes through correctly. Say the fields are “size” and “color” I get size1, color1, color2, color3, etc.. In other words, the code to rename the name and id only works on one of the fields. You used newElem.children(':first') to access the single field in your example. I assumed I could loop through all the children and use a similar line for each child. The length of the children is only 1, which is confusing. I figured there would be one child for each text box. Any ideas? Thanks!

    Phil

  8. Charlie Griefer's Gravatar

    # Posted Charlie Griefer on 9/24/09 12:29 PM

    @Phil - If your div contains 2 text inputs, there should be 2 children elements. For example, if I have this:

    <div id="myDiv">
       <input type="text" name="text1" id="text1" />
       <input type="text" name="text2" id="text2" />
    </div>

    Then the following jQuery alerts the value '2':

    $(function(){
       alert($('#myDiv').children().length);
    });

    Against the same markup, the following jQuery properly alerts "text1" and then "text2":

    $(function(){
       $('#myDiv').children().each(function() {
          alert($(this).attr('id'));
       })
    });

    Perhaps you're not looping properly?

  9. Phil Webb's Gravatar

    # Posted Phil Webb on 9/24/09 2:11 PM

    Thanks Charlie. I am up and running. I had used children.length instead of children().length to get the length of the children.

    Out of curiosity, is there a way to directly access one of the children without using the .each loop or :first? Something like $('#myDiv').children('foo').attr('id').

  10. Charlie Griefer's Gravatar

    # Posted Charlie Griefer on 9/24/09 2:26 PM

    @Phil - Lots of ways :)

    For example...
    $('#myDiv').children().eq(0) <-- first child
    $('#myDiv').children().eq(1) <-- second child

    I'd suggest looking at:
    http://docs.jquery.com/Selectors
    http://docs.jquery.com/Traversing

    and some really good stuff at:
    http://www.learningjquery.com/2006/11/how-to-get-a...
    http://www.learningjquery.com/2006/12/how-to-get-a...

    If you have a specific situation you're trying to address and need some help, let me know.

  11. Steve Snyder's Gravatar

    # Posted Steve Snyder on 10/8/09 6:19 AM

    Ok. So I understand how this works and I am trying to incorporate it with another script. So far it duplicates the input elements perfectly but I have a script that shows additional information when a Select Option is selected. When you "add another", it works fine however the script that shows the additional info only works on the first. Is there anyway that I can send the number of cloned Div to that script?

    Here's the code:
    <script language="JavaScript" type="text/javascript">

    function getElementsByClass( searchClass, domNode, tagNames) {
       if (domNode == null) domNode = document;
       if (tagNames == null) tagNames = '*';
       var el = new Array();
       var tags = domNode.getElementsByTagName(tagNames);
       var tcl = " "+searchClass+" ";
       for(i=0,j=0; i<tags.length; i++) {
          var test = " " + tags[i].className + " ";
          if (test.indexOf(tcl) != -1)
             el[j++] = tags[i];
       }
       return el;
    }


    function showHide(id)
    {
       // hide every element with class 'tab'
       var tabs = getElementsByClass('hideit');
       for(i=0; i<tabs.length; i++)
          tabs[i].style.display = 'none';
       // hide every element with class 'tab'      

       document.getElementById(id).style.display='block';
       // show element with given tabname
    }


    </SCRIPT>

    <script src="http://ajax.googleapis.com/ajax/libs/jquery/1.3/jq...;

    <script type="text/javascript">
    $(document).ready(function() {
    $('#btnAdd').click(function() {
    var num = $('.clonedInput').length;
    var newNum = new Number(num + 1);

    var newElem = $('#input' + num).clone().attr('id', 'input' + newNum);

    newElem.children(':first').attr('id', 'name' + newNum).attr('name', 'name' + newNum);
    $('#input' + num).after(newElem);
    $('#btnDel').attr('disabled','');

    if (newNum == 10)
    $('#btnAdd').attr('disabled','disabled');
    });

    $('#btnDel').click(function() {
    var num = $('.clonedInput').length;

    $('#input' + num).remove();
    $('#btnAdd').attr('disabled','');

    if (num-1 == 1)
    $('#btnDel').attr('disabled','disabled');
    });

    $('#btnDel').attr('disabled','disabled');
    });
    </script>



    </head>
    <body>
    <form id="myForm">
    <div id="input1" style="margin-bottom:4px;" class="clonedInput">
    <fieldset>
    <legend>Fabric Type:</legend>
    Select Fabric: <select name="fabrictype1">
    <option value="Booth Buddy" onClick="showHide('boothbuddy1');" />Booth Buddy</option>
    <option value="PopUp (LP Stretch)" onClick="showHide('popup1');" />PopUp (LP Stretch)</option>
    <option value="Retractable Banner Stand" onClick="showHide('bannerstand1');">Retractable Banner Stand</option>
    <option value="Hanging Sign" onClick="showHide('hangingsign1');">Hanging Sign</option>
    <option value="Delta Fabric" onClick="showHide('deltafabric1');">Delta Fabric</option>
    <option value="Table Throw" onClick="showHide('tablethrow1');">Table Throw</option>
    <option value="Table Skirt" onClick="showHide('tableskirt1');">Table Skirt</option>
    <option value="Companion" onClick="showHide('companion1');">Companion</option>
    <option value="Companion 2" onClick="showHide('companion21');">Companion 2</option>
    <option value="Stretch Case Wrap" onClick="showHide('casewrap1');">Stretch Case Wrap</option>
    <option value="Pole Pocket Banner" onClick="showHide('polepocket1');">Pole Pocket Banner</option>
    <option value="Custom" onClick="showHide('custom1');">Custom ...</option>
    </select>
    <div class="hideit 1" id="boothbuddy1" style="display:none;">
    <b>PolySuede - </b><span id="smalltext">Hemmed Edges, Pole pocket top and bottom, Grommets top and bottom.</span>
    </div>

    <div class="hideit 1" id="popup1" style="display:none;">
    <b>PolySuede - </b><span id="smalltext">Hemmed Edges, 2" Velcro sewn to edge.</span>
    </div>

    <div class="hideit 1" id="bannerstand1" style="display:none;">
    <b>Sierra, X-banner, L-Banner - </b><span id="smalltext">Janus - Hotknife Sides, Finished top and bottom.</span>
    </div>

    <div class="hideit 1" id="hangingsign1" style="display:none;">
    <b>Softknit - </b><span id="smalltext">Zipper(s) along top edges, Hemmed where needed</span>
    </div>

    <div class="hideit 1" id="deltafabric1" style="display:none;">
    <b>PolySuede - </b><span id="smalltext">Sewn silicone</span>
    <b>Tripoly - </b><span id="smalltext">Sewn silicone</span>
    </div>

    <div class="hideit 1" id="tablethrow1" style="display:none;">
    <b>PolySuede - </b><span id="smalltext">Hemmed around edges</span>
    <b>Softknit - </b><span id="smalltext">Hemmed around edges</span>
    </div>

    <div class="hideit 1" id="polepocket1" style="display:none;">
    <b>Material - </b><span id="smalltext">PolySuede</span>
    <b>Finishing - </b><span id="smalltext">Hemmed edges, pole pockets</span>
    <b>Specify - </b><span id="smalltext">Pole pocket size or pole diameter</span>
    </div>

    <div class="hideit 1" id="custom1" style="display:none;">
    <b>Material - </b><input type="text" name="fabcmaterial1" /><br />
    <b>Finishing - </b><input type="text" name="fabcfinishing1" /><br />
    <b>Finished - </b><input type="text" name="fabcfinished1" /><br />
    <b>Live Area - </b><input type="text" name="fabclivearea1" /><br />
    <b>Other Information:</b><br />
    <textarea name="fabccomments1" cols="70"></textarea>
    </div>
    </fieldset></div>

    <div>
    <input type="button" id="btnAdd" value="add another name" />
    <input type="button" id="btnDel" value="remove name" />
    </div>

    </form>

  12. J's Gravatar

    # Posted J on 10/10/09 6:52 PM

    Great post. You broke it down to the perfect level. Just what I was looking for.

    Thanks!

  13. Simon Griffee's Gravatar

    # Posted Simon Griffee on 10/13/09 5:42 AM

    Excellent stuff—thank you Charlie!

    One suggestion: Please considering removing the double-dashes in the URL for this post (…jQuery--Dynamically…).

    The double-dashes seem to break an HTML page when the link to this post is placed inside an HTML comment. I discovered this when crediting you for using the above!

    Cheers,
    Simon

  14. Charlie Griefer's Gravatar

    # Posted Charlie Griefer on 10/13/09 10:18 AM

    @Steve Snyder - You'll need to understand what the code is doing, and use appropriate naming conventions. All elements that are duplicated have a "1" after the element name/id. After using jQuery's clone(), you'd need to increment all of those to "2" (then to "3", etc). I worked up some sample code that I can post later on.

    @Simon - Heh. I suppose I can modify that thru the alias in BlogCFC's administrator. In the meantime, how about tinyurl? Either way, thanks for the credit :)

  15. Dan Fish's Gravatar

    # Posted Dan Fish on 10/14/09 7:03 AM

    Great howto. It's really helped a project of mine. I'm still kind of new to this, so I'm probably being dumb, but I'll like to put the remove button next to the added field so users have the option of removing that added line specifically. Any pointers?

  16. Alexia's Gravatar

    # Posted Alexia on 12/21/09 1:57 PM

    Hi!
    Very gooood work! But i have a question for u: How can i use your code and change also THE LABEL of the text box to Name2, Name3 etc...?
    Any ideas?
    Thank you

  17. Sunny's Gravatar

    # Posted Sunny on 1/7/10 3:00 AM

    Thank you so much for this post, I've been looking for a good explanation of dynamic forms with jQuery. I'm so relieved to find just what I needed that I'm going to give a way-too-personal explanation. I'm at a job where web-programming was suddenly thrown onto me even though I'm an electrical engineer. After a month of stressfully trying to work with php I'm somewhat up and running, actually kind of like web-programming. Anyway thanks for making my life easier! I'll buy you a beer if we ever meet :-)

  18. Snyder's Gravatar

    # Posted Snyder on 1/7/10 9:42 AM

    Hey, first of all thanks for the great tutorial. don't know if you check this post anymore, but was wondering how you would validate the newly created input fields just using php. I tried but when I submit and it fails my checks, it loses all the new input elements and I have to re-type everything. I guess I'm asking how you would make the newly created input fields "sticky". I know I could just do jQuery validation, but I want it to be functional without javascript in case someone doesn't have it enabled. food for thought. Thanks!

  19. Ivan Ribakov's Gravatar

    # Posted Ivan Ribakov on 1/25/10 3:06 PM

    Hi, thanks for the code - i modified it a bit to suit my needs, but faced one problem - the inputs that are created dynamically are not being submitted. This happens only in Firefox, but does work in Chrome and even IE.

    Here's HTML:

    <div id=\"input1\" class=\"clonedInput\">                     
       <label for=\"answer1\" class=\"answer_label\">Answer</label>
       <input type=\"text\" name=\"answer1\" id=\"answer1\" class=\"answer\" />
       <label for=\"answer1pt\" class=\"point_label\">Points</label>
       <input type=\"text\" name=\"answer1pt\" id=\"answer1pt\" class=\"points\" size=\"2\"/>                     
    </div>";

    /*Copied from PHP code that why quotes are backslashed*/

    And your modified JavaScript code:

    $('#btnAddAnswer').click(function() {
    var num      = $('.clonedInput').length;
    var newNum   = num + 1;   
    var newElem = $('#input' + num).clone().attr('id', 'input' + newNum);

       newElem.find(".answer_label").attr('for', 'answer' + newNum);
       newElem.find(".answer").attr('id', 'answer' + newNum).attr('name', 'answer' + newNum).attr('value', '');
       newElem.find(".point_label").attr('for', 'answer' + newNum + 'pt');
       newElem.find(".points").attr('id', 'answer' + newNum + 'pt').attr('name', 'answer' + newNum + 'pt').attr('value', '');
       
    $('#input' + num).after(newElem);
    $('#btnDelAnswer').attr('disabled','');
    if (newNum == 5)
    $('#btnAddAnswer').attr('disabled','disabled');
    });

    Any ideas what i'm doing wrong or what the trouble might be?

    P.S. dont know if it is still relevant, but you can get certain child of an element using this construction:
    newElem.find(":nth-child(1)").attr('for', 'answer' + newNum);

    Thanks in advance!

  20. Kliphten's Gravatar

    # Posted Kliphten on 2/1/10 12:01 PM

    Great post. Question: I used this for multiple image uploads but when I post the data, it doesn't go through? Anybody know if I need to do anything to get this to work? I've tried adding a text field dynamically as well and it didn't work either. Any help would be great!

  21. Gerald's Gravatar

    # Posted Gerald on 2/4/10 10:38 PM

    Great Post,
    I want to know If is it posible to validate the new input generated?

  22. doforumda's Gravatar

    # Posted doforumda on 2/17/10 10:08 PM

    This is a good post i like it but i have some problem with it. i modified your code according to my needs. it is working fine as far as adding new form elements is concern. but the problem arise when i try to submit my data to server side scripting. how can i get all the data that are added to all the dynamic fields in this script. let me give my code to you

    <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
    "http://www.w3.org/TR/html4/loose.dtd">;
    <html>
    <head>
    <title></title>
    <script src="http://ajax.googleapis.com/ajax/libs/jquery/1.3/jq...;

    <script type="text/javascript">
       
    $(document).ready(function() {
       
    $('#btnAdd').click(function() {
       
    var num = $('.clonedInput').length;

    var newNum = new Number(num + 1);

    var newElem = $('#input' + num).clone().attr('id', 'input' + newNum);

    newElem.children(':first').attr('id', 'name' + newNum).attr('name', 'name' + newNum);
    $('#input' + num).after(newElem);
    });
             
    });
    </script>
    <style>
    #myForm div {
       font-size:14px;
       margin-bottom: 10px;
       clear: left;
    }
    #myForm label {
       width: 125px;
       display: block;
       font-size:14px;
       font-weight: bold;
       color: #999;
       float: left;
    }
    #btnAdd {
       margin-left:130px;
    }
    </style>

    </head>

    <body>

    <form id="myForm" method="post" action="process.php">
       <div>
    <label>First Name: </label><input type="text" name="fname" id="fname"><br>
    </div>
    <div>
    <label>Last Name: </label><input type="text" name="lname" id="lname"><br>
    </div>
    <div id="input1" class="clonedInput">
    <label>IM Screen Names:</label>
    <input type="text" name="name1" id="name1" />
    <select name="screenname1" id="screenname1">
    <option value="AIM" selected="selected">AIM</option>
    <option value="gtalk">Google Talk</option>
    <option value="skype">Skype</option>
    <option value="windows live">Windows Live</option>
    <option value="yahoo">Yahoo</option>
    </select><br />
    </div>

    <div>
       <a href="#" id="btnAdd">Add another</a>
    </div>
    <input type="submit" name="submit" value="Submit">
    </form>

    </body>
    </html>

    let me give you server side script too so it will be better to understand it. please modify where am i making mistake

    <?php
    $fname = $_POST['fname'];
    $lname = $_POST['lname'];
    $name1 = $_POST['name1'];
    $screenname1 = $_POST['screenname1'];

    echo $fname."<br>";
    echo $lname."<br>";
    echo $name1."<br>";
    echo $screenname1."<br>";

    ?>

  23. Davidmoreen's Gravatar

    # Posted Davidmoreen on 3/13/10 12:50 PM

    Absolutely perfect. This post could have not of gotten anymore in depth, as well it saved me a few bucks from having to buy an application that incorporated this and viewing the source code. Very good post and I'll be coming back soon to se what other great things you have going on on this website! :-)

  24. George's Gravatar

    # Posted George on 4/8/10 1:45 AM

    Just to say thanks. Just what I needed. I had a label in front of my input so had to change your line 16 from targeting:
    children(':first')
    to:
    children('.myinput')

    If anyone is confused I did this so that the attribute manipulation is done on the child element with the class of "myinput" instead of the first child.

  25. Ajith's Gravatar

    # Posted Ajith on 4/8/10 11:51 PM

    Thanks Charlie.Excellent Article.After wasting my long 2 hours atlast i reached in front you.Very very thanks

  26. Ryan's Gravatar

    # Posted Ryan on 5/7/10 5:09 PM

    I was looking for a quick and easy solution to this and this helped me amazingly - however, I needed to add multiple childen (e.g. name1, email1, phone1) and didn't want to loop.

    I ended up using;
    newElem.children(':nth-child(2)').attr('id... etc.
    newElem.children(':nth-child(3)').attr('id... etc.

    As the number of fields will never change and I didn't want to loop this seemed to offer a quick and easy solution. Unless I'm missing something more complex about this.

    Thanks again for posting.

  27. Jennifer's Gravatar

    # Posted Jennifer on 5/10/10 10:03 AM

    This seems like a great solution. I tried implementing but I am having one small issue. I am cloning several fields including drop down lists. For the text boxes. The textboxes post as a comma delimited list and the dropdowns just post one selected value. It seems like they all have the same name even though they are unique. Since so many people seem to have gotten this working, I am wondering if there is something I am missing.

  28. john's Gravatar

    # Posted john on 6/15/10 5:26 PM

    i'm probably too late for this, but it's worth a try.

    this is wonderful, but i can't seem to get it to work within my existing page

    when i just extract the code, it works fine in my envron, but when i put it into my page, both buttons are grayed out

    why?

  29. john's Gravatar

    # Posted john on 6/22/10 12:44 PM

    to answer my own question, i removed all the other javascript on the page and it worked

    hence a conflict with the other junk i had

    thanks for the script!

  30. mommaroodles's Gravatar

    # Posted mommaroodles on 7/19/10 6:01 AM

    WOW, cant believe my luck! this seems to be just the script I'm looking for, however, I am a real newbie to Jquery and am having difficulty getting the script exactly to work as I'd like it.

    As in doforumda's comment posted on 2/17/10 10:08 PM - what I am trying to do is instead of just adding the one field is add all three fields. Fields being the following

    Boy/Girl - radio button
    Childs name - text field
    Childs birthdate - text field

    Can this be done and if so some advice on how to do this will be greatfully appreciated.

  31. simulation assurance auto's Gravatar

    # Posted simulation assurance auto on 7/26/10 10:25 PM

    Hey...i must say " simply professional "
    Good efforts...I am also working as a web designer as i am from art background.
    I am more interested in flash animations/ sites.

  32. Kris-I's Gravatar

    # Posted Kris-I on 7/30/10 12:53 AM

    Very cool sample. I'd like adapt it for my work. I'd like for each line have a delete button, after confirmation delete the line. Could you help me ? Thanks

  33. Hakan's Gravatar

    # Posted Hakan on 8/10/10 11:27 AM

    It doesn't works correctly if jquery ui 1.8 included to page. I didn't try other versions of ui.

  34. Hakan's Gravatar

    # Posted Hakan on 8/10/10 11:28 AM

    By the way, the add button works, but the delete button doesn't.

  35. zchef2k's Gravatar

    # Posted zchef2k on 8/11/10 3:09 PM

    I'd like to add on to the earlier question about the form elements being duplicated being <select> elements. In my code I have a the following (abbreviated) HTML layout:

    <div id="form1" class="clonedInput">
    <label>blah</label><select>//auto populating from php//</select>
    </div>

    While I can get this script to work and add/delete auto populating select elements, the adding of the name attribute is applying to the labels, not the selects themselves. Therefore, I am not able to POST any unique variables aside from the original select element.

    How can I get the 'name + 1' routine to apply to the select element, not the labels?

  36. Finau's Gravatar

    # Posted Finau on 8/15/10 7:20 PM

    Great work and thanks for sharing your work with us,
    thanks a lot

  37. azrain's Gravatar

    # Posted azrain on 9/1/10 11:35 AM

    hi,
    good tutorial. i really need it. however, just want to add, if you want to clear the value of cloned input element:

    line 14: var newElem = $('#input' + num).clone().attr('id', 'input' + newNum).val('');

    that's all.

  38. azrain's Gravatar

    # Posted azrain on 9/1/10 11:38 AM

    sorry wrong line. this is the correct one:

    line 16: newElem.children(':first').attr('id', 'name' + newNum).attr('name', 'name' + newNum).val('');

    sorry :)

  39. Raymond's Gravatar

    # Posted Raymond on 9/2/10 12:11 AM

    Great walk-thru, nicely done. However (uh oh), there are two issues with it (one minor and one less minor). First, the minor issue - when you clone a field,
    if it already has been filled in which would seem likely, then the clone carries with it the filled-in value. I think this could be easily rectified by simply blanking
    out the value of the newly created element after its creation (hence the "minor" part). The more problematic issue I see, and I've seen this with more than one
    other similar script and one jQuery plugin that attempts to package this behavior is the way element names are addressed. In a backend/server-side language
    that would process the results of a POST or GET from your form, the simple and expected behavior (I think) would be for elements that are essentially multiples
    or clones of one another to have different IDs for referencing purposes but the same name, and have the name include '[]' to indicate that the field is an array.

    So, instead of winding up with $_POST['name1'], and $_POST['name2'], etc. (in PHP, for example), you would have $_POST['name'][0], $_POST['name'][1],
    $_POST['name'][2], and so on, which greatly simplifies processing the output (all the $_POST['name'][??] elements can be handled together as one unit, looping
    through them, instead of having to inspect each $_POST[] variable's name and see if it includes a substring called 'name' which is what would be required
    with the present scheme. Thoughts?

  40. Raymond's Gravatar

    # Posted Raymond on 9/2/10 12:56 AM

    Feeling guilty about being so lazy (above). So, I decided to give it a go
    and realized both issues referenced above can be addressed with fairly
    unobtrusive changes to just two lines of your rather cool code:

    Change line 16 from:
    newElem.children(':first').attr('id', 'name' + newNum).attr('name', 'name' + newNum);
    To:
    newElem.children(':first').attr('id', 'name' + newNum).val('');

    Change line 43 from:
    Name: <input type="text" name="name1" id="name1" />
    To:
    Name: <input type="text" name="name[]" id="name1" />

    To test this I've set the form's method to "post", added a "submit" button to the form,
    and placed the following PHP excerpt at the very top of the page to simply display
    the values posted from the form:

    <?php
    if (isset($_POST['submit'])) {
    echo "<p>Results of form submission:</p>\n";
    echo "<pre>";
    print_r($_POST);
    echo "</pre>\n";
    echo "<hr /><p><a href='" . basename(__FILE__) . "'>Reload Charlie's Very Cool Form</a></p>\n";
    die();
    }
    ?>

    Now, to make the newly added fields auto-focus upon creation... perhaps something to save for
    the next go-round. Hope someone finds the above minor suggestions useful! Again, great work!

Add Comment