Javascript:添加“选择”表行的能力,使用参数调用事件侦听器
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/15572082/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Javascript: Adding ability to "select" rows of a table, calling an event listener with arguments
提问by dpitch40
I am just starting off teaching myself Javascript after learning some basics in a university class a year ago. For a web app I am supposed to write, I need to allow users to "select" rows of a table, preferably by clicking on them. I have a strong Python background and my approach probably reeks of GTK paradigms. I have a nagging fear that I'm "reinventing the wheel" somehow by trying to do this when there is a much more standard or accepted way, but like I said, don't know much Javascript yet. My Javascript code to try and do this is:
一年前在大学课堂上学习了一些基础知识后,我才开始自学 Javascript。对于我应该编写的网络应用程序,我需要允许用户“选择”表格的行,最好是通过单击它们。我有很强的 Python 背景,我的方法可能散发着 GTK 范式的气息。当有更标准或可接受的方式时,我一直担心我会以某种方式“重新发明轮子”,尝试这样做,但就像我说的那样,还不太了解 Javascript。我尝试执行此操作的 Javascript 代码是:
//Get list of rows in the table
var table = document.getElementById("toppings");
var rows = table.getElementsByTagName("tr");
var selectedRow;
//Row callback; reset the previously selected row and select the new one
function SelectRow(row) {
if (selectedRow !== undefined) {
selectedRow.style.background = "#d8da3d";
}
selectedRow = row;
selectedRow.style.background = "white";
}
//Attach this callback to all rows
for (var i = 0; i < rows.length; i++) {
var idx = i;
rows[idx].addEventListener("click", function(){SelectRow(rows[idx])});
}
This apparently doesn't work because after the loop exits idx is equal to rows.length - 1 so the row argument in SelectRow is rows[rows.length - 1], which is always the last row; that is, the values of i are not "saved" as arguments to the event listener callback.How can I call an event listener with its own saved arguments? This threadseems to give a way to do this with the onclick property, but I understand that this is deprecated and addEventListener (or some monstrous IE-compatible addEvent library) is now the "correct" way to do it.
这显然不起作用,因为在循环退出后 idx 等于 rows.length - 1 所以 SelectRow 中的行参数是行 [rows.length - 1],它总是最后一行;也就是说,i 的值不会“保存”为事件侦听器回调的参数。如何使用自己保存的参数调用事件侦听器?该线程似乎提供了一种使用 onclick 属性执行此操作的方法,但我知道这已被弃用,并且 addEventListener(或某些与 IE 兼容的可怕的 addEvent 库)现在是执行此操作的“正确”方法。
采纳答案by Ian
You are having a closure problem. Here's a reference: JavaScript closure inside loops – simple practical example
您遇到了关闭问题。这是一个参考:循环内的 JavaScript 闭包——简单实用的例子
Try this:
试试这个:
for (var i = 0; i < rows.length; i++) {
(function (idx) {
rows[idx].addEventListener("click", function() {
SelectRow(rows[idx]);
});
})(i);
}
Of course, you don't actually need to pass rows[idx]
to SelectRow
- you could pass this
and not have to deal with creating a closure to capture the value of i
at that time. So something like this:
当然,您实际上并不需要传递rows[idx]
到SelectRow
- 您可以传递this
并且不必处理创建闭包来捕获当时的值i
。所以像这样:
for (var i = 0; i < rows.length; i++) {
rows[idx].addEventListener("click", function() {
SelectRow(this);
});
}
In the examples, I included a function for effectively attaching an event to an element:
在示例中,我包含了一个有效地将事件附加到元素的函数:
function addEvent(element, evt, callback) {
if (element.addEventListener) {
element.addEventListener(evt, callback, false);
} else if (element.attachEvent) {
element.attachEvent("on" + evt, callback);
} else {
element["on" + evt] = callback;
}
}
Tries to use the modern standard addEventListener
, then attempts to use IE's way - attachEvent
, then the DOM0 way - element.onevent
. There are several places to get this function from, and probably even better versions, but this should work for now :)
尝试使用现代标准addEventListener
,然后尝试使用 IE 的方式 - attachEvent
,然后是 DOM0 方式 - element.onevent
。有几个地方可以获得这个功能,甚至可能有更好的版本,但这现在应该有效:)
回答by Diego
You have found the usual JavaScript gotcha! :)
您已经找到了常见的 JavaScript 问题!:)
- Variables have function scope
- Closures maintains a reference to the parent scope (not a copy of variable values).
- 变量具有函数作用域
- 闭包维护对父作用域的引用(不是变量值的副本)。
If you sum up 1 + 2 you'll figure out why this line is wrong:
如果你总结 1 + 2 你就会明白为什么这条线是错误的:
rows[idx].addEventListener("click", function(){SelectRow(rows[idx])});
The idx
in the closure is a reference to the parent scope (2), at the end of the loop idx = rows.length
why? It doesn't matter that you put the var
inside the loop, declarations are at function level (1).
该idx
在封闭是与母体范围(2)的参考,在循环结束时idx = rows.length
为什么呢?var
将循环放入循环内并不重要,声明位于函数级别 (1)。
You can use @Ian solution to copy idx.
您可以使用@Ian 解决方案来复制 idx。
NOTE 1:Events handlers receives an event object, you can use event.target
to get the object that fired the event, in that way you don't need to pass row[idx]
:
注意 1:事件处理程序接收一个事件对象,您可以使用它event.target
来获取触发事件的对象,这样您就不需要传递row[idx]
:
rows[idx].addEventListener("click", function(evt){SelectRow(evt.target)});
NOTE 2:Usually attaching event handlers to each row can be inefficient, try by attaching the event handler to the table instead
注意 2:通常将事件处理程序附加到每一行可能效率低下,请尝试将事件处理程序附加到表中