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.
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 parentscope
. Since this will always happen after yourfor
loop has completed, it will always find the last value ofsub
, i.e. whateversub
was when yourfor
loop was completed. So all of your event listeners will use the last value ofsub
.We can get the desired behavior by creating a closure. Replace this:
with this:
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 thescope
inside the IIFE (called creating a closure over thatscope
). Now, when the event handler goes looking forlocalSub
, it will find it in the new parentscope
and it will have the value we want.