I have a drag and drop thing which uses clone. I am having a problem with the date clone though because of datepicker. Therefore, I need to make sure each cloned datepicker has a unique id. A cloned element looks like the following
<div data-type="date" class="form-group">
<label class="control-label col-sm-5" for="dateInput">Date Input:</label>
<div class="col-sm-3">
<input type="text" name="dateInput[]" class="form-control date_picker" id="dateInput">
</div>
</div>
So if I clone two date inputs, I will have two of the above. Now on submit, I clean all of the cloned html, doing things like removing the data-type. At this stage, if there is a cloned date input, I need to give it a unique id. At the moment I am doing this
$("#content").find(".form-group").each(function() {
var html = $(this).attr('class', 'form-group')[0].outerHTML.replace(/ data-(.+)="(.+)"/g, "");
var input = $(this).find('input');
var i = 0;
if(input.attr('id') == 'dateInput') {
alert("TEST");
input.attr("id",'dateInput' + i).datepicker();
i++;
}
console.log(html);
dataArray.push(html);
});
The TEST alert fires twice as it should do if I clone 2 date inputs. However, the id attributes do not seem to change when I output the html to the console. I have set up the following Fiddle to demonstrate that the id of the element is not changing.
Any advice on getting this to change appreciated.
Thanks
Try defining dataArray, i outside out submit event, .each() , using .map() , .get() , .attr(function() {index, attr}) , .outerHTML
$(function() {
// define `i` , `dataArray`
var i = 0, dataArray = [];
$('#content').submit(function(event) {
event.preventDefault();
$("#content").find(".form-group").each(function() {
var html = $(this).attr('class', '.form-group')[0]
.outerHTML.replace(/ data-(.+)="(.+)"/g, "");
dataArray.push($(html).map(function(_, el) {
// adjust `input` `id` here , return `input` as string
return $(el).find("input").attr("id", function(_, id) {
return id + (++i)
})[0].outerHTML
}).get()[0])
});
$("#output")[0].textContent = dataArray.join(" ");
console.log(dataArray)
});
});
jsfiddle https://jsfiddle.net/mLgrfzaL/2/
Related
I have a form where users can create recipes. I start them off with one ingredient field (among others) and then use .append() to add as many more as they want to the div container that holds the first ingredient. The first input field has an id of IngredientName1 and dynamically added input fields are IngredientName2, IngredientName3, etc.
When they start typing in the input field, I pop a list of available ingredients filtered by the value they key into IngredientNameX. When they click on an ingredient in the list, it sets the value of the IngredientNameX field to the text from the div - like a search & click to complete thing. This all works very well; however, when you add IngredientName2 (or any beyond the one I started them with initially) clicking on an available ingredient sets the values of every single IngredientNameX field. No matter how many there are.
I hope this is enough context without being overly verbose, here's my code (I've removed a lot that is not relevant for the purpose of posting, hoping I didn't remove too much):
<div id="ingredientsContainer">
<input type="hidden" id="ingredientCounter" value="1">
<div class="ingredientsRowContainer">
<div class="ingredientsInputContainer"><input class="effect-1 ingredientsInput" type="text" name="IngredientName1" placeholder="Ingredient" id="IngredientName1" data-ingID="1"><span class="focus-border"></span></div>
</div>
<input type="hidden" name="Ingredient1ID" id="Ingredient1ID">
</div>
<script>
$(document).ready(function(){
$(document).on('keyup', "[id^=IngredientName]",function () {
var value = $(this).val().toLowerCase();
var searchValue = $(this).val();
var valueLength = value.length;
if(valueLength>1){
var theIngredient = $(this).attr("data-ingID");
$("#Ingredients").removeClass("hidden")
var $results = $('#Ingredients').children().filter(function() {
return $(this).text() === searchValue;
});
//user selected an ingredient from the list
$(".ingredientsValues").click(function(){
console.log("theIngredient: "+theIngredient);//LOGS THE CORRECT NUMBER
var selectedIngredientID = $(this).attr("id");
var selectedIngredientText = $(this).text();
$("#IngredientName"+String(theIngredient)).val(selectedIngredientText);//THIS IS WHAT SETS EVERYTHING WITH AN ID OF IngredientNameX
$("#Ingredient"+String(theIngredient)+"ID").val(selectedIngredientID);
$("#Ingredients").addClass("hidden");
});
$("#Ingredients *").filter(function() {
$(this).toggle($(this).text().toLowerCase().indexOf(value) > -1)
});
} else {
$("#Ingredients").addClass("hidden")
}
});
$("#AddIngredient").click(function(){
var ingredientCounter = $("#ingredientCounter").val();
ingredientCounter++;
$("#ingredientCounter").val(ingredientCounter);
$('#ingredientsContainer').append('\
<div class="ingredientsRowContainer">\
<div class="ingredientsInputContainer"><input class="effect-1 ingredientsInput" type="text" name="IngredientName'+ingredientCounter+'" placeholder="Ingredient" id="IngredientName'+ingredientCounter+'" data-ingID="'+ingredientCounter+'"><span class="focus-border"></span></div>\
</div>\
<input type="hidden" name="Ingredient'+ingredientCounter+'ID" id="Ingredient'+ingredientCounter+'ID">\
');
});
});
</script>
[UPDATE] I realized the problem is happening because the function is running multiple times. I assume this happening because I'm calling a function on keyup of a field whose id starts with IngredientName so when one has a key up event, all existing fields run the function. How do i modify my:
$(document).on('keyup', "[id^=IngredientName]",function () {
to only run on the field with focus?
I have a bit of HTML here:
<tr taskId="(#=obj.task.id#)" assigId="(#=obj.assig.id#)" class="assigEditRow" >
<td><select name="resourceId" class="get-resources formElements"></select></td>
<td><span class="resources-units"></span></td>
<td><span class="resources-quantity"></span></td>
<td><input type="text" placeholder="Required Q"></td>
<td align="center"><span class="teamworkIcon delAssig" style="cursor: pointer">d</span></td>
</tr>
And a bit of JS here:
'use strict';
function addResourceFunction(){
let ResourcesJSON = (json) => {
let Resources = json;
console.log(Resources);
let contactsLength = json.length;
let arrayCounter = -1;
let resID;
let resName;
let resUnit;
let resQuantity;
let Option = $('<option />');
let assignedID = $('tr.assigEditRow:last').attr("assigId");
while(arrayCounter <= contactsLength) {
arrayCounter++;
resID = Resources[arrayCounter].ID;
resName = Resources[arrayCounter].name;
resUnit = Resources[arrayCounter].unit;
resQuantity = Resources[arrayCounter].quantity;
$('.assigEditRow').last().find('select').append($('<option>', {
value: resName.toString(),
text: resName.toString(),
resourceID: resID.toString(),
resourceUnit: resUnit.toString(),
resourceQuantity: resQuantity.toString()
}));
}
}
$.getJSON("MY JSON URL IS HERE", function(json) {
ResourcesJSON(json);
});
};
So what's actually going on here: I get my data from the URL (JSON array), trigger the addResourceFunction() on click to create a new table row and to add a new select with options passed from the array. As you see from my HTML markup, the select input is placed in td.get-resources, and all that works good. I get my date set, I populate the select field and all works good. I can add as many rows/select dropdowns as I want.
Also, every option has a few custom attributes (you can see it in my JS code above), and I want to add the values of those attributes to the second and third column of the row (in HTML those are span.resources-units and span.resources-quantity). The thing is, I have no clue how to make it work 1:1, meaning that one select dropdown "alters" only units and quantity of its own row. Below is the code for that:
let idCounter = 1;
$(document).on('change', '.get-resources', function() {
$('.assigEditRow').last().find('.resources-units').attr('id', 'units-' + idCounter);
$('.assigEditRow').last().find('.resources-quantity').attr('id', 'quantity-' + idCounter);
this.resourceUn = $( ".get-resources option:selected" ).attr( "resourceUnit" );
this.resourceQuant = $( ".get-resources option:selected" ).attr( "resourceQuantity" );
$('#units-' + idCounter).append(this.resourceUn);
$('#quantity-' + idCounter).append(this.resourceQuant);
idCounter++;
});
What happens is that if I add one select input, and change options, the thing works. When I add another one and change its options, it gets attributes of the first one. Adding more - same thing. Whatever I change, it takes the attribute value of the first item added.
Try getting the id from the element instead of from the variable, since you always update the element with the id of the counter, instead of the element with the id of the row that was clicked.
Hmm, what does the counter do exactly? The more I look at it, the less I understand. What I do know is that you're not selecting the correct elements by using the idCounter to reference the correct row.
You want to do something like
$(document).on('change', '.get-resources', function() {
//var row = this;
this.find(/* Some path to the second column */).att(/* some att to change */);
this.find(/* Some path to the third column */).att(/* some att to change */);
});
where you always use the row as the root again, instead of finding a certain id, so you only update that row.
Native:
<table>
<tr>
<td>
<select>
<option data-text="resName1" data-resourceID="resID1" data-resourceUnit="resUnit1" data-resourceQuantity="resQuantity1">1</option>
<option data-text="resName2" data-resourceID="resID2" data-resourceUnit="resUnit2" data-resourceQuantity="resQuantity2">2</option>
<option data-text="resName3" data-resourceID="resID3" data-resourceUnit="resUnit3" data-resourceQuantity="resQuantity3">3</option>
</select>
</td>
<td>
<div class="column2"></div>
</td>
<td>
<div class="column3"></div>
</td>
</tr>
</table>
<script>
document.addEventListener('change', function ( event ) {
var select = event.target,
option = select.options[select.selectedIndex],
values = {
'text' : option.getAttribute('data-text'),
'resourceID' : option.getAttribute('data-resourceID'),
'resourceUnit' : option.getAttribute('data-resourceUnit'),
'resourceQuantity' : option.getAttribute('data-resourceQuantity')
},
row = select.parentNode.parentNode,/* depending on how deep the select is nested into the tr element */
column2 = row.querySelector('.column2'),
column3 = row.querySelector('.column3');
column2.textContent = 'some string with the values you want';
column3.textContent = 'some string with the other values you want';
});
</script>
Basically you start with the select that was changed, from there you get the option node that was clicked. Then you get the attributes you need from that option. Then you go up a few nodes to the row parent and find the two columns inside that row. Then you can set the content of these two columns.
http://jsfiddle.net/4px4whk0/
I have two question
when first click the checkbox (a), the first console.log should be print the dom with empty data-selected-list attribute, I don't know why it filled what I click already (["a"]) ?
I have to set timeout wrap the container.attr('data-selected-list', selectedList); then it works like what I want.
when click other checkbox (b), I hope it will be ["a","b"] store in attribute. but it only store ["b"] , why?
I hope it can be solve by store data in html attribute not only store in jquery data api
$(document).ready(function() {
$('.container').on('click', 'input[type="checkbox"]', function() {
var container = $(this).closest('.container');
var input = $(this);
console.log(container);
var selectedList = container.data('selected-list');
if (selectedList == '') {
selectedList = [];
}
if (input.is(":checked")) {
selectedList.push(input.val());
}
console.log(selectedList);
selectedList = JSON.stringify(selectedList);
container.attr('data-selected-list', selectedList);
});
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<div class="container" data-selected-list="">
<input type="checkbox" value="a">a
<input type="checkbox" value="b">b
<input type="checkbox" value="c">c
</div>
You have several mistakes:
if you want to print the results of your array before you change it, then move the console.log call to before you push to the array
you were using attr and data interchangeably. These do two different things. attr stores the data in the DOM and data is a jquery method that stores the data somewhere within jquery itself.
finally, if you're using attr you need to do JSON.stringify to serialize your array before storing it (which you did do correctly) but when you pull the data out of the DOM you have to convert it back to an array with JSON.parse
jquery's attr returns undefined for an undefined DOM tag, not empty string
the right solution with these problems fixed is:
$(document).ready(function() {
var container = $('.container');
container.on('click', 'input[type="checkbox"]', function() {
var input = $(this);
console.log(container);
var selectedList = container.attr('data-selected-list');
if (!selectedList) {
selectedList = [];
}else{
selectedList = JSON.parse(selectedList);
}
console.log(selectedList);
if (input.is(":checked")) {
selectedList.push(input.val());
}
selectedList = JSON.stringify(selectedList);
container.attr('data-selected-list', selectedList);
});
});
here's a fiddle: http://jsfiddle.net/yLz6uv1q/
The following code populates the second select statement with HTML data. The problem I'm facing is that I clone the two select statements and on submission of the form, I'd like to save all of the selected option values from the two selects into an Array().
What would be the best way to iterate through all of the drop-down values (there's a maximum of 5 that can be added for both Subject Matter and Category)?
Thanks in advance.
$(".SubjectCategory").live("click", function () {
var $this = $(this);
var $elem = $this.closest('div').nextAll('div').first().find('select');
var a = $this.val();
$.get("/userControls/BookSubjectHandler.ashx?category=" + a, {}, function (data) {
$elem.html(data);
});
});
<div class="singleField subjectField">
<label id="Category" class="fieldSml">Subject Matter</label>
<div class="bookDetails ddl"><select id="ddlSubjectMatter" class="fieldSml SubjectCategory"></select></div>
<label id="Subjects" class="fieldSml">Category</label>
<div class="bookDetails ddl" id="subjectMatter"><select id="ddlSubjects" class="fieldSml Subjects"></select></div>
</div>
Using jQuery .map function you can retrieve all values at once:
var arrayOfValues = $(".bookDetails.ddl select").map(function (i, el) { return $(el).val(); }).get();
fiddle: http://jsfiddle.net/e9zxY/
Let's say I have three related text input fields:
<input name="x1" class="xField" value="" /><div class="note" style="display:none;">Match</div>
<input name="x2" class="xField" value="" /><div class="note" style="display:none;">Match</div>
<input name="x3" class="xField" value="" /><div class="note" style="display:none;">Match</div>
Now, when the value of any of these changes, and it matches the value of another one of the related fields, I want to "un-hide" the .note div next to each input with matching value.
So, this is what I have so far:
$(function() {
$(".xField").change(updateCommentsDisplay);
function updateCommentsDisplay() {
var $aVisibleNotes = [];
$(".xField").each(function() {
// If more than one field with the same value, add the next note div to an array
if ($(".xField[value="+$(this).val()).length > 1)
$aVisibleNotes.push($(this).next(".note");
});
$.each($aVisibleNotes, function(){
$(this).show();
});
}
});
This just doesn't feel efficient. I feel like I should be able to do this without loops. Any suggestions?
To do it without loops, simply use selectors. Also specify not(this) if you don't want the input to show the note following it.
$(".xField").keyup(function(){
var val=$(this).val();
$('.xField[value='+val+']').not(this).next(":hidden").show();
});
You can combine selectors to get the result....xField, same value, different input = show the next note.
$('.xField').change(function() {
var x = $(this);
$('.xField[value="' + x.val() + '"]')
.not(this).next('.note').show();
});
Instead of looping through each input just use a selector to find the inputs whose value matches:
$(function() {
$(".xField").change(function(){
var field_val = $(this).val();
$(".xField[value='" + field_val + "']").show();
});
});
For one thing, you should save the results of $(".xField');
So:
$(function() {
var xFields = $('.xField');
xFields.change(updateCommentsDisplay);
function updateCommentsDisplay() {
var $aVisibleNotes = [];
xFields.each(function() {
// If more than one field with the same value, add the next note div to an array
if(xFields.find('[value="' + $(this).val() + '"]').size() > 1)
$aVisibleNotes.push($(this).next(".note");
});
$.each($aVisibleNotes, function(){
$(this).show();
});
}
});
It also might be faster to use an actual value comparison instead of the Sizzle engine to find matches. You'd want to test it on jsperf, but I have a hunch.
turn them on and off if matches or no matches:
$(".xField").change(function() {
updateCommentsDisplay();
});
function updateCommentsDisplay() {
$(".xField").each(function() {
if ($(".xField[value=" + $(this).val() + "]").length > 1) {
$(this).next(".note").show();
}
else {
$(this).next(".note").hide();
};
});
};
See it in action: http://jsfiddle.net/MarkSchultheiss/UrBEk/