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

Javascript 为什么,当我在循环中添加一组事件侦听器时,每个元素都会触发最后添加的侦听器?,javascript,jquery,events,svg,Javascript,Jquery,Events,Svg,在一个页面上,我有一个美国和加拿大的SVG地图,以及一个省和州的HTML列表。将鼠标悬停在任何省份上,无论是其在列表中的名称还是其在地图上的描述,都会使名称和描述变成不同的颜色。所有名称和路径上都已经有逻辑ID/类 (目前程序混乱不堪,请原谅。) jQuery的事件函数在SVG上不起作用,尽管我知道有一个jQuery插件应该会有所帮助,但我认为这将是一个很好的机会,可以使用比我习惯的更多的普通Javascript 代码中最相关的部分是Javascript第46行到第69行的makeMapInte

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

(目前程序混乱不堪,请原谅。)

jQuery的事件函数在SVG上不起作用,尽管我知道有一个jQuery插件应该会有所帮助,但我认为这将是一个很好的机会,可以使用比我习惯的更多的普通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);
        }
    }
}
函数makeMapInteractive(省){
对于(省中的var省){//迭代每个州/省代码
变量$HTMLtargets=$('ul.provinces li.'+省);
var$SVGtargets=$('path{0},g{0}path'。格式(省));
var$allTargets=$HTMLtargets.add($SVGtargets);
//我先用$()试了一下,每次都是;,但都不行,
//我对它进行了评论,并尝试不使用它。两者都不起作用。
/*$allTargets.each(函数(){
this.addEventListener('mouseover',函数(e){
控制台日志(e);
$HTMLtargets.css('color','#990000');
$SVGtargets.attr('fill','#990000');
},错)
}); */
对于(变量i=0;i<$allTargets.length;i++){
$allTargets.get(i).addEventListener('mouseover',函数(e){
$HTMLtargets.css('color','#990000');
$SVGtargets.attr('fill','#990000');
},假);
}
}
}
我试图告诉它要做的是在每个元素中添加一个mouseover侦听器,这会触发该元素所在区域中涉及的所有元素的更改

实际上,将鼠标悬停在整个页面上的任何内容上都会触发添加的最后一个事件侦听器,即怀俄明事件侦听器。这就像当我更改
$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);
//创建闭包以冻结目标变量
(函数(hTargets、starget){
对于(变量i=0;i<$allTargets.length;i++){
$allTargets.get(i).addEventListener('mouseover',函数(e){
css('color','#990000');
sTargets.attr('fill','#990000');
},假);
}
})($HTMLtargets,$SVGtargets);

仅供参考,我更改了闭包中变量的名称,以使发生的事情更加明显。不需要将参数的名称更改为立即执行的函数表达式,因为它们只会覆盖以前定义的参数,但我认为如果更改名称,情况会更清楚。

每个循环中的目标都在更改。你需要把它们包起来。这个问题被问了几十次,;对不起,我要找一个合适的副本。那个该死的怀俄明州充满了事件!:)@数学-答案可能与之前给出的答案相同,但问题和这种特殊情况并非重复。这个问题很独特。@jfriend00我不同意。他们都是“我的变量不是我想象的那样;我需要一个闭包。”我通常想知道为什么要更改变量名称,这只是为了可读性还是我遗漏了什么?是的,我想他只是想更好地说明实际发生了什么。幸运的是,尽管我在Javascript方面不够聪明,不需要问就知道该做什么,但我确实足够理解它,以至于从他的一段话中立即意识到了我的错误。谢谢,@jfriend00@A.Wolff-one不必更改变量名,但我发现对于这个概念中的新手来说,更清楚的是,我们正在创建一个新的参数变量,其值在闭包中捕获。如果使用相同的变量名,那么实际发生的情况(用新变量覆盖相同名称的变量)就不那么明显了。@jfriend00 Thx,感谢您的反馈!