create an infinitely extensible form? - javascript

I have a form that needs to be able to multiply specific lines in the form, as well as then duplicate the entire form. In the concept example I am working on, there is a Name field, and a URL field. I need 1 name field, and up to 3 URL fields, but then I need to duplicate this entire grouping up to 3 times, and submit it via POST, but the number pairings can be different.
As an example, the first group can have 1 name, 3 urls, the second have 1 url, and the third have 3 urls. I am able to multiply the number of URL fields, or the entire form, but if I have 2 URL fields, all multiples of the form have 2 URL fields, and I am then unable to change it in any of the forms.
Looking at the JSFiddle is much easier than just posting the code here, because you can see my code, as well as how it behaves.
http://jsfiddle.net/wingdom/yCTpf/3/
Thank you for the help!
HTML:
<form id="myForm">
<div id="O1" class="clonedInput5">
<fieldset>
<input type="hidden" name="Ocount" id="Ocount" value="1" />
<legend>Outbound App - Required</legend>
<div>
<label>Name:</label>
<input type="text" name="oname">
</div>
<div id="ourl1" style="margin-bottom:4px;" class="clonedInput1">URL:
<input type="text" name="ourl1" id="ourl1" />
</div>
<div>
<input type="button" id="ourlAdd" value="Add URL" />
<input type="button" id="ourlDel" value="Remove URL" />
</div>
<input type="hidden" name="urlo" id="urlo" value="1" />
</fieldset>
</div>
<input type="button" id="OAdd" value="Add Outbound" />
<input type="button" id="ODel" value="Rm Outbound" />
Javascript:
$('#ourlAdd').click(function () {
var num = $('.clonedInput1').length; // how many "duplicatable" input fields we currently have
var newNum = new Number(num + 1); // the numeric ID of the new input field being added
// create the new element via clone(), and manipulate it's ID using newNum value
var newElem = $('#ourl' + num).clone().attr('id', 'ourl' + newNum);
// manipulate the name/id values of the input inside the new element
newElem.children(':first').attr('id', 'ourl' + newNum).attr('name', 'ourl' + newNum);
// insert the new element after the last "duplicatable" input field
$('#ourl' + num).after(newElem);
document.getElementById("urlo").value = newNum;
// enable the "remove" button
$('#ourlDel').attr('disabled', '');
// business rule: you can only add 5 names
if (newNum == 3) $('#ourlAdd').attr('disabled', 'disabled');
});
$('#ourlDel').click(function () {
var num = $('.clonedInput1').length; // how many "duplicatable" input fields we currently have
$('#ourl' + num).remove(); // remove the last element
document.getElementById("urlo").value = num - 1;
// enable the "add" button
$('#ourlAdd').attr('disabled', '');
// if only one element remains, disable the "remove" button
if (num - 1 == 1) $('#ourlDel').attr('disabled', 'disabled');
});
$('#ourlDel').attr('disabled', 'disabled');
$('#OAdd').click(function () {
var num = $('.clonedInput5').length; // how many "duplicatable" input fields we currently have
var newNum = new Number(num + 1); // the numeric ID of the new input field being added
// create the new element via clone(), and manipulate it's ID using newNum value
var newElem = $('#O' + num).clone().attr('id', 'O' + newNum);
// insert the new element after the last "duplicatable" input field
$('#O' + num).after(newElem);
document.getElementById("Ocount").value = newNum;
// enable the "remove" button
$('#ODel').attr('disabled', '');
// business rule: you can only add 5 names
if (newNum == 3) $('#OAdd').attr('disabled', 'disabled');
});
$('#ODel').click(function () {
var num = $('.clonedInput5').length; // how many "duplicatable" input fields we currently have
$('#O' + num).remove(); // remove the last element
document.getElementById("Ocount").value = num - 1;
// enable the "add" button
$('#OAdd').attr('disabled', '');
// if only one element remains, disable the "remove" button
if (num - 1 == 1) $('#ODel').attr('disabled', 'disabled');
});
$('#ODel').attr('disabled', 'disabled');

One way of doing it would be to store a copy of the original form initially, then use that copy to create your additional forms.
var $originalForm = $("#originalForm").clone();
Now, any time you need to create a new form, you just do: $originalForm.clone() then iterate through the elements within the form fixing ID's (assuming you don't go away from using id's.)
Though, personally i would do this a completely different way.
var formTemplate = "form template here, or get it from a <script type=\"text/template\"></text>";
function ExtendableForm() {
this.$form = $(formTemplate).appendTo("body");
this.bindEvents();
return this.$form;
}
$.extend( ExtendableForm.prototype, {
addAnotherURL: function(){
this.$form.find(".wrap-url").append($(formTemplate).find(".wrap-url").children());
},
addAnotherName: function(){
this.$form.find(".wrap-name").append($(formTemplate).find(".wrap-url").children());
},
bindEvents: function(){
this.$form
.on("click", ".add-url", $.proxy(addAnotherURL,this))
.on("click", ".add-name", $.proxy(addAnotherName,this));
}
});
$("#addAnotherForm").click(function(){
$("#form-container").append(new ExtendableForm());
}).click();
It could probably be made a bit dryer, but that's the basic idea. No one form is aware of or needs to care about any other form.

Related

Unique name for Add More input

I am trying to create an add more button which will create a new input field. However, I would like to have an unique name set for it.
I tried to search up for an answer, but this does not answer my question.
So, basically what I tried to make my namefield unique is to use the php method rand(). The concept is that - when the add more button is clicked, it will have a name attached to the number given to me by rand().
However, what happens is that it takes the value generated by rand() and applies it to all the names of all the inputs generated.
This is my code and what I tried:
HTML:
<div class="field_wrapper">
<div>
<input type="text" name="field_name[<?php echo rand(); ?>]" value=""/>
Add More
</div>
</div>
JQUERY / JAVASCRIPT:
<script type="text/javascript">
$(document).ready(function(){
var maxField = 100; //Input fields increment limitation
var addButton = $('.add_button'); //Add button selector
var wrapper = $('.field_wrapper'); //Input field wrapper
var fieldHTML = '<div><input type="text" name="field_name[<?php echo rand(); ?>]" value=""/>Remove</div>'; //New input field html
var x = 1; //Initial field counter is 1
//Once add button is clicked
$(addButton).click(function(){
//Check maximum number of input fields
if(x < maxField){
x++; //Increment field counter
$(wrapper).append(fieldHTML); //Add field html
}
});
//Once remove button is clicked
$(wrapper).on('click', '.remove_button', function(e){
e.preventDefault();
$(this).parent('div').remove(); //Remove field html
x--; //Decrement field counter
});
});
</script>
As you can see, the first field generates the number as intended. If you click on the add more, the second field does create an unique number. However, if you click add more once again, the third field copies the same name as the 2nd field.
How do I go about achieving what I want and why is rand() not generating a new code?
Also, does rand() guarantee me that it will be an unique ID or is there a chance for it to repeat the same number?
If it does repeat, then what would be the best approach to take to make it as unique as possible?
If you generate random name with PHP it is done once on the server. Your JS code then copies the same element. What you need is to generate unique names with js.
Avoid random if you can, theoretically, you can hit the same number and run into mysterious bugs.
var generateField = function(name)
{
return '<div><input type="text" name="'+name+'" value=""/>Remove</div>'; //New input field html
}
//Once add button is clicked
$(addButton).click(function(){
//Check maximum number of input fields
if(x < maxField){
x++; //Increment field counter
$(wrapper).append(generateField('field_name['+x+']' ) ); //Add field html
}
});
Random does not necessarily mean unique, even if collisions would be extremely rare. This solution simply increments a totalFieldsCreated variable to get the next unique number (up to the maximum value JavaScript can provide.)
The new fields are created dynamically instead of using a fixed string of HTML. (This technique is more flexible.)
$(document).ready(function() {
// Defines global identifiers
let
currentFieldCount = 1,
totalFieldsCreated = 1;
const
maxFieldCount = 100,
addButton = $('.add_button'),
wrapper = $('.field_wrapper');
// Calls `addField` when addButton is clicked
$(addButton).click(addField);
// Executes anonymous function when `Remove` is clicked, which removes
// the parent div, and decrements (and logs) `currentFieldCount`
$(wrapper).on('click', '.remove_button', function(e) {
e.preventDefault();
$(this).parent('div').remove();
currentFieldCount--;
console.log(`currentFieldCount: ${currentFieldCount}`);
});
// Defines the `addField` function
function addField(){
// Makes sure that `currentFieldCount` and `totalFieldsCreated`
// are not at maximum before proceeding
if(
currentFieldCount < maxFieldCount &&
totalFieldsCreated < Number.MAX_VALUE
){
// Creates an input element, increments `totalFieldsCreated`,
// and uses the incremented value in the input's `name` attribute
const input = document.createElement("input");
input.type = "text";
input.name = "field" + ++totalFieldsCreated;
input.value = "";
// Creates an anchor element with the `remove_button` class
const a = document.createElement("a");
a.href = "javascript:void(0);";
a.classList.add("remove_button");
a.title = "remove";
a.innerHTML = "Remove";
// Adds the new elements to the DOM, and increments `currentFieldCount`
const div = document.createElement("div");
div.appendChild(input);
div.appendChild(a);
$(wrapper).append(div);
currentFieldCount++;
// Logs the new values of both variables
console.log(
`currentFieldCount: ${currentFieldCount},`,
`totalFieldsCreated ${totalFieldsCreated}`
);
}
}
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div class="field_wrapper">
<div>
<input type="text" name="field1" value="" />
Add More
</div>
</div>
Try Math.random() in js rather than rand() in php ,Math.floor(Math.random()*90000) + 10000 will generate a five digit random number , Hope this helps
$('.rand').attr('name',"fields["+Math.floor(Math.random()*90000) + 10000+"]")
$('.add_button').click(function(e){
$('.field_wrapper').append('<div><input type="text" name=fields['+Math.floor(Math.random()*90000) + 10000+'] value=""/>Remove</div>')
})
$(document).on('click','.remove_button',function(e){
$(this).parent().remove()
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.0/jquery.min.js"></script>
<div class="field_wrapper">
<div>
<input type="text" class="rand" value=""/>
Add More
</div>
</div>

Adding form post/submit button to javascript script

I found this code on here (thanks to Xavi López) and it is ideal for what I need to add to my project but I'm in need of some help adding a Form post and submit button in JavaScript. I have no knowledge on this subject and I've tried looking at some example but non of them seem to work. I would be grateful if someone could help me. After the user adds the relevant number of input boxes and adds there data, I would like to have a submit button which will POST the results to another web page (result page)
I have added the solution to the below coding (thank you MTCoster) but I'm now try to find a solution to having the submit button appear only when an entry has been added. I have tried different methods but non will work.
function addFields() {
// Number of inputs to create
var number = document.getElementById('member').value;
// Container <div> where dynamic content will be placed
var container = document.getElementById('container');
// Clear previous contents of the container
while (container.hasChildNodes()) {
container.removeChild(container.lastChild);
}
for (i = 0; i < number; i++) {
// Append a node with a random text
container.appendChild(document.createTextNode('Member ' + (i + 1) + ' '));
// Create an <input> element, set its type and name attributes
var input = document.createElement('input');
input.type = 'text';
input.name = 'member' + i;
container.appendChild(input);
// Append a line break
container.appendChild(document.createElement('br'));
}
}
<input type="text" id="member" name="member" value="">Number of Pins: (max. 48)<br>
Add Pinout Entries
<form action="result.asp" method="POST">
<div id="container"></div>
<input type="submit" value="Add Data">
</form>
You’re almost there - all you need to do is wrap your inputs in a <form> element:
function addFields() {
// Number of inputs to create
var number = document.getElementById('member').value;
// Container <div> where dynamic content will be placed
var container = document.getElementById('container');
// Clear previous contents of the container
while (container.hasChildNodes()) {
container.removeChild(container.lastChild);
}
for (i = 0; i < number; i++) {
// Append a node with a random text
container.appendChild(document.createTextNode('Member ' + (i + 1) + ' '));
// Create an <input> element, set its type and name attributes
var input = document.createElement('input');
input.type = 'text';
input.name = 'member' + i;
container.appendChild(input);
// Append a line break
container.appendChild(document.createElement('br'));
}
}
<input type="text" id="member" name="member" value="">Number of Pins: (max. 48)<br>
Add Pinout Entries
<form action="/url/to/post/to" method="POST">
<div id="container"></div>
<input type="submit">
</form>
If you’d like the submit button to only appear after at least one input is visible, you could add it at to div#container at the end of addFields(). I’ll leave this as an exercise to the OP, since it’s not much different to how you’re adding the input fields.

getting the value of a textbox when user enters the name/id of the field

Using jquery to get the value of a textbox.
BUT
i need to enter the id of the textbox, then use that value to get the value of the textbox using jquery.
var tt = $("#fieldname").val()
that works
now how do i enter the fieldname at runtime, and get jquery to execute the val command as if it was hard coded?
There are a few ways that you could do this. One way is to listen to one of the keyboard or change events on the textbox you enter the id into, to help determine when the input has changed. So for example
$("#inputText").on("keyup", function(keyupEvent){
var textboxId = $("#inputText").val();
var textboxIdValue = $("#" + textboxId).val();
});
Or another way could be to use a click event with similar kind of logic, so for example
$("#clickMe").on("click", function(){
var textboxId = $("#inputText").val();
var textboxIdValue = $("#" + textboxId).val();
})
An example for the use case of both can be seen here https://fiddle.jshell.net/xpvt214o/114584/
Here is an example for you to get started with:
<body>
<p>Type "one" or "two" below</p>
<input id="search" />
<input id="one" value="This input is #one" />
<input id="two" value="And this is #two" />
<p id="result">No input specified</p>
</body>
And the corresponding jQuery code:
// Cache jQuery elements for performance and readability
var $search = $("#search");
var $result = $("#result");
$search.on("change", function() {
var search_value = $search.val();
if (search_value.length) {
search_value = "#" + search_value.toLowerCase().trim(); // Sanitise user input
if ($(search_value).length) {
$result.text($(search_value).val());
} else {
$result.text("Input not found");
}
} else {
$result.text("No input specified");
}
});
This will show the value of the specified input, if it exists.
You can see it in action here: https://jsfiddle.net/jeevantakhar/xpvt214o/114558/

How to disable selected options with jQuery in multiple XML-fed, dynamically generated select list?

Ok, I've looked all around, both here and on the web, and can't begin to find an answer that fits this scenario.
I have a page that allows for dynamic creation of 5 select lists that are XML-fed. I need to disable any selected options in all of the select lists. I am confined to using jQuery 1.5.1 and have to support cross-browser back to IE7. Here's the code:
HTML
<fieldset class="clearfix clonedInput" id="input1">
<div class="toolTipHolder">
<label for="FooName" class="foo-select">Foo Select List
<select class="foo1 required infoTrigger foo-select" id="foo1" attr-info="This will be tooltip text.">
<option>loading</option>
</select>
</label>
<input type="button" value="Remove this option" class="foo-remove hidden clearfix" />
</div>
</fieldset>
</div>
<div class="clear"> </div>
<div><input id="btnAdd" type="button" value="Add another foo" class="btnAdd" /></div>
JS
$(document).ready(function() {
//XML IMPORT CODE
$.ajax({
type: "GET",
url: "../../includes/foo.xml",
dataType: "xml",
success: function(xml) {
var select = $('.foo1');
$(xml).find('rid').each(function(){
var fna = $(this).find('fna').text();
select.append("<option>"+fna+"</option>");
$(this).find('value').each(function(){
var value = $(this).text();
});
});
select.children(":first").text("please make a selection").attr("selected",true);
$("input, select").focus(function(){ $(this).fooTooltip(); });
}
});
// CODE TO ADD/REMOVE ELEMENTS
$('#btnAdd').attr('disabled','');
$('#btnAdd').click(function() {
var num = $('fieldset.clonedInput').length; // how many "duplicatable" input fields
var newNum = new Number(num + 1); // the numeric ID of the new input field
var selects = $('select'); //for all select lists
var newElem = $('#input' + num).clone().attr('id', 'input' + newNum);
newElem.children().find('select.foo-select').attr('id', 'foo' + newNum);
newElem.find('input.foo-remove').removeClass('hidden').attr('disabled','').attr('id', 'foo' + newNum);
$('#input' + num).after(newElem);
$("input, select").focus(function(){ $(this).fooTooltip(); });
$("select[id^='foo'] option:selected").val();
$('option[value="value-to-search-for"]', this).remove();
selects.change(function() {
var vals = {};
selects.each(function() {
vals[this.value] = true; }).get();
selects.not(this).children().not(':selected').not(':first-child').each(function() { this.disabled = vals[this.value]; });
});
if (newNum == 5)
$('#btnAdd').attr('disabled','disabled');
return false;
});
$('input.foo-remove').live("click", function() {
$(this).closest('fieldset.clonedInput').remove();
var num = $('fieldset.clonedInput').length; // how many "duplicatable" input fields
$('#btnAdd').attr('disabled','');
if (num-1 == 0)
$('.foo-remove').attr('disabled','disabled').addClass('hidden');
return false;
});
});
The disabling of selected options will work when adding the first cloned select list, but will not work on on subsequent clones. I've thrown everything at this and just can't get it. Please...any help is incredibly appreciated. Thanks!
How about this, say your variable fna is "foo":
select.append("<option class="+fna+">"+fna+"</option>");
Then add a click handler that like this:
$("option").live("click",function(){
var _class = $(this).attr("class");
$("."+class).hide();
});
So, on an option with class (and value) of "foo", you hide all other "foo" options whenever one of them is clicked.
So crazy it just may work. . .;).
Good luck!

Deleting any element, the reassigning the count value

Sorry about the cryptic title, it's hard to describe!
I am using the following script to add a row to my form when a button is clicked:
$(document).ready(function() {
$('#btnAdd').click(function() {
var num = $('.clonedInput').length;
var newNum = new Number(num + 1);
var newElem = $('#input' + num).clone().attr('id', 'input' + newNum);
newElem.children(':first').attr('id', 'name' + newNum).attr('name', 'name' + newNum);
$('#input' + num).after(newElem);
$('#btnDel').attr('disabled','');
if (newNum == 5)
$('#btnAdd').attr('disabled','disabled');
});
$('#btnDel').click(function() {
var num = $('.clonedInput').length;
$('#input' + num).remove();
$('#btnAdd').attr('disabled','');
if (num-1 == 1)
$('#btnDel').attr('disabled','disabled');
});
$('#btnDel').attr('disabled','disabled');
});
This works absolutely fine, but it's not very user-friendly that you can only delete the last added row. Say I want to delete a row added earlier on, I would have to delete everything I'd done since then.
How can I delete any row, and at the same time reassign the count value to the other rows, so that the count is still sequential (i.e. Row1, Row2, Row3 etc, rather than Row1, Row5, Row8)?
I recommend that you don't explicitly set an id to the elements. Instead you can use jQuery powerful selectors to get the position of an element.
This is an example, based on a common html structure. Post your own html to give you a more details.
$('.btnDel').click(function() {
var num = $(this).parent().prevAll().size();
// this points to delete link.
// The previous element is the content element
$(this).prev().remove();
$('#btnAdd').attr('disabled','');
if (num-1 == 1)
$(this).attr('disabled','disabled');
});
<div class='elem'>
<span class='content'>....</span>
<a class='btnDel' href='#'>Delete</a>
</div>
Why are you even adding IDs to the rows? Remember that 83% of jQuery is "Query" - use the selectors to get to the elements you want.
I had to do a similar thing a while ago
Essentially I got the number of the deleted row ie. Row3 and then looped through the remaining rows updating their values. so Row4 becomes Row3 etc.
I used something like this
var last_val = //get the last value of the rows so you know when to stop
//knock off the Row part of the id and parses as an integer
var pos = parseFloat(row_id.slice(3))
var next_val = position+1;
var prev_val = position;
while(next_val<=last_val){
next_selector = "#Row"+next_val;
prev_id = "Row"+prev_val;
$(next_selector).attr("id",prev_id);
next_val++;
prev_val++;
}
There may be a better way to do this, but this worked for me for a cms allowing pages to be deleted from the middle of a list which then updated the row numbering.
I posted a demo here (it doesn't look good in IE because of the float, but I just wanted to post this as an example to help). I don't know how you handle gathering your form data, so I did include renumbering the cloned input IDs.
CSS
form { width: 400px; margin: 0 auto; line-height: 30px; }
input { float: right; }
HTML
<form>
<div class="main">
Name: <input type="text" /><br>
Title: <input type="text" /><br>
Company: <input type="text" /><br>
Location: <input type="text" /><br>
</div>
Additional information: <input id="btnAdd" type="button" value="Add More"/>
<div id="addFields"></div>
</form>
Script
$(document).ready(function(){
$('#btnAdd').click(function(){
// inputs reversed because of the float right
var newInput = '<div class="clonedInput">Additional Field <span></span>: ' +
'<input type="button" class="btnDel" title="Delete this field" value="X"><input type="text" /></div>';
$(newInput).appendTo('#addFields');
// disable add button if there are 5 additional fields
if ($('.clonedInput').length == 5) {
$('#btnAdd').attr('disabled','disabled');
}
renumber();
})
// Delete input field
$('.btnDel').live('click',function(){
$('#btnAdd').attr('disabled','');
$(this).parent().remove();
renumber();
})
})
// This function adds the additional field number and ID for each clonedInput field
function renumber(){
$('.clonedInput').each(function(i){
$(this).find('span').html('#' + (i+1));
// the code below will change the ID of the input, in case you collect your data based on the ID.
$(this).find('input[type=text]').attr('id', 'input' + (i+1));
})
}

Categories

Resources