jquery dynamically add html fields met mysterious problems - javascript

I am using jquery to duplicate several html fields based on user's selection. However, I met an interesting problem. In general, I am asking users to select how many applications they want:
if there is only one application:
a. One need to choose application method (for simplicity, only 'aerial' is available); b. after selecting 'aerial', it will ask you for the further information, chemically application method (CAM).
if they choose two applications, jquery code will clone and rename the necessary questions for you.
My problem is when I choose there are two applications, the sub-question 'CAM' will not show up. After some trouble shoot, I found the problem could be in this javascript :$('.app_method:last').find('select').change(function(). The statement, automatically increase my loop index by one (Can anyone tell me why this will happen?), which mismatch the code.
Here is a DEMO for my code:
Below is my html code:
<div class="articles">
<table align="center">
<tr><th><label for="id_NOA">Number of applications:</label></th><td><select name="NOA" id="id_NOA">
<option value="1" selected="selected">1</option>
<option value="2">2</option>
<option value="3">3</option>
</select></td></tr>
<tr><th><label for="id_Ap_m">Application method 1</label></th><td><select name="Ap_m" id="id_Ap_m">
<option value="" selected="selected">Select an application method</option>
<option value="1">Aerial</option>
</select></td></tr>
<tr><th><label for="id_CAM_1">Chemical application Method (CAM) 1</label></th><td><select name="CAM_1" id="id_CAM_1">
<option value="2">2-Interception based on crop canopy</option>
<option value="9">9-Linear foliar based on crop canop</option>
</select></td></tr>
</table>
</div>
​
jquery code:
$(document).ready(function() {
//this part of code controls inputs when there is only one application
$('#id_CAM_1').attr('id', 'id_1').closest('tr').addClass('method_options').hide();
$('#id_Ap_m').change(function() {
$('tr.method_options').hide();
if ($(this).val() == "1") {
$('#id_' + $(this).val()).closest('tr').show();
}
});
i = 1;
$('.articles').find('table').addClass('table');
$('#id_Ap_m').closest('tr').addClass('app_method');
$('#id_NOA').change(function() {
var total = $(this).val();
//remove all
$('.app_method').each(function(index) {
if (index != 0) $(this).remove()
});
//create new ones
for (var i = 2; i <= total; i++) {
alert('a=' + i);
$('.app_method:first').clone().appendTo('.table').find('label').text('Application method ' + i);
$('.app_method:last').find('select').attr('name', 'Ap_m' + i).attr('id', 'id_Ap_m' + i);
alert('b=' + i);
$('<tr class="method_options_1' + i + '" style="display: none;"><th><label for="id_CAM_1">Chemical application Method (CAM)' + i + '</label></th><td><select name="CAM_1_' + i + '" id="id_1_' + i + '"><option value="2">2-Interception based on crop canopy</option><option value="9">9-Linear foliar based on crop canop</option></select></td></tr>').appendTo('.table');
alert('c=' + i);
//The following statement increase the loop index by one, which causes me problems. Can
//anyone let me know why this could happen?
$('.app_method:last').find('select').change(function() {
alert('d=' + i)
$('.method_options_1').hide();
alert('e=' + i);
if ($(this).val() == "1") {
alert('e=' + i);
$('.method_options_1' + i).show();
}
})
}
})
})​
​

I think this can be done much more simply: (fiddle: http://jsfiddle.net/QaHWz/)
<!DOCTYPE html><html><head></head><body>
<table align="center"><tbody>
<tr><th></th><td>Add Application</td></tr>
</tbody></table>
<table id="template" style="display:none"><tbody>
<tr>
<th><label for="id_Ap_m_{n}">Application method {n}</label></th>
<td>
<select class="Ap_m" name="Ap_m_{n}" id="id_Ap_m_{n}" data-application="{n}">
<option value="" selected="selected">Select an application method</option>
<option value="1">Aerial</option>
</select>
</td></tr>
<tr style="display:none" class="app_{n} method_1"><th><label for="id_CAM_{n}">Chemical Application Method (CAM) {n}</label></th><td><select name="CAM_{n}" id="id_CAM_{n}">
<option value="2">2-Interception based on crop canopy</option>
<option value="9">9-Linear foliar based on crop canopy</option>
</select></td></tr>
</tbody></table>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.7.2/jquery.min.js"></script>
<script>
jQuery(function($) {
var applications = 0;
$('#add_application').click(function() {
applications++;
var last_row = $(this).closest('tr');
last_row.before(jQuery('#template tbody').html().replace(/{n}/g, applications));
});
$(document).delegate('.Ap_m', 'change', function() {
var app = $(this).data('application');
$('.app_'+app).hide();
$('.app_'+app+'.method_'+this.value).show();
});
});
</script>
</body></html>
EDIT: The problem you are having with .change() is that you are using the i variable , which gets incremented before the function is run. You need to get the value of i into the function another way. Here is one possible way you can do it:
$('.app_method:last').find('select').bind('change', { row: i }, function(event) {
var i = event.data.row;
alert('d=' + i)
// ...
});
The { row: i } bit causes jQuery to attach this data to the event object which is passed to the function. Then I create var i inside the scope of the function, which will not be affected by the i outside, and assign this value to it.

Related

Keep auto created fields shown after reload

I have a javscript code that create fields based on select menus
HTML
<div class="container">
<div id="selected_form_code">
<select id="select_btn">
<option value="0">--How many rooms ?--</option>
<option value="1">1</option>
<option value="2">2</option>
<option value="3">3</option>
<option value="4">4</option>
<option value="5">5</option>
<option value="6">6</option>
</select>
</div>
<div id="form_submit">
<!-- Dynamic Registration Form Fields Creates Here -->
</div>
</div>
JS
function get_chambre_html(cn)
{
return "<div>"
+ "<b>Chambre " + cn + ":</b> "
+ "<br/>Adultes: <select id='adultes" + cn + "'>"
+ "<option value='0'>--How many adults ?--</option>"
+ "<option value='1'>1</option>"
+ "<option value='2'>2</option></select>"
+ "<br/>Enfants: <select id='enfants" + cn + "'>"
+ "<option value='0'>--How many enfants ?--</option>"
+ "<option value='1'>1</option>"
+ "<option value='2'>2</option><option value='3'>3</option><option value='4'>4</option></select>"
+ "<div id='ages" + cn + "'></div>" // empty block for further usage
+"</div>";
}
$(document).ready(function()
{
$('select#select_btn').change(function()
{
var sel_value = $('option:selected').val();
$("#form_submit").empty(); //Resetting Form
// Below Function Creates Input Fields Dynamically
create(sel_value);
// Appending Submit Button To Form
});
function create(sel_value)
{
for (var i = 1; i <= sel_value; i++)
{
$("div#form1").append($("#form_submit").append(get_chambre_html(i)));
$("div#form1").append($("#form_submit").append("<div id='ages"+i+"'/>"));
$('select#enfants'+i).change(function(){
var infants = this.value;
var i=this.id.substr(7); // 7 = strlen of 'enfants'
$('#ages'+i).empty();
for(var j=0; j<infants; j++)
$('#ages'+i).append("Age enfant "+(j+1)+" : <select><option>1 an</option><option>2 ans</option><option>3 ans</option></select>");
});
}
};
});
Is there any way to keep auto created fields shown after page reload ? Because, for now, if i reload the page for another search, this fields disappear.
Before/After search:
Fields values are sent by GET method.
There are many ways of doing this. I would write a cookie. A cookie is data written to the user's browser and will persist between requests. Cookies are a great way to store data and there are already API's in javascript to do so. You can write to the cookie by assigning to document.cookie. As your working with a form I would serialize an object that represents the current state of your form.
document.cookie = JSON.stringify({ destination: 'BERCELONE' });
When the page loads you can check your value
var currentFormState = JSON.parse(document.cookie);
functionThatBuildsTheFormFromObject(currentFormState);
Now that we know how to store a cookie we need to figure out what to store in the cookie. To do this I would write two functions. The first function lets call it functionThatBuildsTheFormFromObject() would accept an object. I would use the following object. Note here that for each adult and child we use a value in an array.
{
destination : "Berclona",
depatureDate : "30-08-2016",
adults : [],
children : [ 5, 8],
seniors : [ 55, 58 ]
}
With this object let's create the form
functionThatBuildsTheFormFromObject (theFormObject) {
createDestinationField(theFormObject.destination);
createDepatureDateField(theFormObject.depatureDate);
theFormObject.adults.forEach(adultAge => {
createSelectAgeField('adult', adultAge)
})
theFormObject.children.forEach(childAge => {
createSelectAgeField('child', childAge)
})
theFormObject.seniors.forEach(seniorAge => {
createSelectAgeField('senior', seniorAge)
})
}
So now all that is required is to white functions to create all the fields which in part you have already done. Going the other way I would define a function that will give you the current values of your form. Check this
Convert form data to JavaScript object with jQuery question as it does exactly what you need. I would call this function serializeForm(). I would also look at setting the cookie each time the form changed. That way when ever the user refreshes their browser the form should always be updated. You might also think about providing the user a reset form button too incase they want to start again.

Targeting Auto-Generated Fields with Incremented IDs (JavaScript)

I have a starting set of HTML fields with an Add More fields button. The user can add as many sets of fields as desired.
<input id="alpha1" name="alpha1" type="text">
<input id="beta1" name="beta1" type="text" style="display:none;">
<select id="select_box1" name="select_box1">
<option value="one">one</option>
<option value="two">two</option>
</select>
The Add More option triggers JS to creatate a new set of fields and increments the id and name by one. Code exerpted to streamline, but I can post all if requested.
// [...]
next = next + 1;
// [...]
var fieldtype_1 = '<input id="alpha' + next + '" name="alpha' + next + '">'
var fieldtype_2 = '<input id="beta' + next + '" name="beta' + next + '" style="display:none;">'
var select_options = `
<option value="one">one</option>
<option value="two">two</option>
`
var fieldtype_3 = '<select id="select_box' + next + '">' + select_options + '</select>'
The beta field needs to show/hide based on user input into a select box.
$(document).ready(function () {
toggleFields();
$("#select_box1").change(function () {
toggleFields();
});
});
function toggleFields() {
if ($("#select_box1").val() == "two") {
$("#alpha1").hide();
$("#beta1").show();
}
else {
$("#alpha1").show();
$("#beta1").hide();
}
}
The above code works as intended for my default id(1) set of fields. However, I don't know how to approach targeting all the (unknown number) of additional fields the user could potentially add to this form.
add class activeSelect to every select, you are using. binding the event to the document will allow you to dynamically change the DOM and the event will be binded to every added element
$(document).ready(function () {
toggleFields(1);
$(document).on("change", ".activeSelect", function () {
toggleFields($(this).attr("id").substr(10));
});
});
function toggleFields(id) {
if ($("#select_box"+id).val() == "two") {
$("#alpha"+id).hide();
$("#beta"+id).show();
}
else {
$("#alpha"+id).show();
$("#beta"+id).hide();
}
}
jsFiddle
Use class as single identifier for fields group and wrap the group like this:
<div class="wrapper">
<input id="alpha1" class="item alpha" name="alpha1" type="text">
<input id="beta1" class="item beta" name="beta1" type="text" style="display:none;">
<select id="select_box1" name="select_box1" class="selectClass">
<option value="one">one</option>
<option value="two">two</option>
</select>
</div>
Then toggle all elements of desired class in selected section:
$('.selectClass').on('change', function(){
var classToShow = $(this).val() == "two" ? "beta" : "alpha";
$(this).closest('.wrapper').find('.item').hide();
$(this).closest('.wrapper').find('.' + classToShow).show();
});

2 variables from select box and hidden input

How can I get 2 different variables from select box and hidden inputs in jquery, i.e:
<select name="startID[]" class="startID">
<option value="1">1</option>
<option value="2">2</option>
<option value="3">3</option>
</select>
<input type="hidden" name="startText[]" value="Text1">
<br />
<select name="startID[]" class="startID">
<option value="1">1</option>
<option value="2">2</option>
<option value="3">3</option>
</select>
<input type="hidden" name="startText[]" value="Text2">
<br />
<select name="startID[]" class="startID">
<option value="1">1</option>
<option value="2">2</option>
<option value="3">3</option>
</select>
<input type="hidden" name="startText[]" value="Text3">
so I have 3 select boxes with 3 hidden inputs, how can I get the value of each select boxed and the text that is attached to? i.e: if I select like this:
Select item is 1 and text is Text1
Select item is 3 and text is Text2
Select item is 2 and text is Text3
Thanks in advance
function getValues() {
$('select').each(function (idx, el) {
console.log("Select item is " + $(el).val() + " and text is " + $(el).next('input[type="hidden"]').val());
});
}
If you want to list the values on change:
$('select.startID,input[type="hidden"]').change(getValues);
Demo (modified a bit):
http://jsfiddle.net/6ev9evew/
NOTE
The updates below are not answers for the original question, but the question's author keeps posting extra questions in the comments! So the solution is above!
UPDATE:
As I can understand this is what you looking for:
function getValues() {
var me = this;
$('select').each(function (idx, el) {
console.log("Select item is " + $(el).val() + " and text is " + $(el).next('input[type="hidden"]').val());
if (el === me) return false;
});
}
So basically we stop the loop at the actual element. But it works only if you pass this function to an event handler.
DEMO 2: http://jsfiddle.net/6ev9evew/1/
UPDATE 2:
So, according to the third question, this is a version of the implementation. As I mentioned below in the comments section, there are multiple ways to implement it. This implementation uses that the array indexes are always in order.
function getValues() {
var result = [];
var me = this;
$('select').each(function (idx, el) {
var $el = $(el);
result[10*$el.val()+idx]=("Select item is " + $el.val() + " and text is " + $el.next('input[type="hidden"]').val()+'<br />');
if (me === el) return false;
});
$('#res').html(result.join(''));
}
$('select.startID,input[type="hidden"]').change(getValues);
DEMO 3:
http://jsfiddle.net/6ev9evew/2/
But you can also implement it with array.sort(fn) but than you do a second iteration on the result set.
Anyway if you have more than ten selects in your real code, don't forget to modify the multiplier at result[10*$el.val()+idx] !
If you want to know the value of the changed select (when the user selects a value on any of them) and also get the value of the input type hidden which is next to it, that's the way:
$('.startID').on('change', function () {
var sel = $(this).val();
var hid = $(this).next('input[type=hidden]').val();
console.log('Select item is ' + sel.toString() + ' and text is ' + hid.toString());
});
Demo
UPDATE
To achieve what you've asked in the comments, you can do it like this:
// Create two arrays to store the values.
var sel = [];
var hid = [];
$('.startID').on('change', function () {
// Put the selected values into the arrays.
sel.push($(this).val());
hid.push($(this).next('input[type=hidden]').val());
console.log(sel);
console.log(hid);
for (var i = 0; i < sel.length; i++) {
console.log('Select item is ' + sel[i].toString() + ' and text is ' + hid[i].toString());
}
});
Demo

how to pass values of dynamically created form fields to a textarea

I'm trying to create a simple form with a dynamic form element 'add'/'remove' feature. Ultimately, once the entries are complete, I would like the form results to display in a textarea.
I searched through this forum and managed to cobble together something that does some, but not all of my goals. It will dynamically make my form elements, and remove them. However, when I try to display the results in my textarea, only the 1st option shows. Further, when I enter something into my text input, all subsequent new fields are prepopulated with that user text. Lastly, since one of my elements is a selectbox, I wonder if someone as a better idea of how to let a user pick among 20-30 options that would be there?
my HTML
<form name="builder">
<fieldset>
<h2>Select your results</h2>
<div id="IPOX">
<p>
<select name="column1" id="col1">
<option value="A">A</option>
<option value="B">B</option>
<option value="C">C</option>
<option value="D">D</option>
<option value="E">E</option>
<option value="F">F</option>
<option value="Z">Z</option>
</select>
<select name="column2" id="col2">
<option value="POS">POS</option>
<option value="NEG">NEG</option>
<option value="EQUIV">EQUIV</option>
</select>
<input type="text" name="usercomm" id="usercomm">
</p>
</div>
<p><span class="add">Add another row</span>
</p>
</fieldset>
<br>
<br>
<p>
<input type="button" id="submit" value="Done!" onclick="printIHC()" />
</p>
</form>
my js
$(window).load(function () {
$(function () {
var defaults = {
'usercomm': '-'
};
// separating set and remove
// note that you could add "defaults" as an arg if you had different
// defaults for different fieldsets
var setDefaults = function (inputElements) {
$(inputElements).each(function () {
var d = defaults[this.name];
if (d) {
// set with jQuery
// we don't need the data - just check on the class
$(this).val(d)
.addClass('default_value');
}
});
};
var removeDefaults = function (inputElements) {
$(inputElements).each(function () {
if ($(this).hasClass('default_value')) {
$(this).val('')
.removeClass('default_value');
}
});
};
setDefaults(jQuery('form[name=builder] input'));
$("span.add").click(function () {
// get the correct fieldset based on the current element
var $fieldset = $(this).closest('fieldset');
var $inputset = $('p', $fieldset)
.first()
.clone()
.insertBefore($('p', $fieldset).last());
// add a remove button
$inputset.append('<span class="remove">Remove</span>');
setDefaults($('input', $inputset));
// return false; (only needed if this is a link)
});
// use delegate here to avoid adding new
// handlers for new elements
$('fieldset').delegate("span.remove", {
'click': function () {
$(this).parent().remove();
}
});
// Toggles
$('form[name=builder]').delegate('input', {
'focus': function () {
removeDefaults($(this));
},
'blur': function () {
// switch to using .val() for consistency
if (!$(this).val()) setDefaults(this);
}
});
});
});
// Print values to textarea
function printIHC() {
var myIHC = document.getElementById('output');
var selectAb = document.getElementById('col1');
selectAb.onchange = this.value;
var selectVal = document.getElementById('col2');
selectVal.onchange = this.value;
var userTxt = document.getElementById('usercomm');
ihcOut = 'Column 1\tValue \tComments\n---------------------------------\n' + selectAb.value + '\t\t' + selectVal.value + '\t' + userTxt.value + '\n';
myIHC.value += ihcOut;
}
I'm no programmer by trade, so I don't know if I should be mixing javascript and jquery. i also have a feeling like when the form elements are dynamically created, they appear silent to the print script. Lastly, many of the topics related to this involve PHP scripts, which I would like to avoid as I don't fully understand how those work.
I tried to make this a jsfiddle, if that is easier to view - http://jsfiddle.net/Vqzk9/
Thanks for any help
The first important thing you should do is changing col1,col2,usercomm from id to class. Because id is unique.
Then I write a new printthis as the following:
function printthis() {
var ihcOut = 'Column 1\tValue \tComments\n---------------------------------\n';
$('fieldset p').each(function(){
if($(this).find('.add').length>0)return;
var selectAb = $(this).find('.col1').val();
var selectVal = $(this).find('.col2').val();
var userTxt = $(this).find('.usercomm').val();
ihcOut += selectAb + '\t\t' + selectVal + '\t' + userTxt + '\n';
});
$('#output').val(ihcOut);
}
Here is jsfiddle http://jsfiddle.net/Vqzk9/1/

How to get the value of a multiple option dropdown?

Say I have this dropdown:
<select name="color" multiple="multiple">
<option value="red">Red</option>
<option value="green">Green</option>
<option value="blue">Blue</option>
</select>
So basically more than 1 color can be selected. What I'd like is that if a user selects red, and then clicks green, i'd like a function to be called each time which pops up a message box saying the color which was most recently clicked.
I've tried this:
<option value="red" onclick="alert('red');">Red</option>
<option value="green" onclick="alert('green');">Green</option>
<option value="blue" onclick="alert('blue');">Blue</option>
This works in firefox and chrome, but not in IE.
Any ideas?
$("select[name='color']").change(function() {
// multipleValues will be an array
var multipleValues = $(this).val() || [];
// Alert the list of values
alert(multipleValues[multipleValues.length - 1]);
});
Here's another examples: http://api.jquery.com/val/
The following code should do what I think you're after. Each time an item is selected, it compares the current list of selections against the previous list, and works out which items have changed:
<html>
<head>
<script type="text/javascript">
function getselected(selectobject) {
var results = {};
for (var i=0; i<selectobject.options.length; i++) {
var option = selectobject.options[i];
var value = option.value;
results[value] = option.selected;
}
return results;
}
var currentselect = {};
function change () {
var selectobject = document.getElementById("colorchooser");
var newselect = getselected(selectobject);
for (var k in newselect) {
if (currentselect[k] != newselect[k]) {
if (newselect[k]) {
alert("Option " + k + " selected");
} else {
alert("Option " + k + " deselected");
}
}
}
currentselect = newselect;
}
</script>
</head>
<body>
<select id="colorchooser"
name="color"
multiple="multiple"
onchange='javascript:change();'
>
<option value="red">Red</option>
<option value="green">Green</option>
<option value="blue">Blue</option>
</select>
</body>
</html>
It should work just as well in Internet Explorer as Firefox et al.
Since you using jQuery,I suggest you to take a look at this superb plugins. This plugins will transform a multiple select dropdown into a checkbox list, so user can select multiple values with easy.
To get the values, I suggest you use fieldValue methods from jQuery form plugins. It's a robust way to get value from any type of form element. Beside, you can use this plugins to submit your form via AJAX easily.
This will alert only the last (most recent) selected value. Calling $(this).val() using the select's change handler will return an array of all your selected values:
$(document).ready(function() {
$("select[name=color] option").click(function() {
alert($(this).val());
});
});
I am not sure what you exactly want. This will always alert the last selected color:
$(function(){
var selected = Array();
$('select[name=color] option').click(function() {
if($(this).is(':selected')) {
selected.push($(this).val());
}
else {
for(var i = 0; i < selected.length;i++) {
if(selected[i] == $(this).val()) {
selected = selected.splice(i,1);
}
}
}
alert(selected[selected.length -1])
});
});
The array is used to maintain the history of selected colors.
For the last clicked color, it is simpler:
$(function(){
$('select[name=color] option').click(function() {
alert($(this).val());
});
});
This is so complicated to accomplish that I used a simpler option of listing the items with a checkbox next to them and a select/unselect all button. That works much better and is also supported by IE. Thanks to everyone for their answers.

Categories