umps umps - 1 month ago 17
Javascript Question

Javascript,Setting up onclick method syntax

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);

Answer

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()

The extra parens in 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.

Comments