在开发购物车页面时,我们常会给每个商品行加上数量和加减按钮。但不少初学者写完后发现,只有第一个商品的按钮能点,其余商品点击毫无反应。这个问题根源在于选择器和事件绑定的方式不对。

问题产生的原因
最常见的原因是使用 document.getElementById() 或 document.querySelector() 获取按钮。这两个方法都只返回匹配的第一个元素。如果你把事件直接绑在这个元素上,自然只有第一个商品生效。
<div class="cart-item"> <button class="minus">-</button> <span class="num">1</span> <button class="plus">+</button> </div> <div class="cart-item"> <button class="minus">-</button> <span class="num">1</span> <button class="plus">+</button> </div>
若脚本写成下面这样,就只绑定了第一个 minus 和 plus:
// 错误示例:只拿到第一个元素
var minus = document.querySelector('.minus');
var plus = document.querySelector('.plus');
minus.onclick = function() { /* 只影响第一个 */ };
plus.onclick = function() { /* 只影响第一个 */ };
解决方案一:循环绑定所有按钮
使用 querySelectorAll 获取全部按钮,再遍历每个元素单独绑定事件。
// 正确示例:循环绑定
var items = document.querySelectorAll('.cart-item');
items.forEach(function(item) {
var minus = item.querySelector('.minus');
var plus = item.querySelector('.plus');
var num = item.querySelector('.num');
minus.onclick = function() {
var n = parseInt(num.textContent);
if (n > 1) num.textContent = n - 1;
};
plus.onclick = function() {
var n = parseInt(num.textContent);
num.textContent = n + 1;
};
});
解决方案二:事件委托
把点击事件统一绑到父容器,通过 event.target 判断点到哪个按钮。这样即使后面动态添加商品也不用重新绑事件。
// 正确示例:事件委托
var cart = document.getElementById('cart');
cart.addEventListener('click', function(e) {
var btn = e.target;
if (btn.classList.contains('minus') || btn.classList.contains('plus')) {
var item = btn.parentNode;
var num = item.querySelector('.num');
var n = parseInt(num.textContent);
if (btn.classList.contains('minus') && n > 1) {
num.textContent = n - 1;
} else if (btn.classList.contains('plus')) {
num.textContent = n + 1;
}
}
});
两种方案对比
| 方案 | 优点 | 缺点 |
|---|---|---|
| 循环绑定 | 逻辑直观,易于理解 | 动态添加元素需重新绑定 |
| 事件委托 | 支持动态内容,绑定一次即可 | 需写判断逻辑,稍复杂 |
实际项目中推荐事件委托,尤其当购物车商品由接口动态渲染时,可以避免遗漏新元素的事件绑定。
JavaScript购物车事件绑定修改时间:2026-07-27 23:39:20