http://www.quirksmode.org/dom/domform.html
I am trying to implement this extend form function in my project. Since I am compiling with CakePHP naming convention, in my extend form I have 2 field names:
[Student][0][age]
[Student][0][grade]
The script doesn't work because it is appending the counter only at the end of a field name all alike like this: fieldName + counter, whereas I am trying to increment, as you might have guessed it, like this:
[Student][1][age]
[Student][1][grade]
[Student][2][age]
[Student][2][grade]
I am new to Javascript and hopefully someone can advise how to work this out.
HTML:
<span id="readroot" style="display: none">
<input class="btn btn-default" type="button" value="Remove review" onclick="this.parentNode.parentNode.removeChild(this.parentNode);" />
<br /><br />
<div class="row">
<div class="col-lg-3">
<div class="form-group required"><label for="Student1Age">Age</label><input name="data[Student][0][age]" class="form-control" placeholder="Age" maxlength="11" type="text" id="Student1Age" required="required"/></div>
</div>
<div class="col-lg-3">
<div class="form-group required">
<label for="Student1Grade">級別</label>
<select name="data[Student][0][grade]" class="form-control" id="Student1Grade" required="required">
<option value="">Please Select</option>
<option value="1">Grade 1</option>
<option value="2">Grade 2</option>
</select>
</div>
</div>
</div></span>
<span id="writeroot"></span><input class="btn btn-default" type="button" onclick="moreFields()" value="Give me more fields!" />
Javascript:
var counter = 0;
function moreFields() {
counter++;
var newFields = document.getElementById('readroot').cloneNode(true);
newFields.id = '';
newFields.style.display = 'block';
var newField = newFields.childNodes;
for (var i=0;i<newField.length;i++) {
var theName = newField[i].name
if (theName)
newField[i].name = theName + counter;
}
var insertHere = document.getElementById('writeroot');
insertHere.parentNode.insertBefore(newFields,insertHere);
}
You have to replace this one line like follows.
Yours:
newField[i].name = theName + counter;
New one:
newField[i].name = "[Student][" + counter + "][" + theName + "]";
I made a working sample page out of your script. Check the following link.
http://sugunan.net/demo/extend_form.php
Related
I'm currently trying to create a form that will extend on click using the method shown from this site. http://www.quirksmode.org/dom/domform.html
I have the Extending of the form more or less working fine, My issue is getting the Date field to correctly step up from the date that the user inputs in the first field.
Right now if I click on Add another Date, it adds a day to 1970-01-01 which i'm assuming is a default start date somewhere.
I'm not familiar enough with javascript to reference the generated date to the date that is initially selected by the User.
Here is a fiddle link of you want to see what I mean. https://jsfiddle.net/2nvz6kqj/9/
Note i'm pretty sure you can only get the date field to show up in Chrome correctly.
And here is my code.
<script type="text/javascript">
var counter = 0;
function moreFields() {
var date = document.getElementById("myDate").value;
counter++;
var newFields = document.getElementById("readroot").cloneNode(true);
newFields.id = '';
newFields.style.display = 'block';
var newField = newFields.childNodes;
for (var i=0;i<newField.length;i++) {
var theName = newField[i].name;
if (theName)
newField[i].name = theName + counter;
}
var insertHere = document.getElementById("writeroot");
insertHere.parentNode.insertBefore(newFields,insertHere);
document.getElementById("myDate").stepUp(1);
}
window.onload = moreFields;
</script>
<body>
<div id="readroot" style="display: none">
<input type="button" value="Remove Date"
onclick="this.parentNode.parentNode.removeChild(this.parentNode);" /><br /><br />
<input type="date" id="myDate" value="">
<select name="rankingsel">
<option>School Day</option>
<option value="day1">Day 1</option>
<option value="day2">Day 2</option>
<option value="day3">Day 3</option>
<option value="day4">Day 4</option>
<option value="day5">Day 5</option>
<option value="closed">School Closed</option>
</select><br /><br />
</div>
<form method="post" action="/cgi-bin/show_params.cgi">
<span id="writeroot"></span>
<input type="button" onclick="moreFields()" value="Add Another Date" />
<input type="submit" value="Send form" />
</form>
</body>
Ultimately once i get this working correctly, I'll submit it to a DB with PHP.
Ok, I changed a few things, but this should work for you.
NOTE: In order to keep the code and markup close to what you had, I didn't "fix" some of the JavaScript and HTML that I consider to be bad practice.
I also took some liberties with the readroot element IDs and writeroot is now a DIV not a SPAN as you had it.
<html>
<head>
<script type="text/javascript">
var counter = 0;
var date = new Date();
// this function adds a day to the date variable
function addDay (dateObj) {
var ticks = dateObj.getTime();
var newTicks = ticks + (24 * 60 * 60 * 1000);
dateObj.setTime(newTicks);
}
function moreFields() {
//var dateVal = document.getElementById("myDate").value;
counter++;
// querySelector is nicer way to get your elements
var newFields = document.querySelector("#readroot").cloneNode(true);
newFields.id = newFields.id + counter;
newFields.style = null;
// nifty way to convert node list into an array
var fields = Array.prototype.slice.call(newFields.childNodes);
// now use forEach instead of old-style for loop
fields.forEach(function (f,i,nodes) {
var theName = f.name;
if (theName) {
f.id = theName + counter;
}
});
// convert "writeroot" to div and just append the newFields container
var form = document.querySelector('#writeroot');
form.appendChild(newFields);
// to set the date, you need to use an ISO-format date string
var dateFields = form.querySelectorAll('[name="myDate"]');
var len = dateFields.length;
var dateField = dateFields[len - 1];
if (dateField) {
var dateISO = date.toISOString().substr(0,10);
dateField.value = dateISO;
addDay(date);
}
}
window.onload = moreFields;
</script>
</head>
<body>
<div id="readroot" style="display: none">
<input type="button" value="Remove Date"
onclick="this.parentNode.parentNode.removeChild(this.parentNode);" /><br /><br />
<input type="date" name="myDate">
<select name="rankingsel">
<option>School Day</option>
<option value="day1">Day 1</option>
<option value="day2">Day 2</option>
<option value="day3">Day 3</option>
<option value="day4">Day 4</option>
<option value="day5">Day 5</option>
<option value="closed">School Closed</option>
</select><br /><br />
</div>
<form method="post" action="/cgi-bin/show_params.cgi">
<div id="writeroot"></div>
<input type="button" onclick="moreFields()" value="Add Another Date" />
<input type="submit" value="Send form" />
</form>
</body>
</html>
I am implementing an extend form function, in which there is a label text (marked in html) that I hope to include a number to increment.
So every time when a form is extended/cloned, the label text in the extended form shows Student 1, Student 2... accordingly. Can I be advised how to do that?
var counter = 0;
function moreFields(val1, val2, val3) {
counter++;
var newField = document.getElementById(val1).cloneNode(true);
newField.id = '';
newField.style.display = 'block';
var newFields = newField.querySelectorAll('[name], [id], [for]');
for (var i=0;i<newFields.length;i++) {
var theNames = newFields[i].name
if (theNames)
newFields[i].name = "data[" + val3 + "][" + counter + "][" + theNames + "]";
var theNames2 = newFields[i].id;
if (theNames2)
newFields[i].id = theNames2 + counter;
var theNames3 = newFields[i].htmlFor;
if (theNames3)
newFields[i].htmlFor = theNames3 + counter;
}
var insertHere = document.getElementById(val2);
insertHere.parentNode.insertBefore(newField,insertHere);
}
<span id="readroot" style="display: none">
<div class="row">
<div class="col-lg-3">
Grade 6
</div>
<div class="col-lg-3">
Male
</div>
<div class="col-lg-6">
<div class="row">
<div class="col-lg-12">
<!-- Student number needs to increase when a new form is extended. -->
<label>Student 1</label>
</div>
</div>
<div class="row">
<div class="col-lg-6">
<label for="display_student_1_grade">Grade</label>
<div id="display_student_1_grade"></div>
</div>
<div class="col-lg-6">
<label for="display_student_1_gender">Gender</label>
<div id="display_student_1_gender"></div>
</div>
</div>
</div>
</div>
<input class="btn btn-default" type="button" value="Remove" onclick="this.parentNode.parentNode.removeChild(this.parentNode);" />
</span>
<span id="writeroot"></span>
<input class="btn btn-default" type="button" onclick="moreFields('readroot', 'writeroot', 'Student')" value="One More Student" />
To answer the question directly, you just need to find the appropriate <label> and update the innerHTML (see below).
However, what you are doing here can be achieved using the new HTML5 template element without having to hide <span> elements. Additionally, you have to remember that when you remove a student, the counter isn't going to decrease, nor are the already added students going to update. If you want that kind of functionality, you may want to look into a Javascript MVVM, like Angular.
var counter = 0;
function moreFields(val1, val2, val3) {
counter++;
var newField = document.getElementById(val1).cloneNode(true);
newField.id = '';
newField.style.display = 'block';
// Find the label here and update the innerHTML appropriately
newField.querySelector(".col-lg-12 label").innerHTML = "Student " + counter;
var newFields = newField.querySelectorAll('[name], [id], [for]');
for (var i=0;i<newFields.length;i++) {
var theNames = newFields[i].name
if (theNames)
newFields[i].name = "data[" + val3 + "][" + counter + "][" + theNames + "]";
var theNames2 = newFields[i].id;
if (theNames2)
newFields[i].id = theNames2 + counter;
var theNames3 = newFields[i].htmlFor;
if (theNames3)
newFields[i].htmlFor = theNames3 + counter;
}
var insertHere = document.getElementById(val2);
insertHere.parentNode.insertBefore(newField,insertHere);
}
<span id="readroot" style="display: none">
<div class="row">
<div class="col-lg-3">
Grade 6
</div>
<div class="col-lg-3">
Male
</div>
<div class="col-lg-6">
<div class="row">
<div class="col-lg-12">
<!-- Student number needs to increase when a new form is extended. -->
<label>Student 1</label>
</div>
</div>
<div class="row">
<div class="col-lg-6">
<label for="display_student_1_grade">Grade</label>
<div id="display_student_1_grade"></div>
</div>
<div class="col-lg-6">
<label for="display_student_1_gender">Gender</label>
<div id="display_student_1_gender"></div>
</div>
</div>
</div>
</div>
<input class="btn btn-default" type="button" value="Remove" onclick="this.parentNode.parentNode.removeChild(this.parentNode);" />
</span>
<span id="writeroot"></span>
<input class="btn btn-default" type="button" onclick="moreFields('readroot', 'writeroot', 'Student')" value="One More Student" />
I have a multistep form in my project, the first step of which has a javascript extend field function where a user can add additional fields. If the user goes to the next step, the data is of course stored in Session until final submission. However, the extended form will not be remembered. If the user goes back from next step the additional field and its data is not displayed, worse still, if the data in the additional field is invalid, the user won't see it, as the message is displayed underneath the disappeared field (I'm using Cakephp default validation).
Is there a way, like storing the setting in session, to make it stay?
var counter = 0;
function moreFields(val1, val2, val3) {
counter++;
var newField = document.getElementById(val1).cloneNode(true);
newField.id = '';
newField.style.display = 'block';
var newFields = newField.querySelectorAll('[name], [id], [for], [data-display]');
for (var i=0;i<newFields.length;i++) {
var theNames = newFields[i].name
if (theNames)
newFields[i].name = "data[" + val3 + "][" + counter + "][" + theNames + "]";
var theNames2 = newFields[i].id;
if (theNames2)
newFields[i].id = theNames2 + counter;
console.log(newFields[i].id);
var theNames3 = newFields[i].htmlFor;
if (theNames3)
newFields[i].htmlFor = theNames3 + counter;
//console.log(newFields[i].htmlFor);
var theNames4 = newFields[i].dataset.display;
if (theNames4)
newFields[i].dataset.display = theNames4 + counter;
console.log(newFields[i].dataset.display);
}
var insertHere = document.getElementById(val2);
insertHere.parentNode.insertBefore(newField,insertHere);
}
<span id="readroot" style="display: none">
<div class="row">
<div class="col-lg-6">
<div class="form-group required">
<label for="Student1Grade">Grade</label>
<select name="grade" data-display="#display_student_1_grade" class="form-control" id="Student1Grade" required="required">
<option value="">Please Select</option>
<option value="1">Grade 1</option>
<option value="1">Grade 2</option>
</select>
</div>
</div>
<div class="col-lg-6">
<div class="form-group">
<label for="Student1Gender">Gender</label>
<select name="gender" data-display="#display_student_1_gender" class="form-control" id="Student1Gender">
<option value="1">Male</option>
<option value="2">Female</option>
</select>
</div>
</div>
</div>
<input class="btn btn-default" type="button" value="Remove" onclick="this.parentNode.parentNode.removeChild(this.parentNode);" /><br /><br />
</span>
<span id="writeroot"></span>
<input class="btn btn-default" type="button" onclick="moreFields('readroot', 'writeroot', 'Student')" value="Add Field" />
You need to implement either sessionStorage or localStorage for your page.
localStorage - stores data with no expiration date
sessionStorage - stores data for one session (data is lost when the browser tab is closed)
Here I am just posting sample code for your help through which u can do your desire task. You just need to follow the logical concept from below code and implement in your code.
There is a sample also which is only containing id but you can implement it in other way which can full fill your task.
click here
I am still attempting to create blank fields that users can add on the fly. I am attempting to clone the following hidden template but I can't get it to add.
My HTML
<div class="_100">
<fieldset id="FieldBlank">
<div id="readroot" class="hidden">
<div class="_100">
<div class="_50"> QA Sample ID:<input type="text" id="QASampleID" name="QASampleID"></div>
<div class="_50" data-role="controlgroup" data-mini="true" data-type="horizontal">
<label>Collection Method</label><br />
<input type="radio" id="radGrab1" value="Grab" name="Collection1" />
<label for="radGrab1">Grab</label>
<input type="radio" id="radEWI1" value="EWI" name="Collection1" />
<label for="radEWI1">EWI</label></div>
</div>
<div class="_100">
<div class="_40">
<label class="analysis-label" for="analysis">Analyte:</label>
<select class="analysis" id="analysis" name="analysis" data-iconpos="left" data-icon="grid">
<option>Select</option>
<option value = "TN">TN</option>
<option value = "TP,NO2+3">TP,NO2+3</option>
</select></div>
<div class="_30">
<label class="preserve-label" for="preserve">Preserved</label>
<select class="select_preserve" id="preserve" name="preserve" data-iconpos="left" data-icon="grid">
<option>Select</option>
<option value = "HNO3">HNO₃</option>
<option value = "H2SO4">H₂SO₄</option>
</select></div>
<div class="_30">
<label class="cool-label" for="cool">Cooled</label>
<select class="select_cool" id="cool" name="cool" data-iconpos="left" data-icon="grid">
<option>Select</option>
<option value = "Ice">Ice</option>
<option value = "Frozen">Frozen</option>
<option value = "None">None</option>
</select></div>
</div>
</div>
</fieldset>
</div>
<button type="button" data-theme="b" data-icon="plus" id="moreFields" onclick="moreFields()">ADD FIELD BLANK</button>
<hr /><div id="writeroot"> </div>
My javascript
var counter = 0;
function moreFields() {
counter++;
var newFields = document.getElementById('readroot').cloneNode(true);
newFields.id = '';
newFields.style.display = 'block';
var newField = newFields.childNodes;
for (var i = 0; i < newField.length; i++) {
var theName = newField[i].name
if (theName)
newField[i].name = theName + counter;
}
var insertHere = document.getElementById('writeroot');
insertHere.parentNode.insertBefore(newFields,insertHere);
}
I just cannot get it to work! Is it because this script is not jquery? It doesn't seem to be because it is hidden because when I unhide it the button still doesn't work to add the clones? I am so pulling my hair out right now. I need you help!
Have you tried something like:
$("#readroot input").clone().appendTo("body"); // Or wherever you want to append them to
And, yeah, don't hide stuff manually. Use jQuery's toggle function instead:
$("#readroot").toggle(); // Or hide()
I have a rather long complicated form to be used to record field data. One section of the form requires the ability to add a "Field Blank" section with no data recorded AND the ability to add a duplicate section where everything (including the data from the field blank section) is duplicated. The only different thing that would change on the duplicate would be the SampleID
The field blank script
<script>
var counter = 0;
function moreFields() {
counter++;
var newFields = document.getElementById('readroot').cloneNode(true);
newFields.id = '';
newFields.style.display = 'block';
var newField = newFields.childNodes;
for (var i=0;i<newField.length;i++) {
var theName = newField[i].name
if (theName)
newField[i].name = theName + counter;
}
var insertHere = document.getElementById('writeroot');
insertHere.parentNode.insertBefore(newFields,insertHere);
}
</script>
The duplicate script
<script>
$(document).ready(function(){
var template_index=0;
$("#add_FieldDup").click(function(){
template_index++;
$(this).parent().before($("#template").clone().attr("id","template" + template_index));
$("#template" + template_index).css("display","inline");
$("#template" + template_index + " :input").each(function(){
$(this).attr("name",$(this).attr("name") + template_index);
$(this).attr("id",$(this).attr("id") + template_index);
});
$("#remove_Dup" + template_index).click(function(){
$(this).closest("fieldset").remove();
});
});
});
</script>
My form
<h3>Water Samples</h3>
<fieldset id="template"> <!--for the duplicate-->
<div id="template"> <!--for the duplicate-->
Sample ID: <input type="text" id="SampleID" name="SampleID"></div>
<div class="_40" data-role="controlgroup" data-mini="true" data-type="horizontal">
<label> Collection Method</label><br />
<input type="radio" id="radGrab" value="grab" name="Collection" />
<label for="radGrab">Grab</label>
<input type="radio" id="radEWI" value="EWI" name="Collection" />
<label for="radEWI">EWI</label>
</div>
<fieldset> <!--For the field blank-->
<div id="readroot" class="hidden"> <!--For the field blank-->
QA Sample ID:<input type="text" id="QASampleID" name="QASampleID">
<div class="_30" data-role="controlgroup" data-mini="true" data-type="horizontal">
<label>Collection Method</label><br />
<input type="radio" id="radGrab1" value="Grab" name="Collection1" />
<label for="radGrab1">Grab</label>
<input type="radio" id="radEWI1" value="EWI" name="Collection1" />
<label for="radEWI1">EWI</label></div>
</div>
<label class="analysis-label" for="analysis">Analyte:</label>
<select class="analysis" id="analysis" name="analysis" data-iconpos="left" data-icon="grid">
<option>Select</option>
<option value = "TN">TN</option>
<option value = "TP,NO2+3">TP,NO2+3</option>
</select>
<label class="preserve-label" for="preserve">Preserved</label>
<select class="select_preserve" id="preserve" name="preserve" data-iconpos="left" data-icon="grid">
<option>Select</option>
<option value = "HNO3">HNO₃</option>
<option value = "H2SO4">H₂SO₄</option>
</select>
<label class="cool-label" for="cool">Cooled</label>
<select class="select_cool" id="cool" name="cool" data-iconpos="left" data-icon="grid">
<option>Select</option>
<option value = "Ice">Ice</option>
<option value = "Frozen">Frozen</option>
<option value = "None">None</option>
</select>
</div> <!--Fieldblank-->
</fieldset> <!--Fieldblank -->
<hr /><span id="writeroot"></span>
</div> <!--duplicate template-->
</fieldset> <!--duplicate template-->
<button type="button" data-theme="b" data-icon="plus" id="moreFields" onclick="moreFields()">ADD FIELD BLANK</button>
<button type="button" data-theme="b" data-icon="plus" id="add_FieldDup">ADD FIELD DUP</button>
I got the duplicate to work (when the field blank wasn't hidden) but I cannot get the field blank to work. Any assistance would be greatly appreciated!
You should extract your javascript from your dom, put it in separate js and make function work in any of this blocks, create class="template" block and inside give any needed interaction buttons and inputs classes so there can exist multiple parent elements with same inside structure... then cloning does not get in a way of your interaction logic.
In js go like:
$(document).ready(function() {
$(".template .interaction_button_one").on('click', function(){
//... here you go traversing like
$(this).parents('.template').find('.some_important_div').show();
})
});
Notice .on(), this is delegation, and it will give same interaction bindings to all of your elements.