JavaScript 购物车案例
以课堂案例为基础,更新以下内容
商品列表 DOM 结构由脚本动态生成
修复课堂案例中点击复选框后再改变数量时数据不准确的问题
脚本动态生成 DOM 结构
// (一) 从购物车模块中获取数据
import carts from './carts.js'
// (二) 根据 carts.data 生成对应的商品列表(<tr>),同时将商品数据渲染到页面
const cartsBody = document.querySelector('.carts-body')
carts.data.forEach((item) => {
// 创建临时 elemFlag
const elemFlag = document.createDocumentFragment()
// 每条数据创建一个<tr>元素
const trElem = document.createElement('tr')
trElem.classList.add('item')
// 为<tr>创建对应的单元格<td>
// 第一个<td>: checkbox
// <td><input type="checkbox" name="" class="check" checked /></td>
const tdElem = document.createElement('td')
const checkElem = document.createElement('input')
checkElem.type = 'checkbox'
checkElem.name = ''
checkElem.classList.add('check')
checkElem.checked = true
tdElem.append(checkElem)
trElem.append(tdElem)
// 将当前data数据放置到对应的单元格<td>
for (let key in item) {
const tdElem = document.createElement('td')
if (key === 'num') {
// <td><input type="number" name="" class="num" value="1" min="1" /></td>
const numInput = document.createElement('input')
numInput.type = 'number'
numInput.name = ''
numInput.classList.add(key)
numInput.value = item[key]
numInput.min = '1'
tdElem.append(numInput)
trElem.append(tdElem)
} else {
tdElem.classList.add(key)
tdElem.append(item[key])
trElem.append(tdElem)
}
}
elemFlag.append(trElem)
cartsBody.append(elemFlag)
})
// (三) 将总数量、总金额渲染到页面中
// 1. 总数量 carts.total
const total = document.querySelector('.carts-foot .total')
total.textContent = carts.total
// 2. 总金额 carts.totalMoney
const totalMoney = document.querySelector('.carts-foot .total-money')
totalMoney.textContent = carts.totalMoney
修复课堂案例中点击复选框后再改变数量时数据不准确的问题
思路:
- 将获取的数量(NodeList)、金额(NodeList)转换为数组,得到数量数组(numsArr)和 金额数组(moniesArr)
- 在复选框事件中动态更新数量数组(numsArr)和 金额数组(moniesArr)
- 取消选择:从数组中删除对应的商品,但数组中对应的元素位置保留,重新选择时可用赋值添加
- delete numsArr[index]
- delete moniesArr[index]
- 选择:
- numsArr[index] = curNum
- moniesArr[index] = curMoney
- 取消选择:从数组中删除对应的商品,但数组中对应的元素位置保留,重新选择时可用赋值添加
- 所有事件中,都通过动态更新的数量数组(numsArr)和 金额数组(moniesArr)来计算总数量和总金额( calc.js 模块中定义的计算函数)
代码
html文件中的脚本
import * as calc from './calc.js'
// (四) 获取购物车商品的复选框、数量、单价、金额构成的 NodeList
// 单价(NodeList),用于数量改变时计算金额
const prices = document.querySelectorAll('.carts-body .price')
// 数量(NodeList => Array)
const nums = document.querySelectorAll('.carts-body .num')
let numsArr = [...nums]
// 金额数组(NodeList => Array)
const monies = document.querySelectorAll('.carts-body .money')
let moniesArr = [...monies]
// check数组(NodeList),用于判断是否选中
const checks = document.querySelectorAll('.carts-body .check')
// (五) 为数量控件添加 change 事件
nums.forEach(function (num, index) {
num.onchange = function () {
// 1. 计算当前商品金额 = 数量 * 单价
// 未选择时也可用更改数量及对应的金额,只是不计入汇总
monies[index].textContent = (num.value * prices[index].textContent).toString()
// 2. 计算总数量并渲染到页面
total.textContent = calc.calcTotalNum(numsArr)
// 3. 计算总金额并渲染到页面
totalMoney.textContent = calc.calcTotalMoney(moniesArr)
}
})
// (六) 复选框
// 全选按钮
const checkAll = document.querySelector('.check-all')
// 为全选添加change
checkAll.onchange = function () {
// 遍历所有商品的状态,并将当前全选按钮的状态赋值给它
checks.forEach(check => check.checked = checkAll.checked)
// 根据所有商品的全选/全不选状态,更新 数量数组 numsArr 和 金额数组 moniesArr
// 全不选
if (false === this.checked) {
// 删除数量数组中的所有数据,重新计算总数量并渲染到页面
for (let i = 0; i < numsArr.length; i++) {
delete numsArr[i]
}
total.textContent = calc.calcTotalNum(numsArr)
// 删除金额数组中的所有数据,重新计算总数量并渲染到页面
for (let i = 0; i < moniesArr.length; i++) {
delete moniesArr[i]
}
totalMoney.textContent = calc.calcTotalMoney(moniesArr)
} else {
// 全选
// 从 NodeList 中重新获取数量数组,重新计算总数量并渲染到页面
numsArr = [...nums]
total.textContent = calc.calcTotalNum(numsArr)
// 从 NodeList 中重新获取金额数组,重新计算总金额并渲染到页面
moniesArr = [...monies]
totalMoney.textContent = calc.calcTotalMoney(moniesArr)
}
}
// 遍历每个商品的复选框,并添加change,动态的计算相关数据
checks.forEach(function (check, index) {
check.onchange = function () {
// 根据所有商品的复选框的状态,来动态的设置全选
checkAll.checked = [...checks].every(check => check.checked)
// 如果商品未选中,从数量数组和金额数组中减去相应的数据,并重新计算总数量和总金额
// 如果商品选中,从数量数组和金额数组中添加相应的数据,并重新计算总数量和总金额
if (false === check.checked) {
// 将当前数量对应的<input>元素,从数量数组中删除,重新计算总数量并渲染到页面
delete numsArr[index]
total.textContent = calc.calcTotalNum(numsArr)
// 将当前金额对应的<td>元素,从金额数组中删除,重新计算总金额并渲染到页面
delete moniesArr[index]
totalMoney.textContent = calc.calcTotalMoney(moniesArr)
} else {
// 获取当前商品对应的行元素<tr>
const curTr = this.parentElement.parentElement
// 获取当前商品金额对应的单元格元素<td>
const curMoney = curTr.lastElementChild
// 获取当前商品数量对应的<input>元素
const curNum = curMoney.previousElementSibling.firstElementChild
// 将当前数量对应的<input>元素,添加到数量数组,重新计算总数量并渲染到页面
numsArr[index] = curNum
total.textContent = calc.calcTotalNum(numsArr)
// 将当前金额对应的<td>元素,添加到金额数组,重新计算总金额并渲染到页面
moniesArr[index] = curMoney
totalMoney.textContent = calc.calcTotalMoney(moniesArr)
}
}
calc.js 模块
export function calcTotalNum(arr)
{
let tempTotal = 0
arr.forEach((item) => tempTotal += parseInt(item.value))
return tempTotal
}
export function calcTotalMoney(arr) {
let tempTotalMoney = 0
arr.forEach((item) => tempTotalMoney += parseInt(item.textContent))
return tempTotalMoney
}