javascript - 为什么当我在循环中添加一堆事件监听器时,每个元素都会触发最后添加的监听器?

标签 javascript jquery events svg

<分区>

在一个页面上,我有一个美国和加拿大的 SVG map ,以及一个省和州的 HTML 列表。将鼠标悬停在任何省份上,无论是列表中的名称还是 map 上的描述,名称和描述都会变成不同的颜色。所有名称和路径都已经具有逻辑 ID/类。

Here's a fiddle with my code. (目前这是一个可怕的程序困惑,所以请原谅我。)

jQuery 的事件函数在 SVG 上不起作用,虽然我知道有一个 jQuery 插件据说可以提供帮助,但我认为这将是一个很好的机会,可以比我以前使用的更多地使用 vanilla Javascript。

代码中最相关的部分是 Javascript 第 46 到 69 行的 makeMapInteractive 函数:

function makeMapInteractive(provinces) {

    for(var province in provinces) { // Iterate over every state/province code
        var $HTMLtargets = $('ul.provinces li.' + province);
        var $SVGtargets = $('path#{0}, g#{0} path'.format(province));
        var $allTargets = $HTMLtargets.add($SVGtargets);

        // I tried it first with $().each(); when that didn't work,
        // I commented it out and tried without it. Neither one works.

        /* $allTargets.each(function() {
            this.addEventListener('mouseover', function(e) {
                console.log(e);
                $HTMLtargets.css('color', '#990000');
                $SVGtargets.attr('fill', '#990000');
            }, false)
        }); */

        for(var i = 0; i < $allTargets.length; i++) {
            $allTargets.get(i).addEventListener('mouseover', function(e) {
                $HTMLtargets.css('color', '#990000');
                $SVGtargets.attr('fill', '#990000');
            }, false);
        }
    }
}

我试图告诉它做的是向每个元素添加一个鼠标悬停监听器,它会触发该元素所在省份中涉及的所有元素的更改。

实际发生的是,将鼠标悬停在整个页面上的任何内容上会触发最后添加的事件监听器,即怀俄明州的事件监听器。这就像当我更改 $allTargets 变量时,它将所有先前添加的监听器更改为其新值中包含的元素。但我看不出这是怎么回事,因为我将事件监听器应用于该变量内的 DOM 元素,而不是 jQuery 对象本身。

有人能准确解释这里发生了什么吗?我知道我在这里使用了一些 jQuery,但我希望使用的答案不超过我已经使用的;这是我需要提高的普通 Javascript 技能。

最佳答案

问题是您的 $HTMLtargets$SVGtargets 变量不是您希望它们在事件处理程序回调中的内容,因为当事件触发时(稍后) ,您的外部 for 循环已经完成,因此这两个变量在它们的结束值上。

您将需要一个闭包来为每个事件处理程序分别捕获这些变量。这是一种方法:

    // create closure to freeze the target variables
    (function(hTargets, sTargets) {
        for(var i = 0; i < $allTargets.length; i++) {
            $allTargets.get(i).addEventListener('mouseover', function(e) {
                hTargets.css('color', '#990000');
                sTargets.attr('fill', '#990000');
            }, false);
        }
    })($HTMLtargets, $SVGtargets);

仅供引用,我更改了闭包内变量的名称,以便更清楚地说明发生了什么。不需要将参数的名称更改为立即执行的函数表达式,因为它们只会覆盖先前定义的参数,但我认为如果更改名称会更清楚发生了什么。

关于javascript - 为什么当我在循环中添加一堆事件监听器时,每个元素都会触发最后添加的监听器?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/18880895/

相关文章:

jquery - 将 knockoutjs 与 jqGrid 一起使用

c# - rx里面有没有ThrottleOrMax之类的东西?

javascript - Bootstrap - 模态有时会中断滚动

javascript - 同时运行 2 个 html 视频 JS

javascript - 打印浏览器窗口的可视内容

jquery - YouTube HTML5 嵌入代码适用于桌面设备,但不适用于 iOS

javascript - 使用 datejs 插件根据文化格式化日期

javascript - 如何根据条件将 DIV 滚动到浏览器窗口的顶部?

c# - 通过事件传递数据

java - 停止 JSF 事件链