jquery clone form fields and increment id - javascript

I have a block of form elements which I would like to clone and increment their ID's using jQuery clone method. I have tried a number of examples but a lot of them only clone a single field.
My block is structured as such:
<div id="clonedInput1" class="clonedInput">
<div>
<div>
<label for="txtCategory" class="">Learning category <span class="requiredField">*</span></label>
<select class="" name="txtCategory[]" id="category1">
<option value="">Please select</option>
</select>
</div>
<div>
<label for="txtSubCategory" class="">Sub-category <span class="requiredField">*</span></label>
<select class="" name="txtSubCategory[]" id="subcategory1">
<option value="">Please select category</option>
</select>
</div>
<div>
<label for="txtSubSubCategory">Sub-sub-category <span class="requiredField">*</span></label>
<select name="txtSubSubCategory[]" id="subsubcategory1">
<option value="">Please select sub-category</option>
</select>
</div>
</div>
Obviously elements are lined up a lot better but you get the idea.
I would like to keep the id structure i.e. category1, subcategory1 etc as I use these to dynamically display select options based on the parent selection so if its possible to have each cloned block like category1/category2/category3 etc that would be great.

HTML
<div id="clonedInput1" class="clonedInput">
<div>
<label for="txtCategory" class="">Learning category <span class="requiredField">*</span></label>
<select class="" name="txtCategory[]" id="category1">
<option value="">Please select</option>
</select>
</div>
<div>
<label for="txtSubCategory" class="">Sub-category <span class="requiredField">*</span></label>
<select class="" name="txtSubCategory[]" id="subcategory1">
<option value="">Please select category</option>
</select>
</div>
<div>
<label for="txtSubSubCategory">Sub-sub-category <span class="requiredField">*</span></label>
<select name="txtSubSubCategory[]" id="subsubcategory1">
<option value="">Please select sub-category</option>
</select>
</div>
<div class="actions">
<button class="clone">Clone</button>
<button class="remove">Remove</button>
</div>
</div>
JavaScript - Jquery v1.7 and earlier
var regex = /^(.+?)(\d+)$/i;
var cloneIndex = $(".clonedInput").length;
$("button.clone").live("click", function(){
$(this).parents(".clonedInput").clone()
.appendTo("body")
.attr("id", "clonedInput" + cloneIndex)
.find("*").each(function() {
var id = this.id || "";
var match = id.match(regex) || [];
if (match.length == 3) {
this.id = match[1] + (cloneIndex);
}
});
cloneIndex++;
});
There is only one silly part :) .attr("id", "clonedInput" + $(".clonedInput").length) but it works ;)
JAvascript - JQuery recent (supporting .on())
var regex = /^(.+?)(\d+)$/i;
var cloneIndex = $(".clonedInput").length;
function clone(){
$(this).parents(".clonedInput").clone()
.appendTo("body")
.attr("id", "clonedInput" + cloneIndex)
.find("*")
.each(function() {
var id = this.id || "";
var match = id.match(regex) || [];
if (match.length == 3) {
this.id = match[1] + (cloneIndex);
}
})
.on('click', 'button.clone', clone)
.on('click', 'button.remove', remove);
cloneIndex++;
}
function remove(){
$(this).parents(".clonedInput").remove();
}
$("button.clone").on("click", clone);
$("button.remove").on("click", remove);
working example here

Another option would be to use a recursive function:
// Accepts an element and a function
function childRecursive(element, func){
// Applies that function to the given element.
func(element);
var children = element.children();
if (children.length > 0) {
children.each(function (){
// Applies that function to all children recursively
childRecursive($(this), func);
});
}
}
Then you can make a function or three for setting the attributes and values of your yet-to-be-cloned form fields:
// Expects format to be xxx-#[-xxxx] (e.g. item-1 or item-1-name)
function getNewAttr(str, newNum){
// Split on -
var arr = str.split('-');
// Change the 1 to wherever the incremented value is in your id
arr[1] = newNum;
// Smash it back together and return
return arr.join('-');
}
// Written with Twitter Bootstrap form field structure in mind
// Checks for id, name, and for attributes.
function setCloneAttr(element, value){
// Check to see if the element has an id attribute
if (element.attr('id') !== undefined){
// If so, increment it
element.attr('id', getNewAttr(element.attr('id'),value));
} else { /*If for some reason you want to handle an else, here you go*/ }
// Do the same with name...
if(element.attr('name') !== undefined){
element.attr('name', getNewAttr(element.attr('name'),value));
} else {}
// And don't forget to show some love to your labels.
if (element.attr('for') !== undefined){
element.attr('for', getNewAttr(element.attr('for'),value));
} else {}
}
// Sets an element's value to ''
function clearCloneValues(element){
if (element.attr('value') !== undefined){
element.val('');
}
}
Then add some markup:
<div id="items">
<input type="hidden" id="itemCounter" name="itemCounter" value="0">
<div class="item">
<div class="control-group">
<label class="control-label" for="item-0-name">Item Name</label>
<div class="controls">
<input type="text" name="item-0-name" id="item-0-name" class="input-large">
</div>
</div><!-- .control-group-->
<div class="control-group">
<label for="item-0-description" class="control-label">Item Description</label>
<div class="controls">
<input type="text" name="item-0-description" id="item-0-description" class="input-large">
</div>
</div><!-- .control-group-->
</div><!-- .item -->
</div><!-- #items -->
<input type="button" value="Add Item" id="addItem">
And then all you need is some jQuery goodness to pull it all together:
$(document).ready(function(){
$('#addItem').click(function(){
//increment the value of our counter
$('#itemCounter').val(Number($('#allergyCounter').val()) + 1);
//clone the first .item element
var newItem = $('div.item').first().clone();
//recursively set our id, name, and for attributes properly
childRecursive(newItem,
// Remember, the recursive function expects to be able to pass in
// one parameter, the element.
function(e){
setCloneAttr(e, $('#itemCounter').val());
});
// Clear the values recursively
childRecursive(newItem,
function(e){
clearCloneValues(e);
}
);
// Finally, add the new div.item to the end
newItem.appendTo($('#items'));
});
});
Obviously, you don't necessarily need to use recursion to get everything if you know going in exactly what things you need to clone and change. However, these functions allow you to reuse them for any size of nested structure with as many fields as you want so long as they're all named with the right pattern.
There's a working jsFiddle here.

Clone the main element, strip the id number from it.
In the new element replace every instance of that id number in every element id you want incremented with the new id number.
Ok, here's a quicky code here.
Basically, this part is the most important:
(parseInt(/test(\d+)/.exec($(this).attr('id'))[1], 10)+1
It parses the current id (using RegEx to strip the number from the string) and increases it by 1. In your case instead of 'test', you should put 'clonedInput' and also not only increase the value of the main element id, but the three from the inside as well (category, subcategory and subsubcategory). This should be easy once you have the new id.
Hope this helps. :)

Add data attribute to the input to get the field name, increment the value with variable.
html :
<td>
<input type="text" data-origin="field" name="field" id="field" required="" >
<div role="button" onclick='InsertFormRow($(this).closest("tr"),"tableID","formID");' id="addrow"> + </div>
</td>
and put this javascript function
var rowNum = 1;
var InsertFormRow = function(row, ptable, form)
{
nextrow = $(row).clone(true).insertAfter(row).prev('#' + ptable + ' tbody>tr:last');
nextrow.attr("id", rowNum);
nextrow.find("input").each(function() {
this.name = $(this).data("origin") + "_" + rowNum;
this.id = $(this).data("origin") + "_" + rowNum;
});
rowNum++;
}

Related

Cloning a div and getting specific id's value in the div

I have a html that will allow me to add or remove numbers of rows(divs) and a button that allows me to read through that number of rows(divs)
so the rows is as follow
<div id="mainContent">
<div id="StaffRow" class="WorkItemRow row" display:none;">
<div id="selections">
<select class="form-control">
<option value=""></option>
<option value="1">1</option>
<option value="2">2</option>
<option value="3">3</option>
</select>
</div>
<div>
Name
</div>
<div>
<input id="MemberName" type="text" value="">
</div>
<div>
Mail
</div>
<div>
<input type="text" id="Mail" value="">
</div>
</div>
I've managed to clone the row as such
$('#ButtonAddStaff').click(function (e) {
counters++;
$("#StaffRow").clone(true).attr('id', "#StaffRow" + staffCount).appendTo("#mainContent");
$(".WorkItemRow").last().css('display', '');
});
but now the problem is i cant seem to iterate over the staffrow created and get the datas.
I've tried getting the data as such but it returns me undefined
for (let i = (counters- 1) ; i >= 0; i--) {
if (counters!= 1)
var nameData= document.getElementById('StaffRow' + i).children("#MemberName").val();
nameData= document.getElementById('StaffRow' + i);
list.push(nameData);
}
any idea where i should be looking to accomplish what i wanted to?
so what i wanna do is iterate over the created staffrow that is created after pressing the button and getting the values of membername in each of the row
I think your problem is here:
.attr('id', "#StaffRow" + staffCount)
When you do that, you actually set an id with the sharp. The second parameter of this function is not a kind a selector. Then you do a document.getElementById without this sharp.
You should use:
.attr('id', "StaffRow" + staffCount)
Maybe the following will help a bit, please let me know if you need more help.
This code does not rely on id's but uses a user defined attribute called x-role to indicate what role this element has in your program.
document.querySelector('#ButtonAddStaff').addEventListener(
"click",
function (e) {
const newRow = document.querySelector("#StaffRow").cloneNode(true);
//remove the id, no 2 same id's should be the same in dom
newRow.removeAttribute("id");
//set the x-role attribute, could set it on the hidden first but
// have to use .slice(1) when getting all the rows in getAllStaffRows
newRow.setAttribute("x-role","staffrow");
//show the row
newRow.style.display="";
document.querySelector("#mainContent").append(newRow);
}
);
//keep going to parent until we reach document or until function passed in
// returns true
const getParentUntil = fn => el => {
if(el===document.body){
return false;
}
if(fn(el)){
return el;
}
//keep calling itself until we find the correct parent
// or until we reach document
return getParentUntil(fn)(el.parentElement);
}
//remove handler
document.getElementById("mainContent").addEventListener(
"click",
e=>{
if(e.target.getAttribute("x-role")==="remove"){
const staffRow = getParentUntil(x=>x.getAttribute("x-role")==="staffrow")(e.target);
if(staffRow){
staffRow.remove();
}
}
}
)
document.querySelector('#logdata').addEventListener(
"click",
function (e) {
console.log(
JSON.stringify(
getAllStaffRows(),
undefined,
2
)
)
}
);
const getAllStaffRows = () =>
//get all html elements that have x-role attribute of staffrow
Array.from(document.querySelectorAll(`[x-role='staffrow']`))
//map the row elements into objects that contain the input values
.map(
row=>({
//find the element with x-role attribute that has a membername value
name:row.querySelector(`[x-role='membername']`).value,
//do this for selections and mail as well
selection:row.querySelector(`[x-role='selections']`).value,
mail:row.querySelector(`[x-role='mail']`).value
})
);
<input type="button" value="add" id="ButtonAddStaff">
<input type="button" value="log values" id="logdata">
<div id="mainContent">
<div id="StaffRow" class="WorkItemRow row" style="display:none;">
<div>
<select class="form-control " x-role="selections">
<option value=" "></option>
<option value="1 ">1</option>
<option value="2 ">2</option>
<option value="3 ">3</option>
</select>
</div>
<div>
Name
</div>
<div>
<input x-role="membername" type="text " value=" ">
</div>
<div>
Mail
</div>
<div>
<input type="text " x-role="mail" value=" ">
</div>
<div>
<input type="button" x-role="remove" value="remove">
</div>

How can I filter text/divs from two select options?

I am driving myself crazy trying to figure this out...
I would like two drop-down boxes that function to show/hide text as options are clicked on. I got this part down.
https://jsfiddle.net/n1Lcfm36/
$(document).ready(function(){
$("select").change(function(){
$(this).find("option:selected").each(function(){
if($(this).attr("value")=="red"){
$(".box").not(".red").hide();
$(".red").show();
}
else if($(this).attr("value")=="green"){
$(".box").not(".green").hide();
$(".green").show();
}
else if($(this).attr("value")=="blue"){
$(".box").not(".blue").hide();
$(".blue").show();
}
else{
$(".box").hide();
}
});
}).change();
});
When both boxes are used together, I'd like it to filter similar to how it does here: http://jsfiddle.net/g5cryt31/
$().ready(function() {
$('.selectSome').on('change', function() {
showHide();
});
});
function showHide() {
// hide all rows
$('.row').hide();
// show good row only
if ($('#select_category').val() != 'null' && $('#select_subject').val() != 'null') {
$('#row_' + $('#select_subject').val() + '_' + $('#select_category').val()).show();
}
}
Except, nothing happens when you try to view one box at a time there. They need to both be used to filter...
So, first box "Class", would show nothing at first. Then, you select a class from the dropdown and related classes appear below.
Second box, "Subject" would show nothing at first. Then, you select a Subject from the dropdown and related subjects appear below.
BUT when both are used together (I guess this would require a submit button?) they filter out similar to the second jsfiddle I posted.
I think I understand what you want,
Correct me if I'm wrong-
This will show all elements regarding class X or Subject Y.
If they are picked together it will show only X + Y.
This will do what you what:
$().ready(function() {
$('.selectSome').on('change', function() {
showHide();
});
});
function showHide() {
// hide all rows
$('.row').hide();
// show good row only
if ($('#select_category').val()!= 'null'){
//class selected - check if subject seleceted
if ($('#select_subject').val() != 'null'){
//class and subject selected use double selector
$('.' + $('#select_subject').val()+'.'+$('#select_category').val()).show();
}
else {
//only class selected - use category selector
$('.' + $('#select_category').val()).show();
}
}
else
if ($('#select_subject').val() != 'null'){
//only subject selected without class
$('.' + $('#select_subject').val()).show();
}
}
You will have to add "c1.. s1..." classes (or data-attr) and you can loose the id's
New html for rows:
<div class="row s1 c1">
Subject 1 for Class 1
</div>
<div class="row s2 c1">
Subject 2 for Class 1
</div>
<div class="row s1 c2">
Subject 1 for Class 2
</div>
<div class="row s2 c2">
Subject 2 for Class 2
</div>
<div class="row s3 c2">
Subject 3 for Class 2
</div>
<div class="row s3 c3">
Subject 3 for Class 3
</div>
Fiddle
I would throw out the IDs and just use data attributes. Then dynamically build the selector. Note the different definition on the row divs and the new showHide() function.
$().ready(function() {
$('.selectSome').on('change', function() {
showHide();
});
});
function showHide() {
// hide all rows
$('.row').hide();
// show good rows only
var cat = $('#select_category').val();
var sub = $('#select_subject').val();
var selector = "[class='row']";
if (cat !== 'null') {
selector += "[data-category='" + cat + "']";
}
if (sub !== 'null') {
selector += "[data-subject='" + sub + "']";
}
$(selector).show();
}
.row {
display: none;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<label for="select_class">Class:
<select id="select_category" class="selectSome">
<option value="null">Please select...</option>
<option value="c1">Class 1</option>
<option value="c2">Class 2</option>
<option value="c3">Class 3</option>
</select>
</label>
<br/>
<label for="select_subject">Subject:
<select id="select_subject" class="selectSome">
<option value="null">Please select...</option>
<option value="s1">Subject 1</option>
<option value="s2">Subject 2</option>
<option value="s3">Subject 3</option>
</select>
</label>
<hr/>
<div class="row" data-subject="s1" data-category="c1">
Subject 1 for Class 1
</div>
<div class="row" data-subject="s2" data-category="c1">
Subject 2 for Class 1
</div>
<div class="row" data-subject="s1" data-category="c2">
Subject 1 for Class 2
</div>
<div class="row" data-subject="s2" data-category="c2">
Subject 2 for Class 2
</div>
<div class="row" data-subject="s3" data-category="c2">
Subject 3 for Class 2
</div>
<div class="row" data-subject="s3" data-category="c3">
Subject 3 for Class 3
</div>

Cloning div on click breaks when moving the button out of container?

I found this nifty js fiddle and it does nearly exactly what I need
However its cloning the parent of the button and id like to have the button separate from the actual div being cloned. (if you put the clone button back into the container with the remove button it works fine again)
In all I am trying to accomplish 3 things.
1. Have the button outside of the div that's being duplicated (1 button)
2. Limit the number of duplication's to a total of 6. (or any changeable variable)
3. Update the <h4> content and change the number 1 to the next number. ie: (1-6)
I'm not very JS savvy although I do dabble. If anyone has the time to help me figure out the above it would be beyond appreciated! Here's the JS FIDDLE I've been playing with.
Thanks!
var regex = /^(.+?)(\d+)$/i;
var cloneIndex = $(".clonedInput").length;
function clone(){
$(this).parents(".clonedInput").clone()
.appendTo("body")
.attr("id", "clonedInput" + cloneIndex)
.find("*")
.each(function() {
var id = this.id || "";
var match = id.match(regex) || [];
if (match.length == 3) {
this.id = match[1] + (cloneIndex);
}
})
.on('click', 'button.clone', clone)
.on('click', 'button.remove', remove);
cloneIndex++;
}
function remove(){
$(this).parents(".clonedInput").remove();
}
$("button.clone").on("click", clone);
$("button.remove").on("click", remove);
I think the folowing is what you're trying to acheive, you have to add another variables cloned_nbr and clones_limit to control the cloned divs:
var cloneIndex = 1;
var clones_limit = 4;
var cloned_nbr = $(".clonedInput").length-1; //Exclude Default (first) div
function clone()
{
if(cloned_nbr<clones_limit)
{
cloneIndex++;
cloned_nbr++;
var new_clone = $(".clonedInput").first().clone();
new_clone.attr("id", "clonedInput" + cloneIndex);
new_clone.find(".label-nbr").text(cloneIndex);
new_clone.find(".category").attr("id","category"+cloneIndex);
new_clone.find(".remove").attr("id","remove"+cloneIndex);
new_clone.on('click', 'button.clone', clone);
new_clone.on('click', 'button.remove', remove);
$(".clone").before(new_clone);
}
}
function remove(){
if(cloneIndex>1){
$(this).parents(".clonedInput").remove();
cloned_nbr--;
}
}
$("button.clone").on("click", clone);
$("button.remove").on("click", remove);
body { padding: 10px;}
.clonedInput { padding: 10px; border-radius: 5px; background-color: #def; margin-bottom: 10px; }
.clonedInput div { margin: 5px; }
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="clonedInput1" class="clonedInput">
<div>
<label for="txtCategory" class="">Learning category <span class="label-nbr">1</span><span class="requiredField">*</span></label>
<select class="category" name="txtCategory[]" id="category1">
<option value="">Please select</option>
</select>
</div>
<div class="actions">
<button class="remove">Remove</button>
</div>
</div>
<button class="clone">Clone</button>
You can select the last occurence of .clonedInput and clone that, then insert it after the original element:
var regex = /^(.+?)(\d+)$/i;
function clone(){
var cloneIndex = $(".clonedInput").length + 1;
if (cloneIndex > 6) return;
$source = $(".clonedInput").last();
$source.clone()
.insertAfter($source)
.attr("id", "clonedInput" + cloneIndex)
.find("*")
.each(function() {
var id = this.id || "";
var match = id.match(regex) || [];
if (match.length == 3) {
this.id = match[1] + (cloneIndex);
}
})
.on('click', 'button.remove', remove)
.find('label').html('Learning category ' + cloneIndex + ' <span class="requiredField">*</span>');
}
function remove(){
$(this).parents(".clonedInput").remove();
}
$("button.clone").on("click", clone);
$("button.remove").on("click", remove);
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="clonedInput1" class="clonedInput">
<div>
<label for="txtCategory" class="">Learning category 1 <span class="requiredField">*</span></label>
<select class="" name="txtCategory[]" id="category1">
<option value="">Please select</option>
</select>
</div>
<div class="actions">
<button class="remove">Remove</button>
</div>
</div>
<button class="clone">Clone</button>
I'd initialize plain block as template and use it as clone base.
HTML
<div class="box-wrap">
<button class="clone">Clone</button>
</div>
Full demo: http://jsfiddle.net/jeafgilbert/tfFLt/1898/

Is there a way to simplify this into only a few lines of jQuery

I am trying to display input values of a form into their corresponding div/p tags. So whenever a user starts typing into an input field, that value will be written in the input box as well as in a p tag else where on the page.
I have my jQuery looking at every individual form field and displaying that info to an assigned p tag. Is there a way to write this code so I do not have to create multiple lines of code for each form field?
Write now it is checking for if there is a change in the form, and then seeing if the field has a value and if so displaying the information in the p tag, if not it makes the p tag empty.
He is what I have working now.
$('#webform-client-form-1').on('change',function(e){
/* Distributor Name INPUT */
var distributorNameInput=$('#edit-submitted-distributor-name').val();
if( !$("#edit-submitted-distributor-name").val() ) {
$(".distributor-name p").html("");
} else {
$(".distributor-name p").html("<strong>Distributor Name</strong> <br/>" + distributorNameInput);
};
/* Year INPUT */
var YearInput=$('#edit-submitted-year').val();
if( !$("#edit-submitted-year").val() ) {
$(".year p").html("");
}else {
$(".year p").html("<strong>Year</strong> <br/>" + YearInput);
};
/* General Information INPUT */
var generalinfoInput=$('#edit-submitted-general-information').val();
if( !$("#edit-submitted-general-information").val() ) {
$(".general-info").html("");
}else{
$(".general-info").html("<h2>General Information</h2> <p>" + generalinfoInput + "</p>");
};
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.7.0/jquery.min.js"></script>
<form enctype="multipart/form-data" action="/" method="post" id="webform-client-form-1" accept-charset="UTF-8">
<p>
<label>Distributor Name*</label>
<input type="text" id="edit-submitted-distributor-name" name="submitted[distributor_name]" value=" " size="60" maxlength="128" class="form-text required">
</p>
<p>
<label for="edit-submitted-year">Year*</label>
<select id="edit-submitted-year" name="submitted[year]" class="form-select">
<option value="2015" selected="selected">2015</option>
<option value="2016">2016</option>
</select>
</p>
</form>
<div class="preview" id="preview">
<div class="distInfo">
<div class="distributor-name">
<p><strong>Distributor Name</strong> <br>Text will go here</p>
</div>
<div class="year">
<p><strong>Year</strong> <br>2015</p>
</div>
</div>
</div>
Instead of using IDs you can use classes and data attributes to pass input target element:
$(document).ready(function() {
$(document).on('change input paste', '.input', function() {
$($(this).data('target')).text($(this).val());
});
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<input type="text" class="input" data-target="#text-input" />
<select class="input" data-target="#select-input">
<option value=""></option>
<option value="A">A</option>
<option value="B">B</option>
</select>
<hr/>
<div id="text-input"></div>
<div id="select-input"></div>
you probably meaning something like inline if
/* Distributor Name INPUT */
var distributorNameInput = $('#edit-submitted-distributor-name').val();
$(".distributor-name p").html( distributorNameInput ? "<strong>Distributor Name</strong> <br/>" + distributorNameInput : "");
/* Year INPUT */
var YearInput = $("#edit-submitted-general-information").val();
$(".year p").html( YearInput ? "<strong>Year</strong> <br/>" + YearInput: "");
/* General Information INPUT */
var generalinfoInput = $('#edit-submitted-general-information').val();
$(".general-info").html( generalinfoInput ? "<h2>General Information</h2> <p>" + generalinfoInput : "");
The syntax for an inline if is
condition ? true code: false code
You can create an array of objects and iterate over it. This is great if you are thinking on adding new elements, because you will only need to add them to the array.
$('#webform-client-form-1').on('change',function(e){
var elements=new Array(
{valueToCheck:'edit-submitted-distributor-name',className:'distributor-name', label: 'Distributor Name'},
{valueToCheck:'edit-submitted-year',className:'distributor-name', label: 'Distributor Name'},
{valueToCheck:'edit-submitted-distributor-name',className:'distributor-name', label: 'Distributor Name'},
);
//check'em
elements.forEach(function(element){
var myValue=$("#"+element.valueToCheck).val();
$("."+element.className+" p").html( myValue ? "<strong>"+element.label+"</strong> <br/>" + myValue: "");
});
}

childNodes method cannot find my field names

http://www.quirksmode.org/dom/domform.html
I am trying to implement this extend form function in my project. But the field names cannot be located (the console log returns "undefined"), until I place an input field out of the divs and directly under the parent span tag. I'm not sure how the field names can be located and named accordingly as I intend to keep the divs.
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="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">Grade</label>
<select name="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 = "data[Student][" + counter + "][" + theName + "]";
console.log(newField[i].name);
}
var insertHere = document.getElementById('writeroot');
insertHere.parentNode.insertBefore(newFields,insertHere);
}
childNodes returns only direct children. It will find only elements with name attributes at the top level. To find all descendant elements with names as you want, try
var newField = newFields.querySelectorAll('[name]');
Minor points:
Although in this case you don't need to use either, you should use .children instead of .childNodes. The latter will visit white-space nodes, which won't hurt anything but isn't what you want.
You are missing brackets around the body of the if (theName) statement, meaning the console.log is being executed each time through the loop, even when a white-space node is being visited. To avoid such problems, set up your editor to indent property, and/or run a linter across your code.
if (theName) {
newField[i].name = "data[Student][" + counter + "][" + theName + "]";
console.log(newField[i].name);
}
I'd suggest naming your variables a bit more clearly. You have a variable named newFields which is a single span, and you have a variable called newField which is a collection of fields.

Categories