给拥有相同类名的元素添加点击事件监听器

86

我有一个删除id的列表视图。我想为具有特定类的所有元素添加侦听器,并进行确认警报。

我的问题是,它似乎只会将侦听器添加到找到的第一个具有该类的元素。我尝试使用querySelectorAll,但它没有起作用。

var deleteLink = document.querySelector('.delete');

deleteLink.addEventListener('click', function(event) {
    event.preventDefault();

    var choice = confirm("sure u want to delete?");
    if (choice) {
        return true;
    }
});
  • List:
<?php 
    while($obj=$result->fetch_object())
    {
        echo '<li><a class="delete" href="removeTruck.php?tid='.$obj->id.'">'.$obj->id.'</a>'
            . '<a href="#" class="delete"></a>
                      </li>'."\n";
    }
    /* free result set */
    $result->close();       
    $mysqli->close();
?>

2
为什么要用jQuery标记它?顺便说一句,你最好委托事件,而不是为每个.delete元素设置自己的处理程序。 - A. Wolff
5个回答

165

你应该使用querySelectorAll。它返回NodeList,然而querySelector只会返回第一个找到的元素:

var deleteLink = document.querySelectorAll('.delete');

那么您需要循环:

for (var i = 0; i < deleteLink.length; i++) {
    deleteLink[i].addEventListener('click', function(event) {
        if (!confirm("sure u want to delete " + this.title)) {
            event.preventDefault();
        }
    });
}

同时,只有在 confirm === false 时才应该使用 preventDefault

值得注意的是,return false/true 只对使用 onclick = function() {...} 绑定的事件处理程序有用。对于使用 addEventListening 绑定的事件处理程序,应该使用 event.preventDefault()

演示: http://jsfiddle.net/Rc7jL/3/


ES6 版本

你可以通过使用 Array.prototype.forEach 迭代而不是 for 循环来使其更加简洁(并且更安全,避免闭包在循环中引起的问题):

var deleteLinks = document.querySelectorAll('.delete');

Array.from(deleteLinks).forEach(link => {
    link.addEventListener('click', function(event) {
        if (!confirm(`sure u want to delete ${this.title}`)) {
            event.preventDefault();
        }
    });
});

上面的示例使用了 ES2015 标准中的 Array.from模板字符串


我尝试过但没成功。我做了一个for循环,删除链接[i]。addEventListener('click', function(event) { event.preventDefault(); var choice = confirm("确定要删除吗?"); if (choice) { return true; } }); - user1246950
请看一下我设置好的演示。 - dfsq
1
我猜第一次做的时候我做错了同样的事情,谢谢你 :)) - user1246950
3
另一个不错的技巧是使用扩展运算符,其适用于类似数组的对象。因此,您可以使用[...deleteLinks]代替Array.from(deleteLinks)。我认为Array.from更清晰,但这只是一个值得指出的有趣事情。 - Saad
为什么不直接使用 deleteLinks.forEach 而是要用 Array.from(deleteLinks).forEach - Olivier Boissé
1
由于forEach是Array的一个方法,而当这个回答被写出来时,NodeList并没有实现它。即使现在,在像IE 11之类的旧浏览器中也无法正常工作。 - dfsq

69

使用 querySelectorAll 和一个 for 循环的问题在于它会为数组中的 每个 元素创建一个全新的事件处理程序。

有时这正是您想要的。但是如果有许多元素,则创建单个事件处理程序并将其附加到容器元素可能更有效。然后,您可以使用 event.target 引用触发事件的特定元素:

document.body.addEventListener("click", function (event) {
  if (event.target.classList.contains("delete")) {
    var title = event.target.getAttribute("title");

    if (!confirm("sure u want to delete " + title)) {
      event.preventDefault();
    }
  }
});

在这个例子中,我们只创建了一个事件处理器,并将其附加到 body 元素。每当 body 内部的元素被点击时,click 事件会冒泡到我们的事件处理器。


2
最好将点击事件附加到实际的父容器元素上,而不是body。 - Vahid Amiri
Typescript 3.2.4: (event.target as Element).classListTypescript 3.2.4:(event.target as Element).classList - Patrick Koorevaar

49

一个简洁明了的解决方案,使用ES6:

document.querySelectorAll('.input')
      .forEach(input => input.addEventListener('focus', this.onInputFocus));

7

您需要使用 querySelectorAll 来选择所有具有指定类的元素,由于 querySelectorAll 是一个数组,所以您需要迭代它并添加事件处理程序。

var deleteLinks = document.querySelectorAll('.delete');
for (var i = 0; i < deleteLinks.length; i++) {
    deleteLinks[i].addEventListener('click', function (event) {
        event.preventDefault();

        var choice = confirm("sure u want to delete?");
        if (choice) {
            return true;
        }
    });
}

5
这个答案有点旧了(已经有3.5年了),但我需要纠正一下:querySelectorAll不返回一个数组(Array),而是一个叫做NodeList的列表,看起来像是一个数组,但是没有同样的方法。 - NemoStein

3

(ES5) 我使用forEach来遍历querySelectorAll返回的集合,它运行良好:

document.querySelectorAll('your_selector').forEach(item => { /* do the job with item element */ });

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