i am in trouble with javascript‘s callback,my code seems simple:
var i =0;
for (; i < listSize+1; i++) {
var content = "content"+i;
$("#" + content).focus(function () {
$("#" + content).keydown(check(new Number(i)));
$("#" + content).keyup(check(new Number(i)));
});
}
where lisetSize=3 in my test case and content is the html element's id
and the callback function check(my_num) is:
function check(my_num) {
var content = "content"+my_num;
}
then i try to trigger this function through keyboard input.
however,i got the result that content=content4 all the time via my broswer's debugger,even though the listening element is content0
i have try anyway such as $.extend({},i) $.extend(true,{},i)
it make no difference
now i have no idea about this problem,how can i just pass a value but no reference to the callback function's parameter?
You're not declaring the handlers correctly.
Replace:
$("#" + content).keydown(check(new Number(i)));
$("#" + content).keyup(check(new Number(i)));
With:
$("#" + content).keydown(function(){check(new Number(i));});
$("#" + content).keyup(function(){check(new Number(i));});
What you need to pass to keyup and keydown, are functions that need to be called when keyboard events happen.
What you were passing to keyup and keydown, were the results of calling check(new Number(i)).
Also, since you're declaring these in a loop, you'll want to copy the number to a new variable, in order to reference the current loop iteration's value:
$("#" + content).focus(function () {
var currentNumber = i;
$("#" + content).keydown(function(){check(currentNumber);});
$("#" + content).keyup(function(){check(currentNumber);});
});
Thanks for Cerbrus,even though there is still problem.
Now I realize that the problem was caused by misunderstanding the real running order.
Even after the loop ends up, $("#" + content).focus will still be called once user click the element.And then,the program starts the code
function () {
$("#" + content).keydown(function(){check(currentNum);});
$("#" + content).keyup(function(){check(currentNum);});
}
As the loop has ended,currentNum=4,so everything got an error.
Here is my solution:
for (var i = 0; i < listSize + 1; i++) {
var content = "content" + i;
$("#" + content).focus(function () {
$(this).keydown(function () {
check($(this));
});
$(this).keyup(function () {
check($(this));
});
});
}
function check(trigger) {
var my_num = getContentNum(trigger);
}
function getContentNum(content) {
return (content.attr("id").charCodeAt(7))-48;
}
Not elegant but useful.
Related
Can anyone tell me why my IF statement is firing before updating the UI with the each loop?
The code basically wants to delay adding css classes to the UI then once each one has been added, redirect the user. It currently just directs immediately?!
$("#logo").click(function() {
//define variables:
var eventDuration = 500;
var elementArray = ['ribbon', 'left-panel', 'wid-id-1', 'wid-id-2'];
var animationArray = ['slideOutRight', 'slideOutLeft', 'rotateOutUpRight', 'rotateOutUpRight'];
//Loop through elements and update UI with timer function:
$.each(elementArray, function(index, value) {
//Increments the delay of the element updates:
var delaytimer = index * eventDuration + eventDuration;
//Adds animation css classes to onpage elements:
$('#' + value).delay(delaytimer).queue(function() {
$(this).addClass('animated ' + animationArray[index]).dequeue();
});
//Once complete redirect to the home page:
if (index === 3) {
$(this).delay(delaytimer + 500).queue(function() {
window.location.replace('/').dequeue;
});
}
});
});
Your if statement is being executed immediately because it isn't inside the delay function. Try moving it in there.
$('#' + value).delay(delaytimer).queue(function() {
$(this).addClass('animated ' + animationArray[index]).dequeue();
//Once complete redirect to the home page:
if (index === 3) {
$(this).delay(delaytimer + 500).queue(function() {
window.location.replace('/').dequeue;
});
}
});
Me and my loops again...
I'm trying to run a for loop across several divs, each being in the class "tooltipBox" but with different ids. In each of these divs is an input text field with class "tttFalloutOrder". What I want to do in the for loop is to attach a click-event-listener on each .tttFalloutOrder input field.
This is my code so far:
function installListener(elementId) {
$( "div#" + elementId + " > .tttFalloutOrder" ).on("click", function() {
alert("clicked on " + elementId);
});
}
function runSimulation() {
alert("running simulation...");
$( "#lContent h2" ).html("Simulation <b>in progress...</b>");
var agents = $( "div.tooltipBox" );
var rFOs = $( ".rFO" );
var i, j = 0;
for(i = 0, j = 0; i < agents.length, j < rFOs.length; i++, j++) {
var ttl = rFOs[j].value;
if((ttl == "undefined") || (ttl == "n")) {
continue;
} else {
// function-factory als gscheite closure,
// siehe http://stackoverflow.com/questions/750486/javascript-closure-inside-loops-simple-practical-example
// und http://stackoverflow.com/questions/3572480/please-explain-the-use-of-javascript-closures-in-loops?answertab=votes#tab-top
(function(i, ttl) {
var agentId = agents[i].id;
installListener(agentId);
/*
$( "div#" + agentId + " > .tttFalloutOrder" ).on("change keypress paste focus textInput input", function() {
alert(agentId + "just got changed!");
});
*/
setTimeout(function() {
$("div#" + agentId + " > div.offlineCover").fadeIn(500);
}, ttl*1000);
})(i, ttl);
}
}
$( "#lContent h2" ).html("Simulation <b>complete</b>");
}
As you can see, I am using a closure and even delegated the actual task of attaching the listener to another function, after reading in several SO-answers related to event-listeners in loops that this would help...though I honestly don't quite see how that would make any difference. Anyway, the click listeners still won't fire and frankly I don't understand what is - or rather what is not - happening here.
Thanks in advance - you people at SO have always found a way to point unknowing souls like me into the right direction, and I really appreciate that.
Update
Case closed due to my own stupidity...
First off, yes, I had an undefined member sitting in my installListener() function.
Second, the jQuery selector $( "div#" + elementId + " > .tttFalloutOrder" ) returned undefined, since the > operator selects the second element, which has the first element as a direct parent. However, since .tttFalloutOrder is an input field sitting inside a <form> tag, that is not the case...
I now scrapped the function installListener() and solved the issue with the following code:
function runSimulation() {
alert("running simulation...");
$( "#lContent h2" ).html("Simulation <b>in progress...</b>");
var agents = $( "div.tooltipBox" );
var rFOs = $( ".rFO" );
var waitUntilEvaluate = 0;
var i, j = 0;
for(i = 0, j = 0; i < agents.length, j < rFOs.length; i++, j++) {
var ttl = rFOs[j].value;
if((ttl == "undefined") || (ttl == "n")) {
continue;
} else {
// function-factory als gscheite closure,
// siehe http://stackoverflow.com/questions/750486/javascript-closure-inside-loops-simple-practical-example
// und http://stackoverflow.com/questions/3572480/please-explain-the-use-of-javascript-closures-in-loops?answertab=votes#tab-top
(function(i, ttl) {
var agentId = agents[i].id;
$( "div#" + agentId + " .tttFalloutOrder" ).on("input", function() {
alert(agentId + "just got changed!");
$( "div#" + agentId + " .wasChanged" ).prop("checked", true);
});
setTimeout(function() {
$("div#" + agentId + " > div.offlineCover").fadeIn(500);
}, ttl*1000);
waitUntilEvaluate = waitUntilEvaluate + ttl * 1000;
})(i, ttl);
}
}
console.log(waitUntilEvaluate);
setTimeout(function() {
$( "#lContent h2" ).html("Simulation <b>complete</b>");
evaluate();
}, waitUntilEvaluate);
}
You will find it easier to loop with jQuery.each() rather than for(). The .each() callback function will automatically trap the var you need, so there's no need to make another, inner closure.
The most likely thing that's preventing the click handler from working is listenerKind. If no such member exists, then an error will be thrown and the event thread will die.
Your biggest issue is knowing when to change the "in progress" message to "complete". As it stands, the message will change back immediately without waiting for any of the setTimeouts to complete, let alone all of them.
Personally, I would do something like this (see comments in code) :
function runSimulation() {
var $agents = $("div.tooltipBox"),
$rFOs = $(".rFO"),
$message = $("#lContent h2");
if($agents.filter('.running').length > 0) {
//Inhibit simulation if any part of an earlier simulation is still running.
return;
}
$message.html("Simulation <b>in progress...</b>");
$agents.each(function(i, agent) {
var ttl, $agent;
if(i >= $rFOs.length) {
return false;//break out of .each()
}
ttl = Number($rFOs.eq(i).val());//Any failure to cast as Number will result in NaN.
if(isNaN(ttl)) {
return true;//continue with .each()
}
$agent = $(agent).addClass('running');//Provide a testable state (see below and above)
$agent.children(".tttFalloutOrder").on('click.sim', function() {//Note: namespaced click event allows .off('click.sim') without affecting any other click handlers that might be attached.
alert("click on " + $agent.attr('id'));
});
setTimeout(function() {
$agent.children(".tttFalloutOrder").off('click.sim');//detach the handler attached with .on('click.sim') .
$agent.removeClass('running').children(".offlineCover").fadeIn(500);
if($agents.filter('.running').length == 0) {//if neither this nor any other agent is "running"
$message.html("Simulation <b>complete</b>");//Signify complete when all parts are complete
}
}, ttl*1000);
});
}
untested
If it the click actions still don't work, then I would suspect the $agent.children(".tttFalloutOrder") selector to be incorrect.
There are other ways of doing this type of thing, notably ways that exploit Deferreds/promises and jQuery.when(), but the above code (suitably debugged) should suffice.
I'm having a problem trying to make this code work. The purpose is simply to enable a textbox when the option "Bus" is selected from a DropList.
At the moment I have a for loop running through and disabling all the necessary boxes (there are 15 identical rows). At the same time it is enabling a different function which is based on changing the selection in the same box, which works. Whereas the function in question doesn't work.
Here is the function:
$(function () {
for(var i=0;i<15;i++){ //loop through each of the rows
$("#Select" + i + "Box_C").change(callbackFactory(i)); //This is a working function
$("#Select" + i + "Box_C").change(toBus(i)); //This one isn't working
$("#Text" + i + "Box_Q1").prop('disabled', true); //this one is working
};
function busEnabler(num){
$("#Text" + num + "Box_Q1").prop('disabled', false);
};
function toBus(numm){
var jk = numm //Closures confuse me
if ($("#Select" + numm + "Box").val() === "Bus"){
$("#Text" + numm + "Box_Q1").prop('disabled', false);
console.log(jk);
busEnabler(jk);
}
else {
$("#Text" + numm + "Box_Q1").prop('disabled', true);
console.log($("#Select" + numm + "Box_C") + "=" + $("#Select" + numm + "Box_C").val());
}
};
});
The ID's are made up (the real ones are horribly named - not my choosing) so if there is a typo in the ID's it's irrelevant.
Also as a side note I can't seem to log anything to the console after the page has loaded (using FireBug).
callbackFactory:
function callbackFactory(i){
console.log('changing');
return function(){
transportChange($("#_Q0_Q"+ i +"_Q3_C").val(), i);
};
The problem is in the way you are attaching the onchange events.
The following statement (that you have used) does not attach any method.
$("#Select" + i + "Box_C").change(toBus(i));
So whenever the value of the select box changes, the 'toBus' method is never called.
I have created a Fiddle. Please refer that.
JSFiddle
Use the following code for your purpose and please show the function callbackFactory also, so that I can resolve your complete problem.
$(doucment).on('change', 'select[id^=Select]',function() {
$('input[id^=Text]').prop({disabled:true});
var id = $(this).attr('id');
var txtId = id.replace('Select','Text').replace('Box','Box_Q1');
if($(this).val() == 'Bus') {
$('#'+txtId).prop({disabled:false});
}
});
I have a modal box in jQuery which I have created to display some embed code. I want the script to take the id of the link that is clicked but I can't seem to get this working.
Does anyone know how I can do that or why this may be happening?
My jQuery code is:
function generateCode() {
var answerid = $('.openembed').attr('id');
if($('#embed input[name="comments"]:checked').length > 0 == true) {
var comments = "&comments=1";
} else {
var comments = "";
}
$("#embedcode").html('<code><iframe src="embed.php?answerid=' + answerid + comments + '" width="550" height="' + $('#embed input[name="size"]').val() + '" frameborder="0"></iframe></code>');
}
$(document).ready(function () {
$('.openembed').click(function () {
generateCode();
var answerid = $('.openembed').attr('id');
$('#box').show();
return false;
});
$('#embed').click(function (e) {
e.stopPropagation()
});
$(document).click(function () {
$('#box').hide()
});
});
My mark-up is:
Embed
Embed
Your problem is here:
$('.openembed')
returns an array of matched elements. Your should instead select only the clicked element.
$('.openembed') works correctly if you assing a click event to all elements that have this class. But on the other hand, you're unable do know which is clicked.
But fortunately in the body of handler function click you could call $(this).
$(this) will return the current (and clicked element).
// var answerid = $('.openembed').attr('id'); // Wrong
var answerid = $(this).attr('id'); // Correct
// Now you can call generateCode
generateCode(answerid);
Another error is the body of generateCode function. Here you should pass the id of selected element. This is the correct implementation.
function generateCode(answerid) {
if($('#embed input[name="comments"]:checked').length > 0 == true) {
var comments = "&comments=1";
} else {
var comments = "";
}
$("#embedcode").html('<iframe src="embed.php?answerid=' + answerid + comments + '" width="550" height="' + $('#embed input[name="size"]').val() + '"frameborder="0"></iframe>');
}
Here I have implemented your code with the correct behavior: http://jsfiddle.net/pSZZF/2/
Instead of referencing the class, which will grab all members of that class, you need to reference $(this) so you can get that unique link when it is clicked.
var answerid = $(this).prop('id');
$('.openembed').click(function () {
generateCode();
var answerid = $(this).attr('id');
$('#box').show();
return false;
});
Use $(this). $('.openembed') refers to multiple links.
var answerid = $('.openembed').attr('id');
needs to be
var answerid = $(this).prop('id');
The other answers are trying to fix the click() function, but your issue is actually with the generateCode function.
You need to pass the clicked element to the generateCode function:
$('.openembed').click(function () {
generateCode(this);
And modify generateCode:
function generateCode(element) {
var answerid = element.id;
Of course var answerid = $('.openembed').attr('id'); within the click code isn't correct either, but it doesn't seem to do anything anyway.
Get the id when the correct anchor is clicked and pass it into your generateCode function
$('.openembed').click(function () {
var answerid = $(this).attr('id');
generateCode(answerid)
$('#box').show();
return false;
});
Change your function
function generateCode(answerid) {
// dont need this line anymore
// var answerid = $('.openembed').attr('id');
I have built a dropdown menu system, everything works when tested independently, the problem I have is in the code below. I use the jQuery ready function to build the menu bar from an external array (menubar[]). Here I am trying to get the mouseover event to call the dropdown() function, but using a different argument for each anchor tag.
So rolling over the first should call dropdown(0), the second dropdown(1) and so on.
$(document).ready(function () {
for (i in menubar) {
var declaration = '<a href="' + baseurl + '/' + menubar[i].url +
'" class="menutitle">' + menubar[i].name + '</a>';
var a = $(declaration).mouseover(function () {
dropdown(i);
}).mouseout(function () {
activeTimer = setTimeout("removedropdowns()", 100);
});
$("#menu").append(a);
}
});
The code is calling dropdown(6); on each rollover. How can I pass the loop variable (i) into the mouseover function as a literal/static value!
I got this working fine in FF by using
.attr('onMouseOver','javascript:dropdown('+i+');')
but that wasn't firing for some versions of IE, so I switched to the jQuery mouseover, which fires, but I have the issue above :(
Your actual problem is that each of your mouseover callbacks uses the same i you increase i all the way up to 6, the callbacks still point to the same i and therefore all use 6 as the value.
You need to make a copy of the value of i, you can do this by using an anonymous function.
$(document).ready(function () {
// you should use (for(var i = 0, l = menubar.length; i < l; i++) here in case menubar is an array
for (var i in menubar) {
var declaration = '<a href="' + baseurl + '/' + menubar[i].url +
'" class="menutitle">' + menubar[i].name + '</a>';
(function(e) { // e is a new local variable for each callback
var a = $(declaration).mouseover(function () {
dropdown(e);
}).mouseout(function () {
activeTimer = setTimeout(removedropdowns, 100); // don't use strings for setTimeout, since that calls eval
});
$("#menu").append(a);
})(i); // pass in the value of i
}
});
$(function() {
$(menubar).each(function(i){
$("#menu").append('' + menubar[i].name + '');
});
$("#menu a").hover(
function(){
dropdown($(this).index());
},
function(){
activeTimer = setTimeout("removedropdowns()", 100);
}
);
});
First, don't use for..in but rather ordinary loop.
Second, I would just append the links first then apply the events later:
$(document).ready(function() {
for (var i = 0; i < menubar.length; i++) {
$("#menu").append('' + menubar[i].name + '');
}
$("#menu a").each(function(index) {
$(this).mouseover(function() { dropdown(index); }).mouseout(function() { activeTimer = setTimeout("removedropdowns()", 100); });
});
});
Have a look here and here.
To capture the current value of i, you need to pass it as a parameter to another function where it can be captured as a local variable:
Try using jQuery's each() function:
jQuery(function() {
jQuery.each(menubar, function(index, element) {
var declaration = '' + element.name + '';
var a = $(declaration).mouseover(function() { dropdown(index); }).mouseout(function() { activeTimer = setTimeout("removedropdowns()", 100); });
$("#menu").append(a);
});
});
In JavaScript, if you don't declare your variable, it is defined globally. To fix this, add "var" in front of your i looping variable like this. UPDATE: As Sime noticed (see comment), you also need to pass the variable into the function, otherwise you form a closure on the i.
$(document).ready(function() {
for(var i in menubar) {
var declaration = '' + menubar[i].name + '';
var a = $(declaration).mouseover(function(i) { dropdown(i); }).mouseout(function() { activeTimer = setTimeout("removedropdowns()", 100); });
$("#menu").append(a);
}
});