购物车——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 = str
this.allProducts.innerHTML = str2
localStorage.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 = 0
var 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 = 0
var 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 = str
this.allProducts.innerHTML = str2
localStorage.setItem('goods', JSON.stringify(this.goods));
}
}
var cartObj = new cart();
本文含有隐藏内容,请 开通VIP 后查看