I'm going through JavaScript: The Definitive Guide by David Flanagan and I am stuck at the following example:
window.onload = function() {
var elements = document.getElementsByClassName("reveal");
for(var i = 0; i < elements.length; i++) {
var elt = elements[i];
var title = elt.getElementsByClassName("handle")[0];
title.onclick = function() {
if(elt.className == "reveal") elt.className = "revealed";
else if(elt.className == "revealed") elt.className = "reveal;"
}
}
};
<div class="reveal">
<h1 class="handle">Click Here to Reveal Hidden Text</h1>
<p>This paragraph is hidden. It appears when you click on the title.</p>
</div>
The problem is that the code in its current form works, but if I were to add more than one div with the class of reveal, no matter what h1 element I click on, only the last one will expand. What should I change in the code for it to work on separate divs?
P.S. I do understand that this kind of question is asked quite frequently due to the nature of the problem being closures within a loop, but I'm not able to make my code work given the solutions to similar questions on SO. Thank you.
In JavaScript, variable scope is set at the function level. Functions create closures which essentially define the boundaries of the scope. A child scope can access variables defined in a parent scope, but not the other way around.
When you invoke the onclick handler function, it must look up the scope chain to find the variable elt because elt is not declared within the current scope.
The next level up in the scope chain is your window.onload handler function (i.e., closure). At this scope, the variable elt is declared and it's value is set by the loop. But remember, when you click the <h1> and invoke the onclick handler, the loop has already terminated. Therefore, elt will always be the last value set in the loop. This is why you're always getting elt as the last element.
To resolve this issue, you may use an immediate executing function. The immediate executing function creates a new closure (and scope) where elt is declared locally. Because the variable elt is now declared in this new inner scope, it is not modified by the loop in the parent scope:
window.onload = function() {
var elements = document.getElementsByClassName("reveal");
for(var i = 0; i < elements.length; i++) {
var elt = elements[i];
var title = elt.getElementsByClassName("handle")[0];
title.onclick = (function(elt) {
return function() {
if(elt.className == "reveal") elt.className = "revealed";
else if(elt.className == "revealed") elt.className = "reveal;"
};
})(elt);
}
};
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With