Loading...
 

No Food for Thought

Duplicate form sections with jQuery - Improved

admin Friday May 27, 2016

Based on David Carr's excessively simple technique to duplicate form sections using JavaScript, here is my enhanced but still minimalist take, adding support for:

  1. select multiple
  2. starting the form with several sections already
  3. easy backend usage


This changes indices so they start at 0.
Image Duplicate Form Sections

//define template
var template = $('#sections .section:first').clone();

//define counter
var sectionsCount = jQuery('div.section:last').data('number');

function incrementIdentifier(id, sectionsCount) {
	if (id.substr(-1) == '0') {
		id = id.substr(0, id.length - 1);
	}
	return id + sectionsCount;
}

//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
		newId = incrementIdentifier(this.id, sectionsCount);
		
		//update for label
		$(this).prev().attr('for', newId);
		
		//update id
		this.id = newId;
		
		var baseName = this.name;
		var array = false;
		if (baseName.substr(-2) == '[]') {
			array = true;
			baseName = baseName.substr(0, baseName.length - 2);
		}
		baseName = incrementIdentifier(baseName, sectionsCount);
		if (array) {
			baseName = baseName + '[]';
		}
		this.name = baseName;
	}).end()
	
	//inject new section
	.appendTo('#sections');
	
	jQuery('input[name=lastSection]').val(sectionsCount);
	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;
});


As a bonus, the PHP function I quickly crafted to ease processing the resulting $_REQUEST:

<?php
/**
 * Treat $_REQUEST when working with duplicate-form-sections.js
 * $_REQUEST['numberedParameters'] contains the base name of the parameters to be converted into arrays
 * $_REQUEST['lastSection'] is the number of the last section (the number of sections minus 1)
 * Alters neither $_GET nor $_POST
 * @author Philippe Cloutier
 * @license MIT
 * @return boolean false on failure
 */
function numberedParametersToArrays() {
	if (! isset($_REQUEST['lastSection'])) {
		return false;
	}
	$numberedParameters = $_REQUEST['numberedParameters'];
	$lastSection = $_REQUEST['lastSection'];
	foreach ($numberedParameters as $numberedParameter) {
		$_REQUEST[$numberedParameter] = array();
		for ($i = 0; $i <= $lastSection; $i++) {
			if (isset($_REQUEST[$numberedParameter . $i])) {
				$_REQUEST[$numberedParameter][] = $_REQUEST[$numberedParameter . $i];
			} else {
				$_REQUEST[$numberedParameter][] = array();
			}
			// We could unset the numbered parameters.
		}
	}
	return true;
}
?>


And here is an adjusted usage example:

<form>

<div id="sections">
  <div class="section" data-number="0">
    <fieldset>
        <legend>User</legend>

        <p>
            <label for="firstName">First Name:</label>
            <input name="firstName0" id="firstName0" value="" type="text" />
        </p>

        <p>
            <label for="lastName">Last Name:</label>
            <input name="lastName0" id="lastName0" value="" type="text" />
        </p>

        <p>
            Friends:
            <select name="friends1[]" id="friends1" size="2" multiple>
                <option>Alice</option>
                <option>Bob</option>
            </select>
        </p>

        <p><a href="#" class='remove'>Remove Section</a></p>

    </fieldset>
  </div>
</div>

<p><a href="#" class='addsection'>Add Section</a></p>
<input type="hidden" name="numberedParameters[]" value="firstName">
<input type="hidden" name="numberedParameters[]" value="lastName">
<input type="hidden" name="numberedParameters[]" value="friends">
<input type="hidden" name="lastSection" value="0">
</form>


Note that there are similar solutions elsewhere. I am not saying this one is better than these.