JS 中的 addEventListener 没有按预期工作 [英] addEventListener in JS not working as expected

查看:20
本文介绍了JS 中的 addEventListener 没有按预期工作的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

请先浏览代码.

index.html 是:

<html><head><title>首页</title><script src="js/script.js"></script></head><body onLoad="init()">
    <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>
<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>

script.js 是:

function init() {var 天空 = 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 = "超级菜单"+i;如果 (subs.length > 0) {var sub = subs[0];supcont.addEventListener("点击",function() {切换可见性(子);});supcont.style.background = "#"+sky+sky+sky;sub.style.background = "#"+sky+sky+sky;天空 += 4;}}}功能切换可见性(对象){object.style.visibility =(object.style.visibility == "hidden" ?"visible" :"hidden");}

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

附言我认为问题出在 addEventListener 上.

解决方案

这是一个常见问题,但我会尝试给出比我发现的更好的解释.当您将函数作为参数传递时(就像定义事件处理程序时一样),那时 javascript 不会评估该函数,而是将该函数与其父 scope 的引用一起存储.>

在触发事件处理程序之前不会评估该函数.这时候,解释器会检查父scopesub的值.由于这总是在您的 for 循环完成后发生,因此它总是会找到 sub 的最后一个值,即当您的 sub 是什么subcode>for 循环已完成.因此,您所有的事件侦听器都将使用 sub 的最后一个值.

我们可以通过创建一个 关闭.替换这个:

supcont.addEventListener("click",function() {切换可见性(子);});

这样:

(function(localSub) {supcont.addEventListener("点击",function() {切换可见性(localSub);});})(子);

之所以有效,是因为我们通过调用 IIFE 用新的父 scope 包装每个事件处理程序声明.这会强制事件处理程序在 IIFE 中保留 scope 的副本(称为在该 scope 上创建闭包).现在,当事件处理程序去寻找 localSub 时,它会在新的父 scope 中找到它并且它会有我们想要的值.

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 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.

这篇关于JS 中的 addEventListener 没有按预期工作的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆