Table of Contents
When working with forms it can be incredibly useful to duplicate parts of the form, for instance when creating invoices the line items span multiple lines being able to add new lines as needed.
This tutorial demonstrates taking a div and cloning everything inside.
Demo: https://demos.dcblog.dev/duplicate-form-sections/
Download: https://github.com/daveismynamecom/duplicate-form-sections
To start I have this folder structure:
/index.html
/js/jquery.js
/js/app.js
jquery.js contains version v1.11.3.
Inside index.html include the js files:
<script src="js/jquery.js"></script>
<script src="js/app.js"></script>
index.html
A div with an id of sections wraps the elements, the div with a class of sections will be cloned every time the add section link is pressed.
The for labels and id’s in the input’s will end with a number for each new section this ensured the id is always unique. To remove a section another a link is used the important part is the class called remove, when clicked will call a js function inside app.js.
<form>
<div id="sections">
<div class="section">
<fieldset>
<legend>User</legend>
<p>
<label for="firstName">First Name:</label>
<input name="firstName[]" id="firstName" value="" type="text" />
</p>
<p>
<label for="lastName">Last Name:</label>
<input name="lastName[]" id="lastName" value="" type="text" />
</p>
<p><a href="#" class='remove'>Remove Section</a></p>
</fieldset>
</div>
</div>
<p><a href="#" class='addsection'>Add Section</a></p>
</form>
app.js
First define the template this is the main div with an id of sections. Using clone to clone the first section division, this ensures new sections originate from the original section. Also define a counter to be used for all incrementing the for labels and id’s.
//define template
var template = $('#sections .section:first').clone();
//define counter
var sectionsCount = 1;
Next when a link is pressed inside the body element with a class of addsection activate this function.
The counter is increased the each input is looped through, the for lable and id’s have the same name as the original section. They are changed by assigning them the newId variable. Finally the new section is injected to the end of the sections div.
//add new section
$('body').on('click', '.addsection', function() {
//increment
sectionsCount++;
//loop through each input
var section = template.clone().find(':input').each(function(){
//set id to store the updated section number
var newId = this.id + sectionsCount;
//update for label
$(this).prev().attr('for', newId);
//update id
this.id = newId;
}).end()
//inject new section
.appendTo('#sections');
return false;
});
To remove an existing section a link with a class of remove inside the sections div should be clicked. First fade out the container the 300 sets the speed of the fade. The section is inside of 2 devs they both need to be removed, this is done using parent() twice then call empty.
//remove section
$('#sections').on('click', '.remove', function() {
//fade out section
$(this).parent().fadeOut(300, function(){
//remove parent element (main section)
$(this).parent().parent().empty();
return false;
});
return false;
});
Complete file:
//define template
var template = $('#sections .section:first').clone();
//define counter
var sectionsCount = 1;
//add new section
$('body').on('click', '.addsection', function() {
//increment
sectionsCount++;
//loop through each input
var section = template.clone().find(':input').each(function(){
//set id to store the updated section number
var newId = this.id + sectionsCount;
//update for label
$(this).prev().attr('for', newId);
//update id
this.id = newId;
}).end()
//inject new section
.appendTo('#sections');
return false;
});
//remove section
$('#sections').on('click', '.remove', function() {
//fade out section
$(this).parent().fadeOut(300, function(){
//remove parent element (main section)
$(this).parent().parent().empty();
return false;
});
return false;
});