<button>test</button>
<button>test</button>
<button>test</button>
<button>test</button>
<button>test</button>
<script>
var nodes = document.getElementsByTagName('button');
function clicked(i){
console.log('pass');
// Closure
// return function(){
// console.log(i);
// }
}
for (var i = 0; i < nodes.length; i++) {
nodes[i].addEventListener('click', clicked(i));
}
</script>
I am try to have a fully understanding on js closure, the above function add even listener to the buttons. it console log 'pass' 5 times and then does nothing when button clicked. but if i uncomment out the closure bit (return), console.log will echo out the i, but not log 'pass'. I did find relevant answer but i don't get why with closure onclick doesn't log the 'pass' string when button clicked instead does log out the i.
How do JavaScript closures work?
What you are doing here is directly calling the function clicked
instead of setting it as an event handler.
i don't get why with closure onclick doesn't log the 'pass' string when button clicked instead does log out the i.
well, your closure was
return function(){
console.log(i);
}
Why would that log anything except i
?
The function that creates this closure should log "pass" five times during page initialization (as you call five times in the loop, each time it logs a line and returns the closure).
You could use bind
.
nodes[i].addEventListener('click', clicked.bind(nodes[i], i));
This will give you i
as a parameter to the function, so that you can do
console.log("pass", i);
Attempting to answer the original question as asked...
but i don't get why with closure onclick doesn't log the 'pass' string
when button clicked instead does log out the i
Go back to your code without the commented out section...
<button>test</button>
<button>test</button>
<button>test</button>
<button>test</button>
<button>test</button>
<script>
var nodes = document.getElementsByTagName('button');
function clicked(i) {
console.log('pass');
//Closure
return function(){
output(i);
}
}
function output(i) {
alert('The value of the closure i for this button is: '+i);
}
for (var i = 0; i < nodes.length; i++) {
nodes[i].addEventListener('click', clicked(i));
}
</script>
<style>
html {
counter-reset: button-count -1;
}
button:after {
content: ' ' counter(button-count);
counter-increment: button-count;
}
</style>
(I also added a little extra to help explain)
When you initialise the event listeners in the for loop
, you are passing the return value of clicked(i)
as the function pointer to be called when the click occurs on node[i]
.
In your original code, the return value is a reference to an anonymous function defined as:
function(){
console.log(i);
}
So this is what is executed when you click button i - not clicked
.
clicked
is only executed during the for loop
. You will see pass one time for each button in the console due to the for loop
.
The lexical scope for the anonymous function includes the scope of it's containing function clicked
so the parameter i
is in scope for the anonymous function. Each time clicked
terminates, during the for loop
, a separate closure is created on the parameter i
, preserving it's value for that particular call.
In the code above, I created a function called output that receives a parameter i, so this
function(){
output(i);
}
... is called when you click button i: also having a closure on i
.
I also added some CSS styling to number the buttons.