Copy a Form
Objective: Click a button to duplicate a form.
Solution: Utilized cloneNode()
and appendChild()
methods
Issues: OP's function in general was not nearly adequate enough to handle objective. Instead of going down a list of what was wrong, we will continue to explore the solution and attempt to highlight certain areas where there were discrepancies.
HTML
#id
: OP's code had no occurrence of duplicated ids but I'm mentioning this because it's a common mistake seen very frequently. When duplicating HTML it's important to always remember that there should never be any duplicated ids
.
name
: This attribute is used extensively by form elements and is essential when used with <input type="radio">
. For each group of radio buttons we should always assign one name
to each radio button. This insures that only one radio button is selected out of the group.
Tip: When assigning id
and name
to <form>
and form elements, it's easier just to make them both the same value (with the exception of radio buttons). For a short unbiased explanation see this post. There are varying opinions on this topic, my take is if you use the same name
and id
on each form element, then you have every option available and you don't have to worry whether you used the correct one or not. Keep in mind, if at all possible, try keeping id
and name
simple yet functional especially if you intend to copy and number them.
CSS
Assuming this is only a draft, and not a finished project, we won't go into the minutiae of styling. Note, I have changed the CSS significantly, so feel free to ask or critique.
JS
I couldn't really consider OP's code as copying a form, it's more like an extension:
Result:
// up to 34 extra identical inputs that don't match up to the original form.
<br><input type='text' name='myInputs[]'>
Not very useful. Here's a breakdown of what I've seen on the subject of duplicating forms:
The common way of using JS to duplicating a form is by using the methods cloneNode()
and appendChild()
.
By default cloneNode()
is actually cloneNode(false)
the argument is a boolean that determines how deep the cloning is. If set to false or not at all, cloneNode()
will result in a shallow copy--all attributes, value, and no children. Set to true, will result in a deep copy--all descendants, attributes, and values--everything (with one exception--being event listeners/binding will not be copied).
Since all attributes are copied, that would also mean id
as well. We must make provisions for this by way of collecting every element in the clone and assigning each one a unique id
. The demo provides this by means of using the following:
Demo
There are step by step comments in the JS. I left out some details because I don't want to put you to sleep. Be aware this is not the only way to achieve your objective, this is one of many.
Plunker
Snippet
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<title>35517008</title>
<style>
* { vertical-align: baseline; }
h2 { text-align: center; }
.center { margin: 0 auto; }
fieldset { margin: 0 auto; width: 50%; }
fieldset div { float: left; clear: left; }
#btn1 { position: relative; left: 65%; }
input[type="submit"] { float: right; }
input { margin: 0 5px 5px 0; }
label { padding: 0 0 5px 5px; }
input + input { margin-left: 10px; }
</style>
</head>
<body>
<h2>Driver Line Input Page</h2>
<input id="btn1" class="center" type="button" value="Add Additional Trip" />
<form id="line1" name="line1" method="post" class="center">
<fieldset>
<legend>Line 1</legend>
<div>
<label>
<input type="radio" name="county" value="radio" id="eagle" />
Eagle County
<input type="radio" name="county" value="radio" id="summit" />
Summit County
</label>
</div>
<div>
<label for="driver">Driver Name:
<input name="driver" id="driver" type="text" size="25" />
</label>
</div>
<div>
<label for="time">Trip Time:
<input name="time" id="time" type="text" size="5" />
</label>
<label for="pickup">First Pickup:
<input name="pickup" id="pickup" type="text" size="5" />
</label>
<label for="city">City:
<input name="ciy" id="city" type="text" size="25" />
</label>
</div>
<div>
<label>Transfer Enroute:
<input type="radio" name="trans" id="yes" />
Yes
<input type="radio" name="trans" id="no" checked />
No
</label>
<label for="tDriver">Transfer Driver:
<input name="tDriver" id="tDriver" />
</label>
</div>
<div>
<label>Carseat/Booster Request:
<input type="radio" name="child" id="seat" />
Carseat
<input type="radio" name="child" id="booster" />
Booster
<input type="radio" name="child" id="none" checked />
None
</label>
</div>
<input type="submit" id="submit" name="submit"/>
</fieldset>
<br />
<br />
</form>
<script>
/* When the "Add Additional Trip" button is clicked ... */
var btn1 = document.getElementById('btn1');
btn1.addEventListener('click', function(e) {
// Collect all forms into a NodeList
var forms = document.querySelectorAll('form');
// Total number of forms and the new serial number
var qty = forms.length;
var step = (qty + 1).toString();
// The original form
var template = forms[0];
// Make a clone of the original form
var dupe = template.cloneNode(true);
// Reference the clone's displayed title
var line = dupe.querySelector('legend');
// Set clone's title to Line {{number}}
line.innerHTML = "Line "+ step;
// Set clone's id and name to form{{number}}
dupe.setAttribute('id', 'line'+ step);
dupe.setAttribute('name', 'line'+ step);
// Collect all of clone's inputs into a NodeList
var inputs = dupe.querySelectorAll('input');
// Convert the NodeList into an Array
var inpArray = Array.prototype.slice.call(inputs);
// Iterate through the Array and assign a unique id and name to each one
for(var i = 0; i < inpArray.length; i++) {
var inpID = inpArray[i].getAttribute('id');
var inpName = inpArray[i].getAttribute('name');
inpArray[i].setAttribute('id', inpID + step);
inpArray[i].setAttribute('name', inpName + step);
}
// Place clone after the last form
document.body.appendChild(dupe);
}, false);
</script>
</body>
</html>