querySelectorAll()使用classList添加事件监听器

57

我试图添加一个事件监听器但没有结果。我知道JavaScript具有变量声明提升功能,但我相信我已经尝试了除正确解决方案之外的所有方法。

const cbox = document.querySelectorAll(".box");
function doit() {
  for (let i = 0; i < cbox.length; i++){
    cbox[i].classList.add("red");
  }
}
cbox.addEventListener("click", doit, false);

有人能发现我犯的错误吗?


为什么在“doit()”中使用循环来添加“red”类? - Andreas
因为querySelectorAll返回一个对象,所以我必须进行迭代,对吧? - Evan
2
是的,它返回一个类似数组的对象。下一个问题是:为什么你不使用循环来分配事件处理程序? ;) - Andreas
你的意思是在处理程序内部运行 for 循环还是相反?我两种都尝试过了。 - Evan
4
.querySelectorAll() 方法返回的是一个 NodeList,它没有 .addEventListener() 方法。你需要对列表中的每个项目都添加事件,就像你用类名添加事件一样。注意不要改变原文意思,使翻译通俗易懂。 - Andreas
4个回答

82

代码和您提供的链接之间存在一些不同之处。其中没有 doit() 函数。

您已经将addEvenListener 添加到了 cbox.addEventListener("click",..... 中的 NodeList 上,您需要循环遍历该列表并将事件添加到当前元素上:

Try the following:

const cbox = document.querySelectorAll(".box");

 for (let i = 0; i < cbox.length; i++) {
     cbox[i].addEventListener("click", function() {
       cbox[i].classList.toggle("red");
     });
 }
*,
html,
body {
    padding: 0;
    margin: 0;
}

.box {
    width: 10rem;
    height: 10rem;
    background-color: yellowgreen;
    float: left;
    position: relative;
    margin: 0.5rem;
    transition: .5s all;
}

h3 {
    display: block;
    position: absolute;
    top: 50%;
    left: 50%;
    transform: translate(-50%, -50%);
}

.box:not(:first-child) {
    margin-left: 1rem;
}

.red {
    background-color: orangered;
}
<div id="box1" class="box box1">
    <h3>Box 1</h3>
</div>
<div id="box2" class="box box2">
    <h3>Box 2</h3>
</div>
<div id="box3" class="box box3">
    <h3>Box 3</h3>
</div>
<div id="box4" class="box box4">
    <h3>Box 4</h3>
</div>

您还可以使用 Array.prototype.forEach()箭头函数 语法,这将使您用更少的代码实现相同的效果:

let cbox = document.querySelectorAll(".box");
cbox.forEach(box => {
  box.addEventListener('click', () => box.classList.toggle("red"));
});
*,
html,
body {
    padding: 0;
    margin: 0;
}

.box {
    width: 10rem;
    height: 10rem;
    background-color: yellowgreen;
    float: left;
    position: relative;
    margin: 0.5rem;
    transition: .5s all;
}

h3 {
    display: block;
    position: absolute;
    top: 50%;
    left: 50%;
    transform: translate(-50%, -50%);
}

.box:not(:first-child) {
    margin-left: 1rem;
}

.red {
    background-color: orangered;
}
<div id="box1" class="box box1">
    <h3>Box 1</h3>
</div>
<div id="box2" class="box box2">
    <h3>Box 2</h3>
</div>
<div id="box3" class="box box3">
    <h3>Box 3</h3>
</div>
<div id="box4" class="box box4">
    <h3>Box 4</h3>
</div>


2
虽然这段代码可能回答了问题,但提供有关它如何解决问题以及为什么解决问题的额外上下文信息将会提高答案的长期价值。 - Andreas
2
.querySelectorAll() doesn't return a HTMLCollection but a NodeList - Andreas
所以如果它是某种列表,这意味着我们也可以在元素cbox上使用foreach循环。 - Evan
1
@EvangelosK.,是的,你说得对。你必须使用循环将事件监听器附加到每个元素.....谢谢。 - Mamun
你把我的答案复制到你的答案里,这真是太好了。也许值得一读:https://meta.stackoverflow.com/questions/362656/including-other-peoples-answer-into-your-own-accepted-answer - leonheess
显示剩余2条评论

46

ES6使这变得更加简单:

document.querySelectorAll(".box").forEach(box => 
  box.addEventListener("click", () => box.classList.toggle("red"))
)

示例实现:

document.querySelectorAll(".box").forEach(box =>
  box.addEventListener("click", () => box.classList.toggle("red"))
)
.box {
  width: 5rem;
  height: 5rem;
  background-color: yellowgreen;
  display: inline-block;
}

.box.red {
  background-color: firebrick;
}
<div class="box"></div>
<div class="box"></div>
<div class="box"></div>


2
这是一个使用ES6符号更加简洁的解决方案。 - William

14
你可以在类上使用 forEach 或者使用 事件委托
const cboxes = document.querySelectorAll(".box");
function doit() {
    ... do something ...
}
cboxes.forEach(
  function(cbox) {
   cbox.addEventListener("click", doit,false);
  }
);

注意,我更改了你的变量名。

事件委托

HTML:

<div id="parent">
  <div id="box1" class="box box1">
    <h3>Box 1</h3>
  </div>
  <div id="box2" class="box box2">
      <h3>Box 2</h3>
  </div>
  <div id="box3" class="box box3">
      <h3>Box 3</h3>
  </div>
  <div id="box4" class="box box4">
      <h3>Box 4</h3>
  </div>
</div>

JS部分:

const parent = document.querySelector("#parent");

parent.addEventListener('click', (e) => {
  e.target.classList.add('red');
  console.log(e.target);
});

事件委托更好,使用更少的资源,因为您只需使用1个事件监听器而无需使用for循环。


1
cboxes.forEach() 在IE浏览器中无法使用:https://developer.mozilla.org/zh-CN/docs/Web/API/NodeList/forEach#Browser_Compatibility - Andreas
2
Array.prototype.forEach !== NodeList.prototype.forEach - Andreas
在 <= IE11 中使 forEach 工作的简单 polyfill 修复方法是: NodeList.prototype.forEach = Array.prototype.forEach; - Felix Geenen
你可以像这样使用数组扩展运算符:[...document.querySelectorAll(".box")] - Jason Sperske
parent.addEventListener('click', (e) => { const tgt = e.target; if (tgt.classList.contains('box')) tgt.classList.add('red'); console.log(tgt.id); }); - mplungjan

5

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