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

在页面上,我有一张美国和加拿大的SVG地图,以及省和州的HTML列表。 hover在任何省份上,无论是列表中的名称还是地图上的描述,都应使名称和描述变为不同的颜色。 所有名称和路径都已具有逻辑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); } } } 

我试图告诉它要做的是向每个元素添加一个鼠标hover监听器,触发对该元素所涉及的所有元素的更改。

实际发生的事情是,将鼠标hover在整个页面上的任何内容会触发最后一个事件监听器,即怀俄明州的监听器。 就像我更改$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); 

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