jQuery – Dynamically Adding Form Elements

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: