批改状态:合格
老师批语:
购物车要实现的功能
全选框选中时,底下的单个商品复选框按钮要全部选中
单个商品复选框中只要有一个没有被选中,则全选复选框取消选中
改变商品数量的时候,后边的金额会自动进行加减
总价和总数量中只计算当前勾选的商品,但是没有勾选的商品中数量和金额可以变动
整个购物车的简陋样子如下:

先完成功能 1,2(功能 1,2 后边会和 3,4 进行一下合并)
首先要做的就是拿到全选复选框和全部的单选复选框,然后对复选框添加 “change” 事件。完成 1,2 功能
// 1.获取全选复选框,所有单品的复选框const checkAll = document.querySelector("#check-all");// console.log(checkAll);const checkItems = document.querySelectorAll('input[name="item"]');// console.log(checkItems);// 2.为全选复选框按钮添加事件:change,当前值改变会触发事件checkAll.onchange = (ev) => {// console.log(ev.target.checked);// 当全选复选框是checked时,对每个单品复选框都要进行checked选中checkItems.forEach((item) => (item.checked = ev.target.checked));};// 为每个单独的复选框添加事件:changecheckItems.forEach(// 对单个复选框状态进行判断,如果有一个不为checked状态,则全选复选框不选中(item) =>(item.onchange = () =>(checkAll.checked = [...checkItems].every((item) => item.checked))));// 使用jQuery完成上边的内容,逻辑思路和原生js相同$("#check-all").on("change", (ev) => {// 对全选复选框添加change事件// 当全选复选框的值改变时,对所有的单品复选框进行同步更新它的选中情况$('input[name="item"]').each(function () {// change事件发生时,对每个单品的选中情况进行同步this.checked = ev.target.checked;});});$('input[name="item"]').each(function () {// 对每个单品的复选框添加change事件// 对这个单品复选框集合的checked情况进行every()筛选,// 当所有的单品复选框都选中时,匹配全选复选框this.onchange = function () {// console.log($("#check-all")[0].checked);// console.log([...$('input[name="item"]')]);$("#check-all")[0].checked = [...$('input[name="item"]')].every((item) => item.checked);};});
效果如图

接下来完成功能 3,4,将功能 3,4 涉及到的内容写到一个函数中,因为 3,4 中的功能涉及到功能 1,2,所以后边还需要做一次整合
// 自动计算函数function autoCalculate() {// 对后边数量以及单价,总计进行自动计算// 需要获取到所有的数量,以及所有的单价// 因为拿到的数据都是string,所以需要想办法转换成数值// const prices = document.querySelectorAll(".price");// 因为数据进行运算的时候是会发生类型转换的,所以可以直接进行转换它// 也可以使用parseInt()方法,将字符串转为数值// 获取到当前的单价数组,为后面的计算金额const priceArr = [...document.querySelectorAll(".price")].map((item) => item.textContent * 1);// console.log(priceArr);// 获取到被选中事件的索引值// const checkedArr = [];// [...checkItems].forEach((ev, index) => {// if (ev.checked === true) {// checkedArr.push(index);// }// });// console.log(checkedArr);// const numbers = document.querySelectorAll('input[type="number"]');// console.log(numbers[0].value);// 对商品的复选框状态进行判断,从而获取商品的总数和总金额// 对当前的数量数组进行获取const numArr = [...document.querySelectorAll('input[type="number"]')].map((item) => item.value * 1);// console.log(numArr);// 使用filter()方法,以当前价格的复选框状态作为判断条件,获取到所有的checked为true的单价const checkedPrice = priceArr.filter((item, index) => [...checkItems][index].checked);// console.log(totalPrice);// 获取到所有checked为true的数量const checkedNum = numArr.filter((item, index) => [...checkItems][index].checked);// console.log(totalNum);// 商品总数的获取,可以使用reduce()方法进行累加操作// console.log(numArr.reduce((pre, cur) => pre + cur));// 当reduce()中的参数值为空时,reduce()方法会报错,所以需要进行判断let sum = 0;if (checkedNum.length !== 0) {sum = checkedNum.reduce((pre, cur) => pre + cur);}// 计算商品的金额:单价 * 数量,还是使用reduce()方法进行计算// 商品的金额是不随复选框的变动而变动的,它只和数量的变动相关const amountArr = [priceArr, numArr].reduce((total, curr) =>total.map((item, index) => item * curr[index]));// 计算已选中商品的金额数组const checkedAmount = amountArr.filter((item, index) => [...checkItems][index].checked);// console.log(checkedAmount);// console.log(amount);// 计算已选中商品的总金额let totalAmount = 0;if (checkedAmount.length !== 0) {totalAmount = checkedAmount.reduce((pre, cur) => pre + cur);}// console.log(totalAmount);// 将计算结果渲染到购物车中// 总数量document.querySelector("#sum").textContent = sum;// 总金额document.querySelector("#total-amount").textContent = totalAmount;// 每个商品的金额// 根据当前商品的索引和amountArr商品价格数组的索引对应,然后填充到内容中document.querySelectorAll(".amount").forEach((item, index) => (item.textContent = amountArr[index]));}// 获取到所有的数量控件const numInput = document.querySelectorAll('input[type="number"]');// console.log(numInput);// 用户更新数量时触发自动计算,添加change事件numInput.forEach((ev) => (ev.onchange = autoCalculate));// 刚加载时,也应该触发自动计算事件window.onload = autoCalculate;// 在这块儿对前边的复选框功能做了一下整合,所以只需要这一段代码就好,前边的可以注释掉// 当取消单个选中时,触发计算事件checkItems.forEach((item) =>item.addEventListener("change", function () {checkAll.checked = [...checkItems].every((item) => item.checked);autoCalculate();}));// 当全选复选框值发生变动时,自动计算被触发checkAll.addEventListener("change", function (ev) {checkItems.forEach((item) => (item.checked = ev.target.checked));autoCalculate();});
因为要完成功能 3,4,涉及到功能 1,2 中的复选框的 change 事件,所以进行了整合,前边功能 1,2 的代码可以注释掉
完成的效果图如下

首先要知道什么是模块?模块就是一个 js 代码块。一个封装成模块的 js 文件,内部成员对外不见,除非导出来。模块要写到一个独立 的 js 文件中,并使用一些特别的语法和关键字
其次是模块解决了什么问题?
可维护性: 每个模块是独立的,各写各个互不影响,出错直接定位责任人
可复用性: 只需要一条 import 指令就可以导入
避免污染全局空间: 模块处在自己的命名空间内
模块解决了 js 的模块化开发与代码封装问题
模块的导入
在 ES6 之前的模块导入方式是利用 script 标签的 src 属性
<script src="module1.js"></script>
ES6 之后的模块导入方式
<!-- 导入模块时,必须让type类型为module --><script type="module">// 导入语句,import// 前面的./不能省略import { userName, hello, User } from "./module1.js";</script>
在模块中将想要对外部访问的成员前边加上export关键字,就可以在需要导入的地方使用 import 语句进行导入,除了单个的成员前边加上export关键字之外,还可以对需要导出的所有成员进行统一导出
// 导出语句 export// export let userName = "小红";let userName = "小红";function hello(name) {return "Hello" + name;}class User {constructor(name, age) {this.name = name;this.age = age;}show() {return this.name + "今年" + this.age + "了";}}// 模块中的私有成员,不能被外部访问let gender = "男";// 统一导出export { userName, hello, User };
导入模块之后就可以去使用模块中定义的成员了。
模块中的别名导出导入
为什么要进行别名导入呢?因为有时候会在当前作用域中定义和模块中同名的变量,别名的导入导出是为了不与当前作用域的变量重名。
模块中成员的导出过程
// 在统一导出的时候进行别名化export { userName as name, hello as hi, User };
脚本中对于模块的导入过程(如果导入的成员在模块中已经进行了别名化处理,那么在使用 import 语句导入的时候,就必须使用别名导入,不然无效)
<script type="module">// import语句不允许写在后边// import { userName, hello } from "./module2.js";// 如果模块中定义了别名,再导入的时候就只能使用别名导入// import { userName, echo } from "./module2.js";// import { name, echo } from "./module2.js";// console.log(name);// let userName;// 别名导入import { name as firstName, echo } from "./module2.js";// 别名的导入导出是为了不与当前作用域的变量重名</script>
在对别名的导入过程中,如果当前作用域还是有同名成员时,还可以进行别名处理
模块中默认成员的导入导出
模块中默认成员与普通成员的区别:
// default可以视为一个变量,default = userName;相当于赋值// 默认成员export default userName = "小红";// 一个模块中只允许一个默认导出// 一般成员export function hello(name) {return "Hello" + name;}
// 将成员导出,将hello视为默认成员导出,其他成员为普通成员export { userName, hello as default, gender };
<script type="module">// 默认模块导入// 导入默认成员与普通模块的最大区别在于没有大括号// 导入既有默认成员,又有非默认成员import hello, { userName, gender } from "./module3.js";</script>
模块命名空间的作用
命名空间:是一个容器,内部可以包含任何类型的数据,命名空间是一个对象,可以挂载到当前的全局中。
使用一般的导入模块方式和使用命名空间导入模块的方式
一般方式
<script type="module">// 使用一般方式导入import { userName, hello, User } from "./module1.js";</script>
使用命名空间的方式导入
<script type="module">// 使用命名空间来导入import * as nameSpace from "./module1.js";// 可以在当前作用域随便声明变量,不怕与模块中成员同名,因为命名空间是一个对象let userName;let hello = () => {};class User {}// 查看/使用模块中的成员console.log(nameSpace);console.log(nameSpace.userName);console.log(nameSpace.hello(nameSpace.userName));console.log(new nameSpace.User("小兰", 18).show());</script>
Copyright 2014-2025 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号