JS中的addEventListener未按预期工作

时间:2013-05-15 13:43:33

标签: menu addeventlistener submenu

首先请浏览代码。

index.html是:

<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是:

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");
}

我想要做的是当我按下超级菜单切换所有子菜单的可见性时。但我不知道我在哪里弄错了。当我按下Supmenu0时,Supmenu1的子菜单被切换,而不是Supmenu1的子菜单。提前谢谢。

P.S。我认为问题出在addEventListener。

1 个答案:

答案 0 :(得分:1)

这是一个经常被问到的问题,但我会尝试提供比我发现的更好的解释。当您将函数作为参数传递时(如定义事件处理程序时),javascript不会在此时评估函数,而是存储函数以及对其父scope的引用。

在触发事件处理程序之前,不会对该函数进行求值。那时,口译员将检查父subscope的值。由于这将始终在for循环完成后发生,因此始终会找到sub的最后一个值,即sub循环完成时的for。因此,所有事件侦听器都将使用sub的最后一个值。

我们可以通过创建closure来获得所需的行为。替换这个:

supcont.addEventListener("click",function() {
    toggleVisibility(sub); });

用这个:

(function(localSub) {
    supcont.addEventListener("click",function() {
        toggleVisibility(localSub);
    });    
})(sub);

这是有效的原因是因为我们通过调用IIFE将每个事件处理程序声明包装为新的父scope。这会强制事件处理程序在IIFE中保留scope的副本(称为在scope上创建一个闭包)。现在,当事件处理程序查找localSub时,它会在新的父scope中找到它,它将具有我们想要的值。