避免在JS中分配事件处理程序的循环

时间:2013-05-23 11:14:32

标签: javascript

我有这段代码,其中我试图接受像classListquerySelector这样的JS函数,但由于处理程序的循环等问题,它最终会变得非常冗长:

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来为每个元素分配一个事件处理程序?

3 个答案:

答案 0 :(得分:3)

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

然而,it's ES5. Most modern browsers have it though。另外,the polyfill is also a loop,只是从你身上抽象出来。

Here's your code,几乎直接转换为使用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);
  });

});

答案 1 :(得分: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');
    }
}

答案 2 :(得分: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);
});