问题
Firstly skim codes please.
index.html is :
<html><head><title>Home</title><script src="js/script.js"></script></head>
<body onLoad="init()">
<ul class="sup" id="sup">
<li class="supitem">
<a href="#" class="supcont">Home<div class="v"></div></a>
<ul class="sub">
<li class="subitem"><a href="#" class="subcont">Home1</a></li>
<li class="subitem"><a href="#" class="subcont">Home2</a></li>
<li class="subitem"><a href="#" class="subcont">Home3</a></li>
</ul>
</li>
<li class="supitem">
<a href="#" class="supcont">Blog<div class="v"></div></a>
<ul class="sub">
<li class="subitem"><a href="#" class="subcont">Blog1</a></li>
<li class="subitem"><a href="#" class="subcont">Blog2</a></li>
<li class="subitem"><a href="#" class="subcont">Blog3</a></li>
</ul>
</li>
</ul>
</body>
</html>
script.js is :
function init() {
var sky = 0;
var sup = document.getElementById("sup");
var supitems = sup.getElementsByClassName("supitem");
for (var i = 0, ln = supitems.length; i < ln; i++) {
var supconts = supitems[i].getElementsByClassName("supcont");
var subs = supitems[i].getElementsByClassName("sub");
var supcont = supconts[0];
supcont.innerHTML = "SuperMenu"+i;
if (subs.length > 0) {
var sub = subs[0];
supcont.addEventListener("click",function() {
toggleVisibility(sub); });
supcont.style.background = "#"+sky+sky+sky;
sub.style.background = "#"+sky+sky+sky;
sky += 4;
}
}
}
function toggleVisibility(object) {
object.style.visibility =
(object.style.visibility == "hidden" ?"visible" :"hidden");
}
What I would like to do is when I press supermenu all sub-menus' visibility to be toggled. But I don't know where I have made a mistake. When I press Supmenu0, submenus of Supmenu1 are toggled, not submenus of Supmenu1. Thanks in advance.
P.S. I think the problem is in addEventListener.
回答1:
This is a frequently asked question but I will try to give a better explanation than I have found. When you pass a function as a parameter (as when defining your event handler), javascript does not evaluate the function at that time, but instead stores the function along with a reference to its parent scope
.
The function does not get evaluated until the event handler is triggered. At that time, the interpreter will check the value of sub
in the parent scope
. Since this will always happen after your for
loop has completed, it will always find the last value of sub
, i.e. whatever sub
was when your for
loop was completed. So all of your event listeners will use the last value of sub
.
We can get the desired behavior by creating a closure. Replace this:
supcont.addEventListener("click",function() {
toggleVisibility(sub); });
with this:
(function(localSub) {
supcont.addEventListener("click",function() {
toggleVisibility(localSub);
});
})(sub);
The reason this works is because we wrap each event handler declaration with a new parent scope
by invoking an IIFE. This forces the event handler to retain a copy of the scope
inside the IIFE (called creating a closure over that scope
). Now, when the event handler goes looking for localSub
, it will find it in the new parent scope
and it will have the value we want.
来源:https://stackoverflow.com/questions/16566721/addeventlistener-in-js-not-working-as-expected