OnChange event doesn't seem to get hooked correctly - javascript

Probable something really stupid but suppose I have 2 elements that match $('[id$=_product_id]') why are the change events not matched properly?
var numberPattern = /\d+/g;
$('[id$=_product_id]').each(function(idx, elem) {
recordId = elem.id.match(numberPattern)
productId = elem.value;
console.log(recordId);
$("#client_order_order_lines_attributes_" + recordId + "_product_id").on("change", function(e) {
console.log(recordId);
})
});
I created a fiddle that demonstrates this http://jsfiddle.net/hLYpE/1/
What am I missing?

You need to declare those variables, that's the danger of implicit globals.
var recordId = elem.id.match(numberPattern);
var productId = elem.value;
Demo: http://jsfiddle.net/elclanrs/hLYpE/4/

You should change the code adding the var in front of the name of the variables:
var numberPattern = /\d+/g;
$('[id$=_product_id]').each(function(idx, elem) {
var recordId = elem.id.match(numberPattern),
productId = elem.value;
console.log(recordId);
$("#client_order_order_lines_attributes_" + recordId + "_product_id").on("change", function(e) {
console.log(recordId);
})
});

Related

Click Delete link for each duplicate href?

In the following code, I have some divs with <a> tags, each containing a href of /User.aspx?ID=[some ID]. I wish to click the Delete <a> tag under the parent of the parent of the parent of any divs with duplicate ID's in the href.
Here is my code:
var z = 0;
var info = {};
$("a:contains('Delete')").each(function() {
z++;
var x = $(this).parent().parent().parent().find("span.UserLink a").attr("href");
var id = x.replace("/User.aspx?ID=", "");
info[z] = id;
console.log(info[z]);
});
var uniqueIds = {};
$.each(info, function(i, el){
if($.inArray(el, uniqueIds) === -1) { uniqueIds.push(el) }
else { $("html").find("span.UserLink a[href='/User.aspx?ID='"+info[i]+"']").parent().parent().parent().find("a:contains('Delete')").click() }
});
Use arrays, not objects
Maybe just a typo, I think you wanted to use arrays for info and uniqueIds
var info = [];
var uniqueIds = [];
jQuery.each already provides an index
You don't need z
$("a:contains('Delete')").each(function(index) {
var x = $(this).parent().parent().parent().find("span.UserLink a").attr("href");
var id = x.replace("/User.aspx?ID=", "");
info[index] = id;
console.log(info[z]);
});
Use meaningful names
x and info aren't very good names, you could try (for example) userLinkHref and foundIds
You could store the delete button in the first loop and use it in the second loop
var foundDeleteLinks = [];
$("a:contains('Delete')").each(function() {
var $deleteLink = $(this);
var userLinkHref = $deleteLink.parent().parent().parent().find("span.UserLink a").attr("href");
var id = userLinkHref.replace("/User.aspx?ID=", "");
foundDeleteLinks.push({id:id,$deleteLink:$deleteLink});
console.log(id);
});
var uniqueIds = [];
$.each(foundDeleteLinks, function(i, deleteLink){
var id = deleteLink.id;
if($.inArray(id, uniqueIds) === -1) { uniqueIds.push(id) }
else {
deleteLink.$deleteLink.click();
}
});
You may be able to do it in one loop
var foundIds = [];
$("a:contains('Delete')").each(function() {
var $deleteLink = $(this);
var userLinkHref = $deleteLink.parent().parent().parent().find("span.UserLink a").attr("href");
var id = userLinkHref.replace("/User.aspx?ID=", "");
if($.inArray(id, foundIds) === -1) { foundIds.push(id) }
else { $deleteLink.click(); }
});
I hope that helps enough to find your problems.

Using wildcards in js function

I have a function using which I am getting the auto sum for a particular field in a row.
$(function () {
$('#add_iaDetails_table').on('change', '[name=days_0]', function () {
var $row = $(this).closest('tr');
var $rate = $row.find('input[name=rate_0]');
var $days = $row.find('input[name=days_0]');
var $cost = $row.find('input[name=cost_0]');
var rate = parseFloat($rate.val());
var days = parseFloat($days.val());
$cost.val(rate * days);
});
});
But the thing is I am appending the rows in the table dynamically and I am the name of the fields as well eg. days_1,days_2.....rate_1,rate_2....
My function here will calculate the autosum for only the first row which matches with values given in function.
Is there any way in which I can match all the appended values like days_* , rate_*
Please suggest a solution. Thanks in advance.
A simple
$(function () {
$('#add_iaDetails_table').on('change', '[name=days_0]', function () {
var $row = $(this).closest('tr');
var $rate = $row.find('input[name*=rate_]');
var $days = $row.find('input[name*=days_]');
var $cost = $row.find('input[name*=cost_]');
var rate = parseFloat($rate.val());
var days = parseFloat($days.val());
$cost.val(rate * days);
});
});
would work where * implies that you select any input with attribute containing the given string or you may use the startswith selector from JQUERY where in above code you replace * by ^
To accommodate dynamic content, I would be a bit more flexible with your selector; just filter on the name starting with days_, and then grab that id at the end dynamically.
Something like this:
$('#add_iaDetails_table').on('change', '[name^=days_]', function () {
var name = $(this).attr('name');
var nameId = name.substr(name.indexOf('_') + 1);
var $row = $(this).closest('tr');
var $rate = $row.find('input[name=rate_' + nameId + ']');
var $days = $row.find('input[name=days_' + nameId + ']');
var $cost = $row.find('input[name=cost_' + nameId + ']');
var rate = parseFloat($rate.val());
var days = parseFloat($days.val());
$cost.val(rate * days);
});
you can use starts-with selector, like
$('#add_iaDetails_table').on('change', '[name^=days_]', function () {
var uid = $(this).attr("name").split("_")[1]; //get the unique id from 'name' attribute
var $row = $(this).closest('tr');
var $rate = $row.find('input[name=rate_' + uid + ']');
var $days = $row.find('input[name=days_' + uid + ']');
var $cost = $row.find('input[name=cost_' + uid + ']');
var rate = parseFloat($rate.val());
var days = parseFloat($days.val());
$cost.val(rate * days);
});
jQuery has a whole bunch of different selectors
http://api.jquery.com/category/selectors/
Based on your question you probably want the Starts With refiner
http://api.jquery.com/attribute-starts-with-selector/
$('#add_iaDetails_table').on('change', '[name=^days_]', function () { ... }
The scalable/cross browser solution I can think of is, for every input element add a class that you can identify row/column index. This way you can do horizontal or vertical queries and do whatever you want.

Javascript / jQuery Addition Game

I am working on a Javascript math game and could use some guidance. I need to create a 10 problem game that presents users with questions one at a time for simple addition. I started on something but am already running into trouble. I also need some way to present the users with the answers they got right and wrong in the end.
Any help available? It would be greatly appreciated.
I have included in the answer while I was working on it but obviously I need the user to submit the answer themselves and store that.
Here's a fiddle:
$(document).ready(function() {
//declare arrays and variables for use below
var pNum1 = new Array();
var pNum2 = new Array();
var pNumCarry=0;
var pNumAns = new Array();
var ans = new Array();
var score=0;
function pNumGen(x) {
pNum1[x] = (Math.round(Math.random()*51));
pNum2[x] = (Math.round(Math.random()*51));
pNumAns [x] = pNum1[x] + pNum2[x];
$(".pNum1").html(pNum1[x]);
$(".pNum2").html(pNum2[x]);
$(".pNumAns").html(pNumAns[x]);
}
$(".Play").click(function() {
pNumTrack = 0;
pNumGen(pNumTrack);
});
});
To get user input you may want to look at window.prompt:
As for presenting the users with right/wrong answers in the end, is a very non-specific question and would depend on how you implement the rest of the game.
One possibility might be to use an array where you can push some text to after each guess, e.g. "Question #1: 15 + 39 = 105 - Incorrect", and then looping through and printing the array contents when the game is over.
Ok. I just created little example how you can start to create your game:
http://jsfiddle.net/ePjGs/1/
$(function() {
getQuestion();
});
var count = 0,
results = [];
function getQuestion() {
count++;
var container = $('<div />');
var val1 = Math.round(Math.random()*51);
var val2 = Math.round(Math.random()*51);
var lbl = $('<label />');
lbl.html(val1 + ' + ' + val2 + ' = ');
container.append(lbl);
var input = $('<input type="text" />');
container.append(input);
var btn = $('<input type="button" value="Submit result" />');
var val;
btn.click(function() {
results.push({
number1 : val1,
number2 : val2,
answer : input.val()
});
input.attr('disabled', true);
$(this).attr('disabled', true);
$(this).after(function() {
if(val1 + val2 == input.val()) return 'SUCCESS!';
return 'ERROR';
});
getQuestion();
updateResults();
});
container.append(btn);
$('body').append(container);
}
function updateResults() {
$('#json').html(JSON.stringify(results));
}

JS getElementsByTagName except the one with a specific class

I have this variable:
var _inputs = document.getElementsByTagName('input');
But I want to make a new variable that will select all inputs, except the inputs that have a specific class like ".nothis".
var _newinputs = document.getElementsByTagName('input') && document.getElementsByClass('!nothis');
document.querySelectorAll('input:not(.nothis)')
Try this code:
var _inputs = Array.prototype.filter.call(
document.getElementsByTagName("input"),
function(obj) {
return obj.className.split(" ").indexOf("nothis")===-1;
});
Try this:
var _inputs = document.getElementsByTagName('input');
var filteredInputs = [];
var re = new RegExp('\\b' + 'nothis' + '\\b'); // or any other class name
for(var i=0; i<_inputs.length;i++) {
if (!re.test(input.className)) { // filter out by class name
filteredInputs.push(_inputs[i]);
}
}
Update: Added regex match to eliminate false positives as per suggestion from katspaugh
In modern browsers you can do
var inputs = document.querySelectorAll('input:not(.nothis)');

jQuery: How to remove this code redundancy?

clone.find('[id]').each(function() {
id = $(this).attr('id');
ind = id.search(/\d+$/);
$(this).attr('id', id.substr(0,ind)+id_counter);
});
clone.find('[for]').each(function() {
id = $(this).attr('for');
ind = id.search(/\d+$/);
$(this).attr('for', id.substr(0,ind)+id_counter);
});
I know I can find elements that have either the id attribute or the for attribute, but then how do I know which one I need to set?
If all you're trying to do is reduce code, you could...
function doTheseThings(element) {
clone.find(element).each(function() {
var id = $(this).attr('id');
var ind = id.search(/\d+$/);
$(this).attr('id', id.substr(0,ind)+id_counter);
});
}
doTheseThings('[id]');
doTheseThings('[for]');
EVEN BETTER:
I decided to meld my first answer with some of #SHiNKiROU's ideas and add some more Jquery-ish syntax:
var items = ['[id]', '[for]'];
$.each(items, function (index, element) {
clone.find(element).each(function() {
var id = $(this).attr('id');
var ind = id.search(/\d+$/);
$(this).attr('id', id.substr(0,ind)+id_counter);
});
});
var list = ['id', 'for']
for (var i in list) {
var v = list[i];
clone.find('[' + v + ']').each(function() {
id = $(this).attr(v);
ind = id.search(/\d+$/);
$(this).attr(v, id.substr(0,ind)+id_counter);
});
}
delete list;
Well, the redundency is in that the same code applies to the different attributes, so you factor those out:
$.each(['id', 'for'], function (ignr, attr) {
clone.find('['+attr+']').each(function() {
val = $(this).attr(attr);
ind = val.search(/\d+$/);
$(this).attr(attr, val.substr(0,ind)+id_counter);
});
});
Then, if you want, you could remove a local variable to shorten it, but I'm not sure this is an improvement:
$.each(['id', 'for'], function (ignr, attr) {
clone.find('['+attr+']').each(function() {
val = $(this).attr(attr);
$(this).attr(attr, val.substr(0,val.search(/\d+$/))+id_counter);
});
});

Categories