购物车——js小项目实例
- 首先构思好整体样式
- 添加点击事件
- 🍁事件委托
- 🍁接下来就是各种函数的书写
- 最后附上完整js代码
短短几周,js阶段的学习已然结束,结课作业就是实现一个购物车功能
功能:
1、可以添加商品
2、全选按钮
3、合计
4、数量增减
以上。
先看看成品图
🍁自在飞花轻似梦,无边丝雨细如愁。 —— 浣溪沙·漠漠轻寒上小楼🍁
首先构思好整体样式
HTML
<!-- 作业专用html框架 -->
<!DOCTYPE html>
<html lang="en"><head><meta charset="UTF-8"><meta http-equiv="X-UA-Compatible" content="IE=edge"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>购物车</title><link rel="stylesheet" href="./css/reset.css"><link rel="stylesheet" href="./css/index.css"><link rel="shortcut icon" href="/icon/icon.png" type="image/x-icon">
</head><body><div id="app"><div class="framework"><header><nav><ul class="headbar"><li><span>/</span><a href="#">我的购物车</a>/</li></ul></nav></header><main><div class="container"><div class="title"><span id="realname">商品名称:</span><input type="text" id="productName" value=""><span>商品价格:</span><!-- 正则表达式,只能输入数字,且为正值 --><input type="text" id="productPrice" pattern="[0-9]*"><button id="cartBtn">加入购物车</button></div><div class="content"><ul><li class="listTitle"><input type="checkbox" class="checkbox" id="allCombo"><!-- 全选按钮,让其脱离文档流 --><span class="float">全选按钮</span><span>商品名称</span><span>商品价格</span><span>数量</span><span>操作</span></li></ul><ul id="contentBox"></ul><!-- 添加 --></div><div class="info"><span class="allProducts"></span><button id="buttonClear">清空购物车</button></div></div></main><footer></footer></div></div>
</body>
<script src="./js/index.js"></script></html>
CSS
body {background-color: rgb(41, 42, 45);
}header {width: 100%;height: 50px;background-color: rgb(240, 240, 240);position: fixed;top: 0;
}
.headbar{float: right;height: inherit;line-height: 50px;margin-right: 40px;
}
.headbar li a{font-size: 14px;color: rgb(54,54,55);margin: 0 20px;
}
main {margin: 0 auto;width: 1200px;min-height: 100vh;background-color: rgb(234, 67, 53);padding: 120px 0;
}.title {background-color: rgba(255,255,255,.85);margin: 0 20px 25px;padding: 15px 10px;font-size: 20px;line-height: 45px;border-radius: 10px;
}.title input {width: 300px;height: 45px;vertical-align: middle;
}.title button {width: 180px;height: 40px;border: none;font-size: 18px;vertical-align: middle;background-color: rgb(221, 221, 221);
}.content,
.info {background-color: #fff;margin: 0 20px 15px;padding: 15px 10px;font-size: 20px;position: relative;border-radius: 10px;background-color: rgba(255,255,255,.85);
}.info {position: fixed;bottom: 20px;width: 1155px;
}.float {position: absolute;left: 150px;
}.content li {display: grid;grid-template-columns: 1fr 1fr 1fr 1fr 1fr;vertical-align: middle;border-bottom: 1px solid rgb(204, 204, 204);padding: 15px 0;line-height: 33px;
}li input,
#checkbox {height: 14px;margin: auto 0;cursor: pointer;}#del {cursor: pointer;
}li span {text-align: center;
}.singleNum {outline: none;width: 80px;height: 25px;vertical-align: middle;margin: 0 5px;
}#minus,
#plus {width: 20px;height: 20px;font-size: 18px;border: none;background-color: inherit;cursor: pointer;
}.info button {border: none;font-size: 20px;background-color: inherit;
}input:invalid {animation: floa .4s;
}@keyframes floa {0% {transform: translateX(0px);background-color: inherit;}20% {transform: translateX(20px);background-color: red;}40% {transform: translateX(0px);background-color: red;}60% {transform: translateX(-20px);background-color: red;}80% {transform: translateX(0px);background-color: red;}100% {transform: translateX(0px);background-color: inherit;}
}
这样一个静态页面就算完成了
接下来,小生要进行页面逻辑的编写了
新建一个类
class cart {}
var cartObj = new cart();
在类中添加数据,事件函数,功能函数,变量合集
class cart {goods = [];
}
//将原型引用到实例
var cartObj = new cart();
小生先在Goods中添加一些数据,方便测试开大
class cart {goods = [{id:1,realname:'iPhone14 Pro Max',price:9999,num:1,checked:false},{id:2,realname:'iPhone14 Pro',price:8999,num:1,checked:false},{id:3,realname:'iPhone14 Max',price:6799,num:1,checked:false},{id:4,realname:'iPhone14 Pro Max',price:5999,num:1,checked:false},];
}
var cartObj = new cart();
然后是将数据渲染到页面里,这里小生是遍历一遍数据,然后用字符串拼接,将数据加到页面中
// 渲染到页面render() {var str = ''for (let i = 0; i < this.goods.length; i++) {str = str + '<li id="' + this.goods[i].id + '">';str = str + '<input type="checkbox" name="" id="checkbox" ' + (this.goods[i].checked ? 'checked' : '') + '>';str = str + '<span>' + this.goods[i].realname + '</span>';str = str + '<span><em id="singlePrice">' + this.goods[i].price + '</em> 元</span>';str = str + '<span><button id="minus">-</button><input class="singleNum" readonly="" id="singleNum" type="text" value="' + this.goods[i].num + '"><button id="plus">+</button></span>';str = str + '<span ' + 'id="del" ' + '>删除</span>';str = str + '</li>';}var str2 = ''str2 = str2 + '共计' + this.inTotal() + '种商品,合计' + this.inTotalSum() + '元。';this.contentBox.innerHTML = strthis.allProducts.innerHTML = str2localStorage.setItem('goods', JSON.stringify(this.goods));}
接下来就是实现一些基础功能
添加点击事件
🍁事件委托
对于“对于事件处理程序过多”,问题的解决方案就是事件委托。事件委托利用了事件冒泡,只指定一个事件处理程序,就可以管理某一类型的事件。
使用事件委托,只需要在DOM树中尽量最高的层次上添加一个事件处理程序
例如接下来的点击事件,有增减数量按钮,删除按钮,选中按钮,全选按钮
小生习惯将事件函数都归类在一起。
首先建立一个事件函数
addevent() {//用来添加点击事件};
给购物车按钮添加点击事件
要注意this的指向,弄明白闭包的思想
addevent() {var _this = this;//闭包//购物车按钮添加点击事件this.cartBtn.addEventListener('click', function () {if (!isNaN(_this.productPrice.value) && _this.productPrice.value > 0) {_this.add()//执行添加函数}});};
添加全选按钮事件
addevent() {//其他代码// 全选事件this.allCombo.addEventListener('click', function (e) {_this.selectAll();});};
接下来是内容区的点击事件,由于比较多,就可以用委派的思想
通过 e.target.id,再配合switch,很方便的实现委派思想
addevent() {//其他代码this.contentBox.addEventListener('click', function (e) {switch (e.target.id) {// 单击删除按钮case 'del':console.log('del', e.target.parentNode.id)_this.del(e.target.parentNode.id);break;//全选对应全选case 'checkbox':// console.log('dian',e.target.parentNode.id);_this.exchangeCheckbox(e.target.parentNode.id);break;//减case 'minus':console.log('dian', e.target.parentNode.parentNode.id);_this.decrease(e.target.parentNode.parentNode.id);break;case 'plus':console.log('dian', e.target);_this.increase(e.target.parentNode.parentNode.id);break;// case 'singleNum':// console.log(e.target.onblur)// break;default:break;}});};
🍁接下来就是各种函数的书写
添加数据
//添加数据add() {var cache = {id: new Date().getTime() + String(Math.floor(Math.random() * 10)),realname: this.productName.value,price: this.productPrice.value,num: 1,checked: this.allCombo.checked,}this.goods.unshift(cache);this.render();}
删除数据函数
//删除函数del(id) {for (let i = 0; i < this.goods.length; i++) {if (id == this.goods[i].id) {this.goods.splice(i, 1)}}this.render();};
清空购物车函数
// 清空函数clear() {this.goods = [];this.allCombo.checked = false;this.render();};
全选按钮函数
//全选函数selectAll() {for (let i = 0; i < this.goods.length; i++) {this.goods[i].checked = allCombo.checked;}this.render();};//对应函数exchangeCheckbox(id) {for (let i = 0; i < this.goods.length; i++) {if (id == this.goods[i].id) {this.goods[i].checked = !this.goods[i].checked;}}let flag = true;for (let j = 0; j < this.goods.length; j++) {if (!this.goods[j].checked) {flag = false;}this.allCombo.checked = flag;this.render();}};
增减数量按钮
// 减decrease(index) {for (let i = 0; i < this.goods.length; i++) {if (index == this.goods[i].id) {if (this.goods[i].num > 1) {this.goods[i].num--;}console.log(this.goods[i].num);}}this.render();};increase(index) {for (let i = 0; i < this.goods.length; i++) {if (index == this.goods[i].id) {this.goods[i].num++;console.log(this.goods[i].num);}}this.render();};
计算合计
// 计算价格inTotal() {var num = 0;for (let i = 0; i < this.goods.length; i++) {if (this.goods[i].checked) {num++;}}return num;};inTotalSum() {var sum = 0var sumAll = 0;for (let i = 0; i < this.goods.length; i++) {if (this.goods[i].checked) {sum = this.goods[i].price * this.goods[i].num;}sumAll += sum;}return sumAll;};
这样大部分业务逻辑就算完成了
还有些细节需要完成
添加构造函数,将数据存储在本地
class cart {//商品数据代码//构造函数constructor() {if (localStorage.getItem('goods')) {this.goods = JSON.parse(localStorage.getItem('goods'));}this.render(); //渲染this.addevent();//事件函数在运行}//事件代码//函数代码
}
最后附上完整js代码
class cart {//建立一个数组goods = [];//构造函数constructor() {if (localStorage.getItem('goods')) {this.goods = JSON.parse(localStorage.getItem('goods'));}this.render(); //渲染this.addevent();//事件函数在运行}cartBtn = document.getElementById('cartBtn');//获取加入购物车的按钮productName = document.getElementById('productName');//获取页面中商品名称productPrice = document.getElementById('productPrice');//获取页面中商品价格contentBox = document.getElementById('contentBox');//获取页面中内容的盒子buttonClear = document.getElementById('buttonClear');// 获取页面中清空购物车的盒子allProducts = document.querySelector('.allProducts')//获取页面中的统计信息allCombo = document.getElementById('allCombo');// 全选按钮//事件归类addevent() {var _this = this;//闭包//购物车按钮添加点击事件this.cartBtn.addEventListener('click', function () {if (!isNaN(_this.productPrice.value) && _this.productPrice.value > 0) {_this.add()//执行添加函数}});// 全选事件this.allCombo.addEventListener('click', function (e) {// console.log(e.target)_this.selectAll();});this.contentBox.addEventListener('click', function (e) {switch (e.target.id) {// 单击删除按钮case 'del':console.log('del', e.target.parentNode.id)_this.del(e.target.parentNode.id);break;//全选对应全选case 'checkbox':// console.log('dian',e.target.parentNode.id);_this.exchangeCheckbox(e.target.parentNode.id);break;//减case 'minus':console.log('dian', e.target.parentNode.parentNode.id);_this.decrease(e.target.parentNode.parentNode.id);break;case 'plus':console.log('dian', e.target);_this.increase(e.target.parentNode.parentNode.id);break;// case 'singleNum':// console.log(e.target.onblur)// break;default:break;}});//点击清空购物车this.buttonClear.addEventListener('click', function () {_this.clear();});};//添加数据add() {var cache = {id: new Date().getTime() + String(Math.floor(Math.random() * 10)),realname: this.productName.value,price: this.productPrice.value,num: 1,checked: this.allCombo.checked,}this.goods.unshift(cache);this.render();}//删除函数del(id) {for (let i = 0; i < this.goods.length; i++) {if (id == this.goods[i].id) {this.goods.splice(i, 1)}}this.render();};// 清空函数clear() {this.goods = [];this.allCombo.checked = false;this.render();};//全选函数selectAll() {for (let i = 0; i < this.goods.length; i++) {this.goods[i].checked = allCombo.checked;}this.render();};//对应函数exchangeCheckbox(id) {for (let i = 0; i < this.goods.length; i++) {if (id == this.goods[i].id) {this.goods[i].checked = !this.goods[i].checked;}}let flag = true;for (let j = 0; j < this.goods.length; j++) {if (!this.goods[j].checked) {flag = false;}this.allCombo.checked = flag;this.render();}};// 减decrease(index) {for (let i = 0; i < this.goods.length; i++) {if (index == this.goods[i].id) {if (this.goods[i].num > 1) {this.goods[i].num--;}console.log(this.goods[i].num);}}this.render();};increase(index) {for (let i = 0; i < this.goods.length; i++) {if (index == this.goods[i].id) {this.goods[i].num++;console.log(this.goods[i].num);}}this.render();};// 计算价格inTotal() {var num = 0;for (let i = 0; i < this.goods.length; i++) {if (this.goods[i].checked) {num++;}}return num;};inTotalSum() {var sum = 0var sumAll = 0;for (let i = 0; i < this.goods.length; i++) {if (this.goods[i].checked) {sum = this.goods[i].price * this.goods[i].num;}sumAll += sum;}return sumAll;};// 渲染到页面render() {var str = ''for (let i = 0; i < this.goods.length; i++) {str = str + '<li id="' + this.goods[i].id + '">';str = str + '<input type="checkbox" name="" id="checkbox" ' + (this.goods[i].checked ? 'checked' : '') + '>';str = str + '<span>' + this.goods[i].realname + '</span>';str = str + '<span><em id="singlePrice">' + this.goods[i].price + '</em> 元</span>';str = str + '<span><button id="minus">-</button><input class="singleNum" readonly="" id="singleNum" type="text" value="' + this.goods[i].num + '"><button id="plus">+</button></span>';str = str + '<span ' + 'id="del" ' + '>删除</span>';str = str + '</li>';}var str2 = ''str2 = str2 + '共计' + this.inTotal() + '种商品,合计' + this.inTotalSum() + '元。';this.contentBox.innerHTML = strthis.allProducts.innerHTML = str2localStorage.setItem('goods', JSON.stringify(this.goods));}
}var cartObj = new cart();