2012-05-18 53 views
3

这可能是一个简单的问题,但我无法弄清楚它的最佳答案。Javascript中的事件处理程序范围

我在屏幕上有10 <div>元素。他们每个人都有一个click()事件侦听器:

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> 
<html xmlns="http://www.w3.org/1999/xhtml"> 
<head> 
</head> 
<body> 
    <div id="element0">Click me! (0)</div> 
    <div id="element1">Click me! (1)</div> 
    <div id="element2">Click me! (2)</div> 
    <div id="element3">Click me! (3)</div> 
    <div id="element4">Click me! (4)</div> 
    <div id="element5">Click me! (5)</div> 
    <div id="element6">Click me! (6)</div> 
    <div id="element7">Click me! (7)</div> 
    <div id="element8">Click me! (8)</div> 
    <div id="element9">Click me! (9)</div> 
    <script type="text/javascript"> 
    for (var i = 0; i < 10; i++) { 
     var element = document.getElementById("element" + i); 
     element.onclick = function() { 
      alert("Element " + i); 
     } 
    } 
    </script> 
</body> 
</html> 

但每次我点击一个元素时,它说“元素10”!看来所有这些事件处理程序都使用相同的值i

我希望它显示“元素N”,其中N是当前元素的编号。我不想从元素ID中提取N。我也不想使用jQuery的data()方法来存储它。我相信这个问题一定有一个更简单的解决方案,但我找不到它。任何人?

+0

'i'在全局范围内声明,不在'onclick'函数内,因此当它在'onclick'中被访问时,它将从全局范围读取。它显示'10',因为那是循环的最后一次迭代。 – MrCode

回答

6

在所有点击处理程序共享的外部作用域中,只有一个变量i。您需要为每个闭包创建一个局部变量i。这将工作:

for (var i = 0; i < 10; i++) { 
    var element = document.getElementById("element" + i); 
    element.onclick = (function(i){ 
     // returns a new function to be used as an onclick handler 
     return function() { 
      alert("Element " + i); 
     } 
    })(i); // Pass in the value of the outer scope i 
} 

检查“臭名昭著的循环”问题this article(读整条每页)了解更多信息:)

+0

所以我为每个事件侦听器创建两个函数。没有更高效更清洁的版本吗? – AlexStack

+0

@AlexStack这非常高效。您的循环中有一个匿名函数,它会立即执行,并为每个点击处理程序返回一个带有本地i变量的新函数。每个事件处理程序仍然只有一个功能。 – Paulpro

+0

我知道,但在我们的项目中,每隔几秒就会有数千个这样的功能。这就是为什么我关心效率。 – AlexStack

2

所以你的问题是做循环的内部异步代码的性质另一篇文章说。在你的情况下,尽管完全有更好的方法来解决这个问题,那就是使用事件委托。

document.body.onclick = function(e) { 
    if (e.target.tagName.toLowerCase() === "div") { 
    alert("Element " + e.target.id); 
    } 
} 

如果您有jQuery的你可以这样做:

$(document.body).on("click", "div", function() { 
    alert($(this).attr("id")); 
}); 

欲了解更多信息请查看这篇文章: http://www.sitepoint.com/javascript-event-delegation-is-easier-than-you-think/

显然jQuery和其他库处理这些东西,并自动完成。

+0

谢谢,但正如我所说我不想解析触发元素的ID来获得N.另外我试了码。似乎需要将'this.id'替换为'e.target.id'才能正常工作。 – AlexStack

+0

感谢您的修复。事件委派使用的资源少于为每个div分配点击事件的资源,所以我仍然推荐使用其他方法。 –

+0

这是实际的HTML还是你想解决更复杂的问题? –

1

你可以抓住从元素的id属性数量:

for (var i = 0; i < 10; i++) { 
    var element = document.getElementById("element" + i); 
    element.onclick = function() { 

     var i = this.id.substr(7); 
     alert("Element " + i); 
    } 
}​ 

JsFiddle here

+0

有趣的解决方案。虽然如我所说,我不想解析编号来获得数字。这仅仅是一个例子,用来展示一个项目中更大的“范围问题”。 – AlexStack