The answer to why this code is not working is because: The problem with this method is is you're not providing a function as the second argument. You're calling a function - that function is making things blue. It's not returning a function that is making things blue. So you're making things blue immediately and, when the click happens, there's nothing to call, because the function you called returned nothing.
But I'm lost after the first sentence. could someone dumb this down? perhaps a visual explanation of the code would help!
Code: https://jsfiddle.net/2yfj89af/1/
var submitSOBox = document.getElementsByClassName("submitSOBox");
for (i = 0; i < submitSOBox.length; i++) {
submitSOBox[i].addEventListener('click', SOBoxColor(i));
}
function SOBoxColor(i) {
submitSOBox[i].style.backgroundColor = "blue";
}
The reason explained: you have this line:
submitSOBox[i].addEventListener('click', SOBoxColor(i));
This calls addEventListener which expects a function as the last argument. You provide SOBoxColor(i), which is not a function, but a value returned by a function -- you call it. To pass a function, you would need to pass something like SOBoxColor, so without calling it. This is needed because your browser needs to know what to call when the click event happens.
Now, passing just SOBoxColor will not do what you want. Yes, it will make that SOBoxColor gets called when a click happens, but you will not have the i value passed to it.
To achieve that, you need a modified version of your function that somehow has the i value pre-filled as argument. Luckily there is a way to create such a modified function with the bind method, which returns the same function, but with some things bound to it:
submitSOBox[i].addEventListener('click', SOBoxColor.bind(submitSOBox[i], i));
The first argument of bind determines what the special this object will represent when SOBoxColor is called, and the second argument is what i will be.
Now, this will work.
But you can do it even nicer if you would use the this keyword in your SOBoxColor function, because then you don't even need the i parameter:
function SOBoxColor() {
this.style.backgroundColor = "blue";
}
... and your binding can also do without the i argument:
submitSOBox[i].addEventListener('click', SOBoxColor.bind(submitSOBox[i]));
It can still be done more concisely, because when the click happens, the browser will already provide a nice service to us: it sets the this keyword to the element to which you added the event handler, so in the end it is not even necessary to bind submitSOBox[i] to it explicitly. This will work also:
submitSOBox[i].addEventListener('click', SOBoxColor);
... provided you changed the SOBoxColor function to work with this.
When you add the event listener like you are:
submitSOBox[i].addEventListener('click', SOBoxColor(i));
It is calling the function SOBoxColor(), expecting it to return a function that will be called when the event listener is triggered.
By return a function, I mean like this:
function SOBoxColor(){
return function(){
alert("hello")
//Do stuff
}
}
If you replaced the current function with the one above, it would alert "hello" whenever the click listener is triggered.
How would you fix this?
Simply put the functions name in it, without calling it:
submitSOBox[i].addEventListener('click', SOBoxColor);
The problem you are going to have now is, with your current function, SOBoxColor:
function SOBoxColor(i) {
submitSOBox[i].style.backgroundColor = "blue";
}
You are sending the number as a parameter to select the appropriate element, which you can't do anymore.
However, this isn't a problem, since you were selecting the element incorrectly in the first place.
Instead of selecting it with it's index in the HTMLCollection submitSOBox, just reference the current element with the this keyword:
function SOBoxColor() {
this.style.backgroundColor = "blue";
}
So, your complete code should now look like:
var submitSOBox = document.getElementsByClassName("submitSOBox");
for (var i = 0; i < submitSOBox.length; i++) {
submitSOBox[i].addEventListener('click', SOBoxColor);
}
function SOBoxColor() {
this.style.backgroundColor = "blue";
}
You're assuming calling a function must be done by
functionName();
But it's not the only way of calling functions. You could input a function as an argument, use the call method: functionName.call(null,args), etc.
What functionName(); does is make Javascript go, "Hey, I need to find out what that function returns." So if functionName is like
function functionName(){
console.log("hello!");
return undefined;
};
Javascript will try to simplify everything. It will run through your function, console log "hello!" while it's busy working, and in the very end figure out what your function returns:
undefined;
Think of it like simplifying math expressions back in algebra class or something.
In your case, you're telling the click event to call whatever is returned by SOBoxColor(i). So if SOBoxColor looked like this:
function SOBoxColor(i) {
submitSOBox[i].style.backgroundColor = "blue";
return 9000;
}
Your click event would look like this:
for (i = 0; i < submitSOBox.length; i++) {
submitSOBox[i].addEventListener('click', 9000);
}
addEventListener is a function that wants a function as the 2nd argument, not a number. so nothing would happen.
As you are said the function returns nothing. in fact it is not a function at all. The idea of closure is to return function which uses values in outer scope.
var submitSOBox = document.getElementsByClassName("submitSOBox");
for (i = 0; i < submitSOBox.length; i++) {
//changes are possible here but we'll do it later
submitSOBox[i].addEventListener('click', SOBoxColor(i));
//in your initial code SOBoxColor(i) returned undefined (no return)
//and event listener must be a function. Yours was not.
}
//change your function like this:
function SOBoxColor(i) {
//get the element from collection.
//both collection and i are available at this point
var elem = submitSOBox[i];
return function(){
//function knows elem from parent scope
elem.style.backgroundColor = "blue";
};
}
I hope this explanations are helpful.
Extra
This is an exercise with closure. If it were not then it would be simpler solution:
var submitSOBox = document.getElementsByClassName("submitSOBox");
for (i = 0; i < submitSOBox.length; i++) {
submitSOBox[i].addEventListener('click', function(){
this.style.backgroundColor = "blue";
//in this context **this** is clicked element
});
}
Basically
submitSOBox[i].addEventListener('click', SOBoxColor(i));
He is excecuting the function SOBoxColor with the current value of i as argument.
Also when when you add an event listener to an element, you have already the implicit value of this pointing at the DOM element. So just the change the code:
function SOBoxColor() {
this.style.backgroundColor = "blue";
}
var submitSOBox = document.getElementsByClassName("submitSOBox");
for (i = 0; i < submitSOBox.length; i++) {
submitSOBox[i].addEventListener('click', SOBoxColor);//your passing the function
}
Also it's all more simple very simple with jQuery:
$(".submitSOBox").click(function(){ $(this).css("background-color","blue"); });
Related
I am more of a C++/Java programmer and fairly new to Javascript. I have been reading Javascript the good parts book.In the chapter on Functions, the author cites and example of attaching an event listener to nodes in the DOM by doing the following
// When you click on a node, an alert box will display the ordinal of the node.
1 var add_the_handlers = function (nodes) {
2 var i;
3 for (i = 0; i < nodes.length; i += 1) {
4 nodes[i].onclick = function (i) {
5 return function (e) {
6 alert(e);
7 };
8 }(i);
9
10 };
The aim of the above function is to add an onclick event handler to all of the nodes in the DOM such that when anyone of the nodes is clicked it alerts the number assigned to it or its ordinal.
I am not able to understand lines 5 through 8. I understand that the function (the onclick = function(i)...) is immediately called with i as its argument. But how is this 'i' being passed to the function that is being returned ? How is 'e' getting the value of 'i' that is later being alerted?
I tried playing around with it and ran the following code in my browser
window.onclick = function(i){
return function(e){
console.log("Inner function called");
alert(e);
}
}(4);
I expected an alert window with the number 4 in it. Instead the alert window shows [object MouseEvent].
I would really appreciate if someone explained this to me.
Thanks
It's a mistake in the book. They were trying to demonstrate the awful (performance-wise) pattern of using an otherwise unnecessary function scope to allow an inline function to closure around a variable in an outer scope, which, because it is not referenced anywhere else, becomes a kind of private variable referenced only by the inline function. The code would do its job if the i variable was indeed used as the argument to the alert() call:
var add_the_handlers = function(nodes) {
for (var i = 0; i < nodes.length; ++i) {
nodes[i].onclick = function(i) {
return function() {
alert(i);
};
}(i);
} // end for
};
add_the_handlers(document.getElementsByTagName('div'));
http://jsfiddle.net/pzht9mLu/
In the book's code, instead of closuring around the function parameter i in the outer scope, the inline function that becomes the onclick handler doesn't closure around anything. Instead, the e variable that is used as the argument in the alert() call binds to the function parameter e of the inline function (that's why no closuring happens; a closure only happens when a variable reference inside a function scope fails to bind to any local inside the scope, and instead binds to a variable in an outer scope). Because the function ends up serving as an event handler, its parameter e is set equal to the Event object that initiated the callback (if and when it happens), and that's why you see that in the alert message.
What you should realize is that in lines 5-9 (where's number 7 btw?:)) a new function is created, which will be bound as the event listener function of the onclick.
See line 9:
}(i);
This means that the above function is immediately invoked, passing in i as its function parameter, and then it returns a new function.
Should you, for example, use the value of i in this inner function, it is captured in a closure, so the onclick function will have access to it, even though it is invoked from an entirely different scope.
I tried playing around with it and ran the following code in my
browser
window.onclick = function(i){
return function(e){
console.log("Inner function called");
alert(e);
} }(4);
I expected an alert window with the number 4 in it. Instead the alert window shows [object MouseEvent].
The argument i is never used. The alert produces e, which is the argument to the onclick event handler. Which is a MouseEvent object.
var add_the_handlers = function (nodes) {
var i;
for (i = 0; i < nodes.length; i += 1) {
nodes[i].onclick = function (i) {
return function (e) {
alert(e);
};
}(i);
};
The aim of the above function is to add an onclick event handler to
all of the nodes in the DOM such that when anyone of the nodes is
clicked it alerts the number assigned to it or its ordinal.
I am not able to understand lines 5 through 8. I understand that the
function (the onclick = function(i)...) is immediately called with i
as its argument. But how is this 'i' being passed to the function that
is being returned ? How is 'e' getting the value of 'i' that is later
being alerted?
i Isn't passed to the second function, and will never be shown by your current code. What you're doing now is effectively no different than this:
nodes[i].onclick = function (e) { alert(e); };
If you want to produce an alert with the correct value of i in it, you'd simply use your original code but alert i instead of e.
This question already has answers here:
do something when function executes Jquery
(2 answers)
Closed 9 years ago.
Is it possible to execute something every time a specific function runs without any knowledge about that function besides its name?
This would be similar to bind
var clicked = 0;
$('#foo').bind('click',function(){
clicked += 1;
alert(clicked);
});
So there, every time something with the ID foo is clicked, it will add 1 to the variable clicked so that I know how many times it has been clicked. What I want to do would be the equivalent of the following if it were correct syntax:
var fired = 0;
$('my_function').bind('run',function(){
fired += 1;
alert(fired);
});
I don't care if in any given situation you would be in, you would always be able to figure something out about the function and use that, I don't want work arounds, this is what I want for an answer:
How I can execute something everytime a specific function runs, just given the name of the function. If that is not possible, why not?
Try something like this:
var temp = my_function, fired = 0;
my_function = function() {
fired++;
temp.apply(this,arguments);
}
I think something like this may be the closest you can come:
function adjustFunctionToCount(f){
var count = 0;
function newF(){
count++;
f.apply(this, arguments);
}
newF.getCount = function(){ return count; };
return newF;
}
And so if you have
function handler(val){
console.log('called with val ' + val);
}
You could do
handler = adjustFunctionToCount(handler);
handler('a');
handler('b');
console.log(handler.getCount());
FIDDLE
And needless to say you could create your function inline
var handler = adjustFunctionToCount(function(val){ console.log('called with val ' + val); });
handler('a');
handler('b');
console.log(handler.getCount());
UPDATED FIDDLE
I'm pretty sure that's impossible in the general case.
Remember, functions are objects, really, and the name of a function is just a variable. Functions can exist without being assigned to a named variable, the variables can be out of your scope, or reassigned/swapped around. In any case, I know of no API that lets you hook onto a JS function call.
This may be of interest: Can I intercept a function called directly?
This is where event driven programming comes in - and jQuery makes it really easy to do.
var myFunction = function() {
//...
//...
//...
$(document).trigger('someEvent');
}
$(document).on('someEvent',function() {
//the function you would like to run every time myFunction is called
});
Try this:
var count = (function(){
var c = 0;
return function(){
alert(c++);
};
})();
$('#foo').click(count);
OR
$('#foo').bind('click', count);
When an Anonymous Function or a Variable that represents a Function is passed it is the same thing. You could make your own code that executes a Function like this:
function executeFun(func){
return func();
}
executeFun(count)
executeFun(function(){
/*everything happens in here. The Anonymous Function will be called
automatically because of the parameter next to the variable func above */
})
Although, that example is impractical it shows you what happens internally. Also, I solved your potential global scope variable problem with a Closure. For more on Closures visit https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Closures .
The code in question:
function addLinks () {
for (var i=0, link; i<5; i++) {
link = document.createElement("a");
link.innerHTML = "Link " + i;
link.onclick = function (num) {
return function () {
alert(num);
};
}(i);
document.body.appendChild(link);
}
}
window.onload = addLinks;
My problem is since the returned function is a closure and variable num is the global variable each time the function executes num should hold the current value replacing the old one and should automatically update with that value everywhere. But this isn't happening -- when I click each link I get a different value. Why is this happening?
num is closed over by the anonymous function. The function (num) { return function () {}}(i) passes i as it is to the inner function and returns a new function based on whatever i's value is at the time.
If you want the click callback to always alert the maximum value of i, it's actually even easier:
link.onclick = function () {
alert(i);
}
function (var varname) is simply invalid syntax.
Think about it: what if you had three links like so:
0
1
2
You want them to alert out their number when you click on them, so you do it like this:
var links = $('a');
for (var i = 0; i < links.length; i++) {
links[0].onclick = function () {
alert(i);
}
}
At first glance, you'd expect that, for example, since you assigned the click handler on the first link when i = 0, it'll alert 0 when you click it. However when you click it, it'll actually alert 3.
You said it yourself, your code is creating a closure. What the code above does is that it's assigning a function handler to the click event of each link. Each of those function handlers is maintaining a reference to the variable i (note: not it's current value!).
At the point when you assign the function handler, it actually doesn't evaluate what value i has (because it doesn't need it). When you click, aha, that's when it checks what value i has and alerts it.
By the time you click a link, your for loop will be long finished, with i = 3, and that's what your click handler alerts.
Please look at the (i) after your function. This type of notation is for self-invocating functions only. It's as if you are setting link.onclick = a number, whereas it expects a function. You can simply use the following.
link.onclick = function (event) {
event.preventDefault();
alert(i);
};
Please note that click functions receive the "event" as a parameter by default. Make sure you call the preventDefault() method of the event otherwise it will bubble up the DOM and trigger a postback due to the nature of the anchor element.
This is strange and disagrees with what I thought to knew about closures. With the only change (in the first version of addLinks in the question):
link.onclick = function (num)
to
link.onclick = function ()
You get the expected result, that is, the actual value of the global num variable is alerted any time when a link is clicked.
It probably has to do with how the interpreter saves scope variables that are referenced within a closure when it encounters that closure. When a variable is referneced in a closure, the closest occurrence of that variable is searched for going upwards from the current scope.
While in the first case it is defined as a parameter (to the function that is called after declaration multiple times) it has different value each time so a different scope value is "remembered" by each closure.
In the 2nd case, the only occurrence found is in the global scope which results in the actual value of num is being used regardless which handler is called.
You haven't explained where the variable num has come from or how it's used. I'm guessing you mean to alert the current value of i. The click handler takes an event object as a parameter so I would try it like this:
function addLinks () {
for (var i=0; i<5; i++) {
var link = document.createElement("a");
link.innerHTML = "Link " + i;
link.onclick = function (event) {
alert(i);
};
document.body.appendChild(link);
}
}
window.onload = addLinks;
This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
What do parentheses surrounding a JavaScript object/function/class declaration mean?
What does this “(function(){});”, a function inside brackets, mean in javascript?
A Javascript function
I encountered markup similar to this:
var something = (function(){
//do stuff
return stuff;
})()
document.ondblclick = function(e) { alert(something(e)) };
I don't understand the opening ( and closing )() in the something variable.
Could you explain the difference to writing it like this?
var something = function(){
//do stuff
return stuff;
};
Thanks!
It's probably easier to understand if you leave the redundant parens out because they serve no purpose:
var something = function() {
return 3;
} // <-- a function.
(); // now invoke it and the result is 3 (because the return value is 3) assigned to variable called something
console.log(something) //3 because the function returned 3
var something = function() {
return 3;
}; // a function is assigned to a variable called something
console.log(something) //logs the function body because it was assigned to a function
console.log(something()) //invoke the function assigned to something, resulting in 3 being logged to the console because that's what the function returns
(function(){ ... }) is a (anonymous) function expression, you could e.g. assign that value to a variable.
The brackets behind it will immidiately execute the function expression, resulting in the return value of the function (in here: stuff). The construct is called IIFE.
When stuff is a function (which I assume, because you invoke something lateron), this is called a closure - the returned function (stuff, assigned to something) still has access to the variables in the execution context of that anonymous function.
On the question what it does, read all the comments and other answers. They are absolutely right.
Why would you want to use it? You find this pattern very often when using closures. The intent of the following code snippet is to add an event handler to 10 different DOM elements and each one should alert it’s ID attribute (e.g. “You’ve clicked 3″). You should know that if this was your actual intent, then there is a much easier way to do this, but for academic reasons let’s stick with this implementation.
var unorderedList = $( "ul" );
for (var i = 0; i < 10; i++) {
$("<li />", {
id: i,
text: "Link " + i,
click: function() {
console.log("You've clicked " + i);
}
}).appendTo( unorderedList );
}
The output of the above code may not be what you first expect. The result of every click handler will be “You’ve clicked 9″ because the value of i at the point the event handler was fired is “9″. What the developer really wanted is for the value of i to be displayed at the point in time the event handler was defined.
In order to fix the above bug we can introduce a closure.
var unorderedList = $( "ul" ), i;
for (i = 0; i < 10; i++) {
$("<li />", {
id: i,
text: "Link " + i,
click: function(index) {
return function() {
console.log("You've clicked " + index);
}
}(i)
}).appendTo( unorderedList );
}
You can execute and modify the above code from jsFiddle.
One way to fix the above code is to utilize a self-executing anonymous function. That is a fancy term that means we are going to create a nameless function and then immediately call it. The value of this technique is that the scope of the variable stays within the function. So, first we will surround the event handler content in a function and then immediately call the function and pass in the value of i. By doing that, when the event handler is triggered it will contain the value of i that existed when the event handler was defined.
Further reading on closures: Use Cases for JavaScript Closures
All of the answers were good, but I think the simplest answer has been skimmed over:
var something = (function(){
//do stuff
return stuff;
})()
After this code executes, something becomes stuff. The function that returned stuff is executed before something is assigned.
var something = function(){
//do stuff
return stuff;
};
After this code executes, something is a function which returns stuff. The function that returns stuff was never executed.
Check the JavaScript FAQ section, too: Here are some pretty good explanations and examples
Ok, why should you use this:
Suppose my script is running, and there are a couple of things (I'm, for instance, looping through a nodes list) I might be needing later on. That's why I might choose to do something like this:
for(var i=0;i<nodesList.lenght;i++)
{
if (nodesList[i].id==="theOneINeed")
{
aClosure = (function(node,indexInNodesList)//assign here
{
return function()
{
node.style.display = 'none';//usable after the parent function returns
alert(indexInNodesList+ ' is now invisible');
}
})(nodesList[i],i);//pass the element and its index as arguments here
break;
}
}
i = 99999;
aClosure();//no arguments, but it'll still work, and say i was 15, even though I've just
//assigned another value to i, it'll alert '15 is now invisible'
What this enables me to do is to prevent function arguments from being garbage collected. Normally, after a function returns, all its var's and arguments are GC'd. But in this case, the function returned another function that has a link to those arguments (it needs them), so they're not GC'ed for as long as aClosure exists.
As I said in my comment. Google closures, practice a bit, and it'll dawn on you... they really are quite powerful
I have about 12 div elements in a class called "item"
i have used for-loop to attach a onClick javascript function to each one of them:
for(var i = 0; i < $$(".item").length; i++){
$$(".item")[i].observe("click", detailPopup);
}
so if I click any of the elements that are of "item" class, it'll run "detailPopup" function. But I want to pass a parameter to that function. More specifically, I want "this"() to be passed.
How do I do that??
I think I made my question as specific as possible, but in case I didn't, let me know, and I'll clarify my question.
THANKS!
Although I haven't explicitly tried this myself I believe you should be able to burn in your parameters.
$$('.item').invoke('observe', 'click', detailPopup.curry(this));
A reference to this will then be passed before all other parameters. Your function might look something like the following...
function detailPopup(parent, event)
{
...
The original meaning of this is preserved for the handler's scope, that of the triggered element. Also I used invoke to avoid all that messing around with index values and anonymous functions and stuff.
You can bind this to the function:
for(var i = 0; i < $$(".item").length; i++){
$$(".item")[i].observe("click", detailPopup.bind(this));
}
JSFiddle Example
You'll want to create an anonymous function. In place of detailPopup, you should put something like: function() { detailPopup(that); }
What is that? Because the value of this isn't transferred to nested functions, you need to assign it to a variable before you can use it. Example: var that = this;
var that = this;
for(var i = 0; i < $$(".item").length; i++){
$$(".item")[i].observe("click", function() { detailPopup(that); });
}
You can do it like this:
for(var i = 0; i < $$(".item").length; i++){
(function(t){
$$(".item")[i].observe("click", function { detailPopup(t); });
})(this);
}
the (function(t){ ... })(this); is a closure which ensures that this is the correct this otherwise you will get scoping issues. This is important in a few situations, for loops being one of them. If you didnt have a for loop you could have done this:
$$(".item")[i].observe("click", function { detailPopup(someParameter); });