I'm trying to use the following code in Javascript. I'd like to pass a function rulefunc() a number of times into the onChange() function iteratively. I want to be able to access i from within the function when it is called. How can I do this?
var gui = new DAT.GUI();
for for (var i=0; i<5; i++) {
// want to associate ruleFunc with i
gui.add(lsys, 'grammarString').onChange(ruleFunc);
}
function ruleFunc(newVal) {
...
// access i here
}
At the event side:
Here since for loop is synchronous an IIFE is used so that right value of i is passed
IIFE and the onchange event makes a closure which makes the right value of i to be passed
at the argument
At the event callback side
Closure is used so that the function that is returned can access the value of the argument
var gui = new DAT.GUI();
for (var i=0; i<5; i++) {
// want to associate ruleFunc with i
(function(a){ //making an IIFE to make sure right value of i is passed to the function
f1.add(lsys, 'grammarString').onChange(ruleFunc(a));
})(i);
}
function ruleFunc(newVal) {
return function(){
//making a closure which will have access to the argument passed to the outer function
console.log(newVal);
}
}
You can write a function that returns a function like
function ruleFunc(i) {
return function(newVal){
// ... here use your newVal and i
}
}
And use like
f1.add(lsys, 'grammarString').onChange(ruleFunc(i));
You call the function that gets the i from the outer scope and then the returned function gets the newVal of the 'onChange' event. Note that I am calling the function ruleFunc and pass a parameter i. Inside the function you can now use your i variable and newVal.
Example of how it works. Here I add functions to the array with approprite i values. After that when I execute each function, it properly knows what i was used when it have been created. It is called closure.
var functions = [];
function ruleFunc(newVal) {
return function (){
console.log(newVal);
};
}
for(var i = 0; i < 5; i++) {
functions.push(ruleFunc(i));
}
for(var i = 0; i < functions.length; i++) {
functions[i]();
}
Related
for (var i = 0; i < json.length; i++) {
$.Mustache.load('/mustaches.php', function(i) {
//Do Something
});
}
How do I pass the var i to the function in this case?
EDIT: Sorry I don't actually want to make the Mustache.load call too many times. Only once. How can I do that?
This is a little more complicated than you might think, as you must ensure you pass the right value of i, so that the callback doesn't use the value of end of loop.
for (var i = 0; i < json.length; i++) {
(function(i){
$.Mustache.load('/mustaches.php', function() {
// use i. Call a function if necessary
//Do Something
});
})(i);
}
About the callback term : it refers to a function you pass as argument so that the function you call can call it back.
To understand the code I wrote, you must
understand that the callback is called later, when the loop has finished and so when i in the loop has the value of end of loop
that the scope of a non global variable is the function call in which it is defined. That's why there's this intermediate function : to define another variable i which is called with the value of the loop
An elegant way to solve your question would be using the bind method.
for (var i = 0; i < json.length; i++) {
$.Mustache.load('/mustaches.php', function(i) {
//Do Something
}.bind(this, i));
}
the bind method returns a new function with a new context (in this case this) and applies one (or more) argument(s) to your function (i in this particular case). You can find more about bind and currying here.
EDIT. You can optimise your loop by loading the template only once. In fact, $.Mustache.load fetches /mustache.php on each cycle of the loop. Also, because the function asynchronously fetches the template with AJAX, you might get not consistent ordering in your template (one response may take longer than others). The fix is pretty straightforward: we load the template and then we iterate through the loop.
$.get('/mustache.php').done(function(template){
$.Mustache.add('my-template', template);
for (var i = 0, len = json.length; i < len; ++i) {
var rendered_template = $.Mustache.render('my-template', {
i: i,
...
});
}
});
I am using a getJSON method to post the data I have in a database, through a for loop and into an HTML page. But I would like to the function to call different tables in my database depending on the integer the for loop is currently on, something like this:
for (var r = 0; r < 8; r++){
$.getJSON("PHP-PAGE.php?jsoncallback=?", function(table+r) {
//function stuff here
});
}
But when I try to do this, the "table+r" is flagging a syntax error. What am I doing wrong?
You are defining a function, not calling it. Between ( and ) you have to put identifiers (variable names) not expressions.
To pass data here, you need to use variables from a wider scope than the function. Since the variable is going to change (and the function is called asynchronously) you have to use a closure to do this.
function mkCallback(table) {
var foo = "table" + table;
return function () {
// function stuff that uses foo here
// foo from the time mkCallback was called to make this function
// will still be in scope
};
}
for (var r = 0; r < 8; r++){
$.getJSON("PHP-PAGE.php?jsoncallback=?", mkCallback(table+r));
}
function(table+r) { tries to create a function with table+r as a parameter, but + is not valid in a variable name. I think you instead want something like this:
for (var r = 0; r < 8; r++){
$.getJSON("PHP-PAGE.php?jsoncallback=?",
(function(currentR){
return function() {
var someVariable=table+currentR; // No idea where table came from...
//function stuff here
}
})(r));
}
As #Quentin mentioned by the time the callback is called, r will have reached its final value, hence the interesting closure.
I think what you probably want is
for (var r = 0; r < 8; r++){ //outer loop
function(tablenum){ //closure function
tablename = table+tablenum // saved reference to "table+r"
$.getJSON("PHP-PAGE.php?jsoncallback=?", function() {
//function stuff here, using tablename as the param
});
}(r)
}
This creates a closure to maintain the value of the iterated value. You can reference tablename in the callback function, and that will refer to a value equivalent to table+r
The issues with your original example
You were putting table+r as a parameter to a function you were defining, rather than an argument to one you were calling
You were trying to get the callback to reference r. But the callback won't run until after the loop has executed, so r will be 8 for all callback functions.
If you were trying to reference "table1", "table2" then you want to have "table"+r. Otherwise I assume you're referencing a table variable outside the scope of the code you showed us.
You can directly reference the variable r in your callback. Not sure what table is - the return data from the JSON call? Try the following:
for (var r = 0; r < 8; r++){
$.getJSON("PHP-PAGE.php?jsoncallback=?", function(jsonReturnData) {
//function stuff here
alert(r);
});
}
I have the following code that adds an onmouseover event to a bullet onload
for (var i = 0; i <= 3; i++) {
document.getElementById('menu').getElementsByTagName('li')[i].onmouseover = function () { addBarOnHover(i); };
}
This is the function that it is calling. It is supposed to add a css class to the menu item as the mouse goes over it.
function addBarOnHover(node) {
document.getElementById('menu').getElementsByTagName('li')[node].className = "current_page_item"; }
When the function is called, I keep getting the error:
"document.getElementById("menu").getElementsByTagName("li")[node] is
undefined"
The thing that is stumping me is I added an alert(node) statement to the addBarOnHover function to see what the value of the parameter was. The alert said the value of the parameter being passed was 4. I'm not sure how this could happen with the loop I have set up.
Any help would be much appreciated.
This is a common problem when you close over an iteration variable. Wrap the for body in an extra method to capture the value of the iteration variable:
for (var i = 0; i <= 3; i++) {
(function(i){ //here
document.getElementById('menu').getElementsByTagName('li')[i].onmouseover = function () { addBarOnHover(i); };
})(i); //here
}
an anonymous function is created each time the loop is entered, and it is passed the current value of the iteration variable. i inside the anonymous function refers to the argument of this function, rather than the i in the outer scope.
You could also rename the inner variable for clarity:
for(var i=0; i<=3; i++){
(function(ii){
//use ii as i
})(i)
}
Without capturing the iteration variable, the value of i when it is finally used in the anonymous handler has been already changed to 4. There's only one i in the outer scope, shared between all instances of the handler. If you capture the value by an anonymous function, then the argument to that function is used instead.
i is being passed as a reference (not by value), so once the onmouseover callback is called, the value of i has already become 4.
You'll have to create your callback function using another function:
var menu = document.getElementById('menu');
var items = menu.getElementsByTagName('li');
for (var i = 0; i <= 3; i++) {
items[i].onmouseover = (function(i) {
return function() {
addBarOnHover(i);
};
})(i);
}
You could make it a little more readable by making a helper function:
var createCallback = function(i) {
return function() {
addBarOnHover(i);
};
};
for (var i = 0; i <= 3; i++) {
items[i].onmouseover = createCallback(i);
}
I am looking at a javascript code that manipulates an HTML A tag , and I'm having trouble understanding how it sets up the "onclick" property. It seems to be telling it to update ytplayer_playitem with the index variable j and then call ytplayer_playlazy(1000)
But what's up with all the parentheses? What details in the javascript syntax allows it to be setup like this?
var a = document.createElement("a");
a.href = "#ytplayer";
a.onclick = (function (j) {
return function () {
ytplayer_playitem = j;
ytplayer_playlazy(1000);
};
})(i);
Well, basically, the value of onclick is a function that will get called when the element is clicked. Whatever you want to happen when the user clicks the element goes in the body of the function.
You could create a named function and then assign it to the element's onclick attribute:
function youClickedMe() {
...
}
a.onclick = youClickedMe
but that clutters up the namespace with a function name that is never referenced anywhere else. It's cleaner to create an anonymous function right where you need it. Normally, that would look like this:
a.onclick = function() { ... }
But if we try that with your specific example:
a.onclick = function() {
ytplayer_playitem = something; // ??
ytplayer_playlazy(1000);
}
We see that it hard-codes the something that gets played. I'm assuming the original code was taken from a loop which generates several clickable links to play; with the code just above, all of those links would play the same thing, which is probably not what you want.
So far, so straightforward, but this next leap is where it gets tricky. The solution seems obvious: if you're in a loop, why not just use the loop variable inside the function body?
// THIS DOESN'T WORK
a.onclick = function() {
ytplayer_playitem = i;
ytplayer_playlazy(1000);
}
That looks like it should work, but unfortunately the i inside the function refers to the value of the variable i when the function is called, not when it's created. By the time the user clicks on the link, the loop that created all the links will be done and i will have its final value - probably either the last item in the list or one greater than that item's index, depending on how the loop is written. Whatever its value is, you once again have the situation where all links play the same item.
The solution in your code gets a little meta, by using a function whose return value is another function. If you pass the loop control variable to the generating function as an argument, the new function it creates can reference that parameter and always get the value that was originally passed in, no matter what has happened to the value of the outer argument variable since:
function generate_onclick(j) {
// no matter when the returned function is called, its "j" will be
// the value passed into this call of "generate_onclick"
return function() { ytplayer_playitem = j; ytplayer_playlazy(1000); }
}
To use that, call it inside the loop like this:
a.onclick = generate_onclick(i);
Each generated function gets its very own j variable, which keeps its value forever instead of changing when i does. So each link plays the right thing; mission accomplished!
That's exactly what your posted original code is doing, with one small difference: just like the first step in my explanation, the author chose to use an anonymous function instead of defining a named one. The other difference here is that they are also calling that anonymous function immediately after defining it. This code:
a.onclick = (function (j) { ... })(i)
is the anonymous version of this code:
function gen(j) { ... }
a.onclick = gen(i)
The extra parens around the anonymous version are needed because of JavaScript's semicolon-insertion rules; function (y) {...}(blah) compiles as a standalone function definition followed by a standalone expression in parentheses, rather than a function call.
"But what's up with all the parentheses? "
Most of the parentheses are just doing what you'd expect.
There's an extra set that isn't technically needed, but is often used as a hint that the function is being invoked.
// v-v---these are part of the function definition like normal
a.onclick = (function (j) {
// ^-----------this and...v
return function () {
ytplayer_playitem = j;
ytplayer_playlazy(1000);
};
// v---...this are technically not needed here, but are used as a hint
})(i);
// ^-^---these invoked the function like normal
"What details in the javascript syntax allows it to be setup like this?"
The upshot is that the function is invoked immediately, and passed i so that its value is referenced by the j parameter in the immediately invoked function.
This creates a variable scope that the returned function will continue to have access to. This way it always has access to the j variable, and not the i that gets overwritten in the loop.
These inlined functions are abused a bit IMO. It becomes clearer if you simply make it a named function.
for(var i = 0; i < 10; i++) {
// create the new element
a.onclick = createHandler(i);
// append it somewhere
}
function createHandler (j) {
return function () {
ytplayer_playitem = j;
ytplayer_playlazy(1000);
};
}
The resulting handler is exactly the same, but the code is much less cryptic.
Right, I'm going to guess that the surrounding code looks like this:
for (var i = 0; i < playitems.length; i++) {
// above code here
}
Now, you could do the obvious thing here, and assign the onclick property like this:
a.onclick = function() {
ytplayer_playitem = i;
ytplayer_playlazy(1000);
};
However that wouldn't work very well, because the value of i changes. Whichever link was clicked, the last one would be the one activated, because the value of i at that point would be the last one in the list.
So you need to prevent this happening. You need to do this by creating a new scope, which is done by creating an extra function, which is immediately invoked:
(function (j) {
// some code here
})(i);
Because i has been passed into the function, the value is passed rather than a reference to the variable being kept. This means that you can now define a function which will have a reference to the correct value. So you get your extra function to return the click handling function:
a.onclick = (function (j) { // j is the right number and always will be
return function () { // this function is the click handler
ytplayer_playitem = j;
ytplayer_playlazy(1000);
};
})(i);
So each a element has its own click handler function, each of which has its own individual j variable, which is the correct number. So the links, when clicked, will perform the function you want them to.
a.onclick = (function (j) {
return function () {
ytplayer_playitem = j;
ytplayer_playlazy(1000);
};
})(i);
This creates a "closure" to ensure that the value of i that is bound to the handler is the value of i "at that time" and not i in the general.
In your code, the function inside the () is an expression, executed and passed the variable i. This is the (i) you see in the end part. In this executed function expression, the i becomes the local variable j. This executed function expression returns the handler function that is to be bound the onclick event carrying the value of j which was i "at that time"
if i did not use the closure:
//suppose i is 1
var i = 1;
a.onclick = function () {
ytplayer_playitem = i;
ytplayer_playlazy(1000);
};
//and changed i
i = 2;
//if you clicked the <a>, it would not be 1 onclick but 2 because you
//did not assign the value of i "at that time". i is "tangible" this way
a.onclick = (function (j) {
return function () {
ytplayer_playitem = j;
ytplayer_playlazy(1000);
};
})(i);
What you have here is a self-invoking anonymous function. Let's break it down, first replacing the body of the function with something simpler (return j + 1;):
function( j ) { return j + 1; }
This s a run-of-the-mill anonymous function or closure. This line of code is an expression, and so it has a value, and that value is a function. Now we could do this:
var foo = function( j ) { return j + 1; }
foo( 5 ); // => 6
You recognize this, I'm sure—we're assigning the anonymous function to the variable foo, and then calling the function by name with the argument i. But, instead of creating a new variable, because the closure is an expression we can call it like this instead:
( function( j ) { return j + 1; } )( 5 ); // => 6
Same result. Now, it's just returning j + 1 but in your code it returns something else: Another anonymous function:
return function() { /* ... */ }
What happens when we have a self-invoking anonymous function that returns a function? The result is the "inner" function that was returned:
a.onclick = ( function( j ) {
return function() {
ytplayer_playitem = j;
ytplayer_playlazy( 1000 );
}
}
)( i );
If i was equal to 9 then a.onclick would now hold a function equivalent to this:
function() {
ytplayer_playitem = 9;
ytplayer_playlazy( 1000 );
}
As others have pointed out, the usefulness of this is that when ( function( j ) { /* ... */ } )( i ) is invoked you are capturing the value of i at that time and putting it into j rather than creating a reference to the value i holds, which may (and probably will) change later on.
I know that this code doesn't work and I also know why.
However, I do not know how to fix it:
JavaScript:
var $ = function(id) { return document.getElementById(id); };
document.addEventListener('DOMContentLoaded', function()
{
for(var i = 1; i <= 3; i++)
{
$('a' + i).addEventListener('click', function()
{
console.log(i);
});
}
});
HTML:
1
2
3
I want it to print the number of the link you clicked, not just "4".
I will prefer to avoid using the attributes of the node (id or content), but rather fix the loop.
Wrap the loop block in its own anonymous function:
document.addEventListener('DOMContentLoaded', function()
{
for(var i = 1; i <= 3; i++)
{
(function(i) {
$('a' + i).addEventListener('click', function() {
console.log(i);
})
})(i);
}
}
This creates a new instance of i that's local to the inner function on each invocation/iteration. Without this local copy, each function passed to addEventListener (on each iteration) closes over a reference to the same variable, whose value is equal to 4 by the time any of those callbacks execute.
The problem is that the inner function is creating a closure over i. This means, essentially, that the function isn't just remembering the value of i when you set the handler, but rather the variable i itself; it's keeping a live reference to i.
You have to break the closure by passing i to a function, since that will cause a copy of i to be made.
A common way to do this is with an anonymous function that gets immediately executed.
for(var i = 1; i <= 3; i++)
{
$('a' + i).addEventListener('click', (function(localI)
{
return function() { console.log(localI); };
})(i);
}
Since you're already using jQuery, I'll mention that jQuery provides a data function that can be used to simplify code like this:
for(var i = 1; i <= 3; i++)
{
$('a' + i).data("i", i).click(function()
{
console.log($(this).data("i"));
});
}
Here, instead of breaking the closure by passing i to an anonymous function, you're breaking it by passing i into jQuery's data function.
The closure captures a reference to the variable, not a copy, which is why they all result in the last value of the 'i'.
If you want to capture a copy then you will need to wrap it in yet another function.