本文详细介绍如何使用原生JavaScript实现购物车的核心功能,包括商品总价计算、复选框联动、全选/反选、编辑模式切换及删除操作。通过利用隐藏域存储商品小计,结合遍历选中状态累加金额,实现了无需框架依赖的完整购物车交互逻辑,旨在帮助开发者深入理解DOM操作与事件处理机制。
需求分析与背景
随着Vue、React、Angular等MVVM框架的普及,原本需要复杂DOM操作才能完成的需求变得简洁。这些以数据驱动视图的框架让项目开发更加高效。然而,在享受框架便利的同时,深入理解原生JavaScript对技术提升同样重要。通过手写原生代码实现功能,不仅能巩固基础,也能更深刻地理解框架的设计初衷。接下来,通过效果图(左侧为购物车编辑界面,右侧为列表展示)来明确具体需求。
- 当用户点击单个商品的复选框时,该商品的价格需被累加计入总费用。
- 当用户调整商品数量,且该商品复选框处于选中状态时,总费用需随之更新。
- 点击“全选”按钮时,所有商品的复选框应被选中,且总费用显示为所有选中商品的总价之和。
- 点击“编辑”按钮后,按钮文字切换为“完成”,同时右下角的“去结算”按钮变为红色的“删除”按钮。
- 在编辑模式下,勾选商品复选框后点击删除,对应商品将从列表中移除,总费用重置为0,“完成”文字变回“编辑”,删除按钮恢复为“去结算”。


HTML结构与核心难点
此需求的核心难点在于如何准确汇总各商品总价,并根据复选框状态动态累加至合计费用。首先分析商品的HTML结构。
<div class="cartMain1" data-id=${goods_id}>
<input type="checkbox" class="checkbox" />
<input type="hidden" class="hiddenVal">
<div class="cartMsg">
<img src="${goods_image}" alt="" />
<div class="cartDetail2">
<h3>${goods_name}</h3>
<div class="cartDCont">
<p class="commonMoney">¥<span class="singlePrice">${goods_price_max}</span></p>
<div class="count">
<button class="decrease">-</button>
<input type="text" class="goodsSum" value=${goods_num} />
<button class="increase">+</button>
</div>
</div>
</div>
</div>
</div>解决方案与代码实现
解决方案如下:在每个商品复选框后添加一个隐藏域,用于存储当前商品的总价。当商品数量变动时,将计算后的最终价格赋值给该隐藏域的value属性。随后,通过遍历所有复选框的选中状态,获取对应隐藏域的value值,即可计算出总费用。具体JavaScript实现代码如下:
1、遍历隐藏input的值获取金额
function getHiddenMoney() {
let str = 0;
Array.from(checkboxEl).forEach((item) => {
if (item.checked == true) {
str += +item.nextElementSibling.value;
}
});
document.querySelector(".totalPrice").innerHTML = str.toFixed(2);
}2、点击子复选框将金额赋值给隐藏input
function toHiddenMoney(el) {
const parent = el.parentNode;
//获取单价元素
const singlePrice = +parent.querySelector(".singlePrice").innerHTML;
//获取商品数量
const goodsSum = +parent.querySelector(".goodsSum").value;
//商品总价为
let totalPriceVal = (singlePrice * goodsSum).toFixed(2);
//赋值给hidden input框
el.nextElementSibling.value = totalPriceVal;
getTotalMoney();
}3、点击子复选框给父复选框添加状态
for (let i = 0; i < box.length; i++) {
box[i].addEventListener("click", function () {
checkbox.checked = Array.from(box).every((item) => item.checked);
toHiddenMoney(box[i]);
});
} 4、点击全选复选框=>所有复选框都被选中
const checkbox = document.querySelector("#checkbox");
const box = document.querySelectorAll(".checkbox");
checkbox.addEventListener("click", function () {
for (let i = 0; i < box.length; i++) {
box[i].checked = checkbox.checked;
toHiddenMoney(box[i]);
}
});
5、点击商品数量增减时
let goodCount = document.querySelectorAll(".count");
goodCount.forEach((item) => {
item.addEventListener("click", function (e) {
let target = e.target;
if (target.className == "decrease") {
const inp = target.nextElementSibling;
const hidden =
target.parentNode.parentNode.parentNode.parentNode
.previousElementSibling;
const checkBox =
target.parentNode.parentNode.parentNode.parentNode
.previousElementSibling.previousElementSibling;
const singleVal =
+target.parentNode.previousElementSibling.querySelector(
".singlePrice"
).innerHTML;
if (inp.value == 1) {
return alert("不能再减了~");
} else {
inp.value--;
hidden.value = singleVal * inp.value;
toHiddenMoney(checkBox);
}
}
if (target.className == "increase") {
const inp = target.previousElementSibling;
const hidden =
target.parentNode.parentNode.parentNode.parentNode
.previousElementSibling;
const checkBox =
target.parentNode.parentNode.parentNode.parentNode
.previousElementSibling.previousElementSibling;
const singleVal =
+target.parentNode.previousElementSibling.querySelector(
".singlePrice"
).innerHTML;
inp.value++;
hidden.value = singleVal * inp.value;
toHiddenMoney(checkBox);
}
});
});
const checkboxEl = document.querySelectorAll(".checkbox");6、点击编辑
const edit = document.querySelector(".edit");
let flag = true;
const editHtml = edit.innerHTML;
const account = document.querySelector(".account");
const cancel = document.querySelector(".cancel");
let newCheckbox = [];
edit.addEventListener("click", function () {
let editHtml2 = `<span style="color: red; font-size: 3.7333vw">完成</span>`;
console.log(flag);
if (flag) {
this.innerHTML = editHtml2;
account.style.display = "none";
cancel.style.display = "block";
} else {
this.innerHTML = editHtml;
account.style.display = "block";
cancel.style.display = "none";
}
flag = !flag;
});7、点击删除按钮
let goodsIdArr = [];
cancel.addEventListener("click", function () {
//获取被选中的复选框
newCheckbox = [...checkboxEl].filter((item) => item.checked == true);
newCheckbox.forEach((item) => {
item.parentNode.remove();
goodsIdArr.push(item.parentNode.dataset.id);
});
edit.innerHTML = editHtml;
account.style.display = "block";
cancel.style.display = "none";
document.querySelector(".totalPrice").innerHTML = +0.0;
});
8、封装获取复选框的状态来计算总费用
function getTotalMoney() {
let checkboxNew = document.querySelectorAll(".checkbox");
arr = [];
for (let i = 0; i < checkboxNew.length; i++) {
if (checkboxNew[i].checked == true) {
arr.push(+checkboxNew[i].nextElementSibling.value);
getTotalPrice(arr);
} else {
getTotalPrice(arr);
}
}
}9、计算总价
function getTotalPrice(arr) {
document.querySelector(".totalPrice").innerHTML = arr
.reduce((prev, cur) => prev + cur, 0)
.toFixed(2);
}
以上即为使用原生JavaScript实现购物车总金额计算的完整代码。编写过程中发现原生JS处理此类交互较为繁琐,代码逻辑仍有优化空间,欢迎指正与点评。