2

一年前在大学课堂上学习了一些基础知识后,我才刚刚开始自学 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])});
}

这显然不起作用,因为在循环退出后 idx 等于 rows.length - 1 所以 SelectRow 中的行参数是 rows[rows.length - 1],它始终是最后一行;也就是说,i 的值不会“保存”为事件侦听器回调的参数。如何使用自己保存的参数调用事件侦听器?该线程似乎提供了一种使用 onclick 属性执行此操作的方法,但我知道这已被弃用,并且 addEventListener (或一些与 IE 兼容的怪物 addEvent 库)现在是“正确”的方法。

4

2 回答 2

2

您遇到了关闭问题。这里有一个参考:循环内的 JavaScript 闭包——简单实用的例子

试试这个:

for (var i = 0; i < rows.length; i++) {
    (function (idx) {
        rows[idx].addEventListener("click", function() {
            SelectRow(rows[idx]);
        });
    })(i);
}

http://jsfiddle.net/2Vs5w/2/

当然,您实际上并不需要传递rows[idx]SelectRow- 您可以传递this而不必处理创建闭包来捕获当时的值i。所以是这样的:

for (var i = 0; i < rows.length; i++) {
    rows[idx].addEventListener("click", function() {
        SelectRow(this);
    });
}

http://jsfiddle.net/2Vs5w/3/

在示例中,我包含了一个用于有效地将事件附加到元素的函数:

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;
    }
}

尝试使用现代标准addEventListener,然后尝试使用IE的方式- attachEvent,然后是DOM0方式- element.onevent。有几个地方可以获得这个功能,甚至可能是更好的版本,但这现在应该可以工作了:)

于 2013-03-22T13:58:24.983 回答
1

您已经找到了常见的 JavaScript 陷阱!:)

  1. 变量具有函数作用域
  2. 闭包维护对父作用域的引用(不是变量值的副本)。

如果你总结 1 + 2,你就会明白为什么这条线是错误的:

   rows[idx].addEventListener("click", function(){SelectRow(rows[idx])});

闭包中的是对父作用域(2)的引用,为什么idx在循环结束时?idx = rows.length将循环放在内部并不重要var,声明位于函数级别(1)。

您可以使用@Ian 解决方案来复制 idx。

注意 1:事件处理程序接收一个事件对象,您可以使用它event.target来获取触发事件的对象,这样您就不需要传递row[idx]

   rows[idx].addEventListener("click", function(evt){SelectRow(evt.target)});

注意 2:通常将事件处理程序附加到每一行可能效率低下,请尝试将事件处理程序附加到表中

于 2013-03-22T14:02:14.867 回答