有没有一种纯JavaScript的方法可以将一个函数应用于多个元素的事件?

5
我想使用纯Javascript将一个函数绑定到多个事件上。
在jQuery中,我会使用以下代码:
$('.className').click(function(e){ //do stuff });

所以我尝试使用纯JS:
document.getElementsByClassName('className').onclick = function(e){ //do stuff };

这段话的意思是:

这个方法行不通,因为getElementsByClassName返回一个数组,而不是DOM对象。

我可以通过循环遍历数组来解决问题,但这似乎过于冗长,而且应该不必要:

var topBars = document.getElementsByClassName('className');
for(var i = 0; i < topBars.length; i++){
    topBars[i].onclick = function(e){ //do stuff };
}

有没有一种纯JavaScript的标准方法来完成这个?

1
jQuery做的是相同的循环...只不过它是在幕后完成的。 - charlietfl
1
这正是 jQuery 所做的方式。然而你也可以使用 Array.map.call()。但它并不真的更慢/更快。 - Mouser
它可以是 $(document).on('click', '.className', ...),这实际上只会附加一个处理程序。同样的事情也可以在纯JS中完成(使用事件冒泡)。 - zerkms
你可以在最近的父元素上使用事件委托。 - elclanrs
你也可以扩展NodeListHTMLCollection的原型,但仍然会发生循环(如前所述),否则就需要委托。 - David Thomas
@zerkms,那不是jQuery吗?这正是问题所说的不要使用的东西。 - Hlawuleka MAS
3个回答

10
您可以将事件处理程序添加到父元素上,然后确定是否单击了具有所需类名的子元素之一:
var parent = document.getElementById('parent');
parent.addEventListener('click', function (e) {
    if ((' ' + e.target.className + ' ').indexOf(' item ') !== -1) {
        // add logic here
        console.log(e.target);
    }
});

这里有一个示例

或者...

var parent = document.getElementById('parent');
parent.addEventListener('click', function (e) {
    Array.prototype.forEach.call(parent.querySelectorAll('.item'), function (el) {
        if (el === e.target) {
            // add logic here
            console.log(e.target);
        }
    });
});

这里有一个示例


上述代码片段仅在单击具有指定类的元素时起作用。换句话说,如果单击该元素的子元素,则无法起作用。为解决此问题,您可以使用以下方法:

var parent = document.getElementById('parent');
parent.addEventListener('click', function (e) {
    var target = e.target; // Clicked element
    while (target && target.parentNode !== parent) {
        target = target.parentNode; // If the clicked element isn't a direct child
        if (!target) { return; } // If element doesn't exist
    }
    if ((' ' + target.className + ' ').indexOf(' item ') !== -1){
        // add logic here
        console.log(target);
    }
});

替代示例

(此链接为英文原文)
var parent = document.getElementById('parent');

parent.addEventListener('click', function (e) {
    var target = e.target; // Clicked element
    while (target && target.parentNode !== parent) {
        target = target.parentNode; // If the clicked element isn't a direct child
        if (!target) { return; } // If element doesn't exist
    }
    Array.prototype.forEach.call(parent.querySelectorAll('.item'), function (el) {
        if (el === target) {
            // add logic here
            console.log(target);
        }
    });
});

这里是示例


e.target.className.indexOf('item') === 0 应该改为 e.target.className.indexOf('item') !== -1 - James G.
@JoshCrozier,我认为第二个例子变得非常复杂了。它是正确的,仍然只需要一个事件处理程序。所以我仍然喜欢它 :-) - Mouser
1
虽然我认为循环对于我的当前需求来说更好(而且更简洁),但我将选择这个答案,因为它似乎记录了最佳的循环替代方案。 - James G.
3
使用indexOf将匹配类名中的任何字符串,例如'items'、'lotsOfitems'等。考虑使用' ' + target.className + ' ').indexOf(' item ')来按照匹配类值的规则进行匹配。 - RobG
@RobG 已更新。谢谢 Rob,感谢您的评论。 - Josh Crozier

1
作为一种技巧,当DOM使用原型继承模型实现时(大多数浏览器但不是全部),您可以向NodeList构造函数添加一个迭代器:
if (NodeList && NodeList.prototype && !NodeList.prototype.forEach) {
  NodeList.prototype.forEach = function(callback, thisArg) {
    Array.prototype.forEach.call(this, callback, thisArg)
  }
} 

然后:

document.getElementsByClassName('item').forEach(function(el){
  el.addEventListener('click', someFn, false);
})

或者

document.querySelectorAll('.item').forEach(function(el){
  el.addEventListener('click', someFn, false);
})

当然,在生产环境中你不应该这样做(不要改动宿主对象等等),但如果可以这样做岂不美哉?或者DOM列表和集合中添加迭代器呢?


不错,我没想到那是可能的。 - Josh Crozier

-1
var elems = document.querySelectorAll('.className');
var values = Array.prototype.map.call(elems, function(obj) {
    return obj.onclick = function(e){ //do stuff };
});

这是一个与传统循环无关的示例(改编自MDN),可以用来演示如何实现它。

2
  1. 它不比 OP 的代码更好
  2. 实际上它更糟糕
- zerkms
@zerkms。并不更好。我在评论中已经说过了。但是你能解释一下为什么它更糟糕吗?这个示例可以在MDN上找到(这本身并不意味着它是一个好的示例)。请给我解惑一下。 - Mouser
2
[class="classname"] --- 这个选择器与问题中的选择器不完全相同。还有一个个人的事情:[] .map.call --- 我更喜欢在不必要时不创建对象:Array.prototype.map.call 就可以了。 - zerkms
问题不在于大小写。这是一个严格的比较。它不会匹配 class="foo className" - zerkms
1
我们还没有完成 - 还是错的! :-) PS:抱歉没有指出来,我以为“CSS(高级)”和“Javascript(高级)”很明显 :-) - zerkms
显示剩余5条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接