1

我有这段代码,我试图在其中包含 JS 函数classListquerySelector但由于处理程序的循环等,它最终变得非常冗长:

var cg = document.querySelectorAll('.control-group'),
    cgL = cg.length;

while (cgL--) {
    var _cg = cg[cgL],
        inputs = _cg.querySelectorAll('input'),
        i = 0;

    for (l = inputs.length; i < l; i++) {
        inputs[i].addEventListener('focus', focus, false);
        inputs[i].addEventListener('blur', focus, false);
    }

    function focus() {
        _cg.classList.toggle('focus');
    }
}

小提琴:http: //jsfiddle.net/YGeh5/3/

有什么方法可以避免必须循环一个 NodeList 来为它的每个元素分配一个事件处理程序?

4

3 回答 3

3

你可以使用Array.forEach(). 但是由于您返回了一个 NodeList,并且forEach()在其原型中没有,您需要使用.call它并将其作为上下文提供。

但是,它是 ES5。大多数现代浏览器都有它。此外,polyfill 也是一个循环,只是从您那里抽象出来的。

这是您的代码,几乎直接转换为使用forEach()

var cg = document.querySelectorAll('.control-group');

Array.prototype.forEach.call(cg, function (group) {
  var inputs = group.querySelectorAll('input');

  function focus() {
    group.classList.toggle('focus');
  }

  Array.prototype.forEach.call(inputs, function (input) {
    input.addEventListener('focus', focus, false);
    input.addEventListener('blur', focus, false);
  });

});
于 2013-05-23T11:23:20.927 回答
2

您可以直接在“.control-group”上委派事件,然后按 e.target 过滤输入。它还将在大量输入上消耗更少的内存。工作示例:http: //jsfiddle.net/RZhTj/1/

var cg = document.querySelectorAll('.control-group'),
    cgL = cg.length;

while (cgL--) {
    var _cg = cg[cgL];

    _cg.addEventListener('focus', focus, true);
    _cg.addEventListener('blur', focus, true);

    function focus(e) {
        if (e.target instanceof HTMLInputElement) _cg.classList.toggle('focus');
    }
}
于 2013-05-23T11:44:04.307 回答
1

如果浏览器有Array#forEach,您可能希望使用 forEach 来迭代 NodeList。您首先必须将其转换为数组。一种方法是使用 Array.prototype.slice。

Array.prototype.slice.call(_cg.querySelectorAll('input'), 0).forEach(function (input) {
    input.addEventListener('focus', focus, false);
});

或者只是在 NodeList 上调用 Array.prototype.forEach:

Array.prototype.forEach.call(_cg.querySelectorAll('input'), function (input) {
    input.addEventListener('focus', focus, false);
});

请注意,您可以将两个 querySelectorAll 分组在一个查询中。

您还可以将焦点闭包移出循环,因为它实际上不需要对元素的引用。

你的整个代码变成:

function focus(event) {
    event.target.classList.toggle('focus');
}

Array.prototype.forEach.call(document.querySelectorAll('.control-group input'), function (input) {
    input.addEventListener('focus', focus, false);
    input.addEventListener('blur', focus, false);
});
于 2013-05-23T11:24:40.207 回答