s实现淘宝购物车类似功能:
主要有添加商品
增加和减少商品数量
根据增加、减少或选择的商品获取金额
实现商品价格的计算
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>购物车</title>
</head>
<style type="text/css">
h1{
text-align: center;
}
table{
margin: 0 auto;
}
body{
font-size: larger;color: crimson;
background-image: url(img/2.jpg);
background-repeat: no-repeat;
background-size: 100%;
}
table th,table td{
}
</style>
<body >
<h1>购物车:真划算</h1>
<table border="1" >
<tr>
<!--文本th-->
<th>商品</th>
<th >单价</th>
<th>颜色</th>
<th>库存</th>
<th>好评率</th>
<th>操作</th>
</tr>
<tr>
<td>面膜</td>
<td >150</td>
<td>白色</td>
<td>100</td>
<td>88%</td>
<td align="center">
<input type="button" value="加入购物车" onclick="add_shoppingcar(this)"/>
</td>
</tr>
<tr>
<td>口红</td>
<td >350</td>
<td>白色</td>
<td>166</td>
<td>82%</td>
<td align="center">
<input type="button" value="加入购物车" onclick="add_shoppingcar(this)"/>
</td>
</tr>
<tr>
<td>鼠标</td>
<td >150</td>
<td>黑色</td>
<td>99</td>
<td>75%</td>
<td align="center">
<input type="button" value="加入购物车" onclick="add_shoppingcar(this)"/>
</td>
</tr>
<tr>
<td>键盘</td>
<td >120</td>
<td>黑色</td>
<td>50</td>
<td>80%</td>
<td align="center">
<input type="button" value="加入购物车" onclick="add_shoppingcar(this)"/>
</td>
</tr>
</table>
<h1> 购物车</h1>
<table border="1">
<thead>
<tr>
<th>商品</th>
<th >单价</th>
<th>数量</th>
<th>金额</th>
<th>删除</th>
</tr>
</thead>
<tbody id="goods">
<!--<tr>
<td>面膜</td>
<td>150</td>
<td align="center">
<input type="button" value="-" id="jian" onclick="change(this,-1);"/>-->
<!--readonly规定输入字段为只读-->
<!--<input id="text" type="text" size="1" value="1" readonly="readonly" />
<input type="button" value="+" id="add" onclick="change(this,1);"/>
</td>
<td> <input id="money" size="1" value="80"></input></td>
<td align="center">
<input type="button" value="X" onclick="del(this)" />
</td>
</tr>-->
</tbody>
<tfoot>
<tr>
<td colspan="3" align="center" >总计</td>
<td id="total"></td>
<td>元</td>
</tr>
</tfoot>
</table>
</body>
<script type="text/javascript">
//this js中指当前对象
function add_shoppingcar(btn){
var tr=btn.parentNode.parentNode;
var tds=tr.getElementsByTagName("td");
var name=tds[0].innerHTML;
var price=tds[1].innerHTML;
var tbody=document.getElementById("goods");
var row=tbody.insertRow();//insertRow表格开头插入新行
row.innerHTML="<td>"+name+"</td>"+
"<td>"+price+"</td>"+
"<td align='center'>"+
"<input type='button' value='-' id='jian' onclick='change(this,-1)' />"+
"<input id='text' type='text' size='1' value='1' readonly='readonly' />"+
"<input type='button' value='+' id='add' onclick='change(this,1)' />"+
"</td>"+
"<td>"+price+"</td>"+
"<td align='center'>"+
"<input type='button' value='X' onclick='del(this)'/>"+
"</td>"+
"</tr>"
total();
}
//增加减少数量,用n正负1来表示点击了加减按钮
function change(btn,n){
//获取数量的三个input对象
var inputs = btn.parentNode.getElementsByTagName("input");
//获取原来的数量
var amount = parseInt(inputs[1].value);
//当amount=1时不能再点击"-"符号
//用n<0来表示点击了减button
if(amount<=1 && n<0){
return;
}
//根据加减来改变数量
inputs[1].value = amount + n;
实现效果:
着过年放假在家复习了之前学的JS知识,用原生撸了一个购物车模块,下面我来整理一下我的思路分享给大家。
1.1 废话不多说,首先上个效果图,如下:
购物车功能效果图
1.2 功能介绍:
功能介绍完毕,下面开始介绍我写这个购物车的步骤。
2.1 HTML代码
<table>
<caption>
购物车
</caption>
<thead>
<tr>
<!-- 全选复选框 -->
<th>
<input type="checkbox" name="checkAll" id="check-all" checked /><label for="check-all">全选</label>
</th>
<th>图片</th>
<th>品名</th>
<th>单位</th>
<th>单价/元</th>
<th>数量</th>
<th>金额/元</th>
</tr>
</thead>
<tbody>
<tr>
<td>
<input type="checkbox" name="item" value="SN-1020" checked />
</td>
<td>
<a href=""><img src="images/p1.jpg" alt="" /></a>
</td>
<td>iPhone 11</td>
<td>台</td>
<td class="price">4799</td>
<td><input type="number" min="1" value="1" /></td>
<td class="amount">xxxx</td>
</tr>
<tr>
<td>
<input type="checkbox" name="item" value="SN-1020" checked />
</td>
<td>
<a href=""><img src="images/p2.jpg" alt="" /></a>
</td>
<td>小米pro 11</td>
<td>部</td>
<td class="price">3999</td>
<td><input type="number" min="1" value="2" /></td>
<td class="amount">xxxx</td>
</tr>
<tr>
<td>
<input type="checkbox" name="item" value="SN-1030" checked />
</td>
<td>
<a href=""><img src="images/p3.jpg" alt="" /></a>
</td>
<td>MacBook Pro</td>
<td>台</td>
<td class="price">18999</td>
<td><input type="number" min="1" value="1" /></td>
<td class="amount">xxxx</td>
</tr>
<tr>
<td>
<input type="checkbox" name="item" value="SN-1040" checked />
</td>
<td>
<a href=""><img src="images/p4.jpg" alt="" /></a>
</td>
<td>小米75电视</td>
<td>台</td>
<td class="price">5999</td>
<td><input type="number" min="1" value="2" /></td>
<td class="amount">xxxx</td>
</tr>
<tr>
<td>
<input type="checkbox" name="item" value="SN-1050" checked />
</td>
<td>
<a href=""><img src="images/p5.jpg" alt="" /></a>
</td>
<td>Canon 90D单反</td>
<td>台</td>
<td class="price">9699</td>
<td><input type="number" min="1" value="1" /></td>
<td class="amount">xxxx</td>
</tr>
</tbody>
<tfoot>
<tr style="font-weight: bolder; font-size: 1.2em">
<td colspan="5">总计:</td>
<td id="sum">xxxx</td>
<td id="total-amount">xxxx</td>
</tr>
</tfoot>
</table>2.2 CSS代码
table {
border-collapse: collapse;
width: 90%;
text-align: center;
margin: auto;
}
table caption {
margin-bottom: 15px;
font-size: 1.5rem;
}
table th, table td {
border-bottom: 1px solid #ccc;
padding: 5px;
font-weight: normal;
}
table thead tr:first-of-type {
background-color: #e6e6e6;
height: 3em;
}
table input[type="checkbox"] {
width: 1.5em;
height: 1.5em;
}
table tbody tr {
border-bottom: 1px solid #ccc;
}
table tbody tr:hover {
background-color: #f6f6f6;
cursor: pointer;
}
tbody img {
width: 3em;
}
tbody input[type="number"] {
width: 3em;
}
button {
width: 150px;
height: 30px;
outline: none;
border: none;
background-color: teal;
color: white;
letter-spacing: 5px;
}
button:hover {
opacity: 0.7;
cursor: pointer;
}2.3 效果图
购物车效果图
以上就是一个简单的购物车页面的HTML和CSS样式代码。
三、完成相关JS代码
首先,我们先完成商品的全选与取消全选的功能,所以肯定是需要拿到全选复选框元素和商品前面的复选框元素,代码如下:
// 获取全选复选框,所有的商品都有一个独立的复选框
const checkAll = document.querySelector('#check-all');
const checkItems = document.getElementsByName('item');拿到全选和每个商品的复选框元素之后,给全选框添加一个change事件,监听它的checked值的变化。此时全选框的checked值可以通过事件监听回调函数中的ev参数下的ev.target.checked拿到。
checkALl.onchange = ev => {
// 如果全选框处于选中状态,ev.target.checked的值就为true,反之,为false。
console.log(ev.target.checked);
};如果想让全选框的的状态和每个商品前的复选框状态保持一致,那么就使他们的checked值一致即可。因此,我们可以在全选复选框的change事件中遍历每个商品的复选框元素。
checkALl.onchange = ev => {
// 如果全选框处于选中状态,ev.target.checked的值就为true,反之,为false。
console.log(ev.target.checked);
checkItems.forEach(item => item.checked = ev.target.checked);
};这样点击全选框的时候,就可以实现全部选中,和取消全选的功能了。效果如图:
全选与取消全选
全选和取消全选的功能完成之后,下面开始完善逐个勾选商品,直至勾选全部商品,让全选按钮自动变成被选中的状态。
要完成这个功能,我们可以通过对每个商品的复选框添加一个change事件来监听checked的变化。因此需要通过forEach()方法对遍历每一个商品。
checkItems.forEach(item => item.onchange = ev => {
// 在这里处理每一项的checked值
});此时,我们可以这样考虑:当每个商品的复选框都被勾选,即:所有商品复选框的checked的值全部为true时,全选复选框才会显示被勾选的状态,也就是全选复选框的checked的值也要为true。
由于checkAll的状态依赖于每一项商品的checked值,那么可以利用一个数组函数:Array.every()遍历每一项商品,当所有商品的checked值都为true时,every()方法的返回值就是一个true,然后再赋值给checkAll即可。注意:由于我们拿到的checkItems是一个NodeList数组,需要先将其转换成数组后再进行操作。
checkItems.forEach(item => item.onchange = ev => {
checkAll.checked = Array.from(checkItems).every(checkItem => checkItem.checked);
});点击选中每个商品
至此,全选和单选功能全部完成了。下面开始写自动计算金额的和总数的功能。
购物车的数量和金额不仅包含每一项商品的数量和每一项商品的总金额,还包含了计算选中的商品总数,以及所有选中的商品的总金额。
下面首先完成单个商品的总金额计算,总金额 = 单价 * 数量,根据这个公式,我们首先拿到商品的单价和数量元素。
// 获取单价组成的数组
const priceLists = document.querySelectorAll('.price');
// 获取数量组成的数组
const numberLists = document.querySelectorAll('body input[type=number]');以上单价(priceLists)和数量(numberLists)都是NodeList类型的,需要先将它们转换成数组,由于表单中获取的内容都是string类型,而参与计算的需要的是整型,所以这里需要进行一下转换,使用parseInt()方法即可。
// 获取商品单价组成的数组
const priceLists = document.querySelectorAll('.price');
const priceArr = Array.from(priceLists).map(item => parseInt(item.textContent)); // [ 4799, 3999, 18999, 5999, 9699 ]
// 获取商品数量组成的数组
const numberLists = document.querySelectorAll('body input[type=number]');
const numbersArr = Array.from(numberLists).map(item => parseInt(item.value)); // 默认值:[ 1, 1, 1, 1, 1 ]注意:商品价格和商品数量在取值时有些不同。商品的单价是普通元素直接使用textContent即可拿到它内部的值,而数量这个用的是表单控件,所以需要使用value才可以拿到值。 我刚开始写这个功能的时候懵逼了半天,此处一定要注意。
拿到商品的单价和数量之后就可以按照上面的公式进行计算了,由于商品的价格和商品的数量都是一个数组,并且价格和数量在数组中都是一一对应的关系,因此可以使用JS数组的reduce()方法进行遍历。
let amountArr = [priceArr, numbersArr].reduce((prev, curr) => {
return prev.map((item, index) => {
return item * curr[index];
});
});总感觉上述写法有点怪怪的,是不是可以进行简化呢?根据箭头函数的特征,当只有一条返回语句的时候可以省略掉return关键字和大括号,因此上述方法可以简写成下面这样:
let amountArr = [priceArr, numbersArr].reduce((prev, curr) => prev.map((item, index) => item * curr[index]));
console.log(amountArr); // [ 4799, 3999, 18999, 5999, 9699 ]
(PS:上面的方法我一开始也没有发现可以简写,我是把代码发给我朋友看了之后,朋友给我点醒了。还是才疏学浅呀。)
这时已经计算出来了每个商品的总金额,那么我们将其渲染到页面中。
// 获取单个商品总金额的元素数组
const amountDOM = document.querySelectorAll('.amount');
amountDOM.forEach((item, index) => item.textContent = amountArr[index]);计算每个商品的金额并渲染到页面中
单个商品的总金额渲染到页面之后,下面就开始计算商品的总数,和总金额了。根据某东、某宝的购物车功能,我们可以发现,总计那里统计的商品总数是一般是我们勾选上的商品总数,总金额也是一样的,那么我们就需要根据商品的状态来进行计算了。
首先声明一个数组,用于存储被选中的商品的状态,如果被选中,值为1,未被选中,则为0。
let isChecked = [];
checkItems.forEach(item => isChecked.push(item.checked === true ? 1 : 0));
// 打印出商品状态值
console.log(isChecked);打印商品状态值
商品的状态已经记录好了,那么现在就需要统计选中的商品对应的数量了。
// 声明一个用于存储商品数量的数组,该数组的作用是用于与对应的商品的状态值的数组进行相乘,得到实际的被选中的商品的数组。
let checkedNumbers = [];
numbersArr.forEach((item, index) => checkedNumbers.push(item * isChecked[index]));
// 打印被选中的商品的数量
console.log(checkedNumbers);打印出选中的商品的数量数组
计算出被选中的商品数量的总数并渲染到页面中:
let checkedSum = checkedNumbers.reduce((prev, curr) => prev + curr);
// 将获取的数量结果渲染到页面中
document.querySelector('#sum').textContent = checkedSum;效果如上图已经出来了。
下面开始计算被选中的商品的总金额,该总金额等于上面所有被选中的商品的总金额之和。计算出结果之后渲染到页面中。
// 声明一个数组用于存储每一个被选中的商品的总金额
let checkedPrice = [];
checkedNumbers.forEach((item, index) => checkedPrice.push(item * priceArr[index]));
// 打印被选中的每个被选中的商品总金额
console.log(checkedPrice);
// 计算被选中的商品总金额
let totalAmount = checkedPrice.reduce((prev, curr) => prev + curr);
// 将选中的商品总金额渲染到页面中
document.querySelector('#total-amount').textContent = totalAmount;将总金额渲染到页面
至此,关于计算单个商品的总金额以及被选中商品的数量和总金额的功能已经全部完成了,但是我们还需要实现在页面加载以及更改某个商品数量时自动计算的功能。那么就需要将上述的计算功能封装成一个函数,以便后面每一次执行计算时使用。
function autoCalculate() {
// 获取单价组成的数组
const priceLists = document.querySelectorAll('.price');
const priceArr = Array.from(priceLists).map(item => parseInt(item.textContent));
// 获取数量组成的数组
const numberLists = document.querySelectorAll('body input[type=number]');
const numbersArr = Array.from(numberLists).map(item => parseInt(item.value));
console.log(priceArr, numbersArr);
// 由于拿到的表单里的数据都是string类型的,所以需要先将其转换成int类型,因此需要使用`map()`方法操作一下
let amountArr = [priceArr, numbersArr].reduce((prev, curr) => prev.map((item, index) => item * curr[index]));
console.log(amountArr);
const amountDOM = document.querySelectorAll('.amount');
amountDOM.forEach((item, index) => item.textContent = amountArr[index]);
// 首先声明一个数组,用于存储被选中的商品的状态,如果被选中,值为1,未被选中,则为0
let isChecked = [];
checkItems.forEach(item => isChecked.push(item.checked === true ? 1 : 0));
console.log(isChecked);
// 声明一个用于存储是商品数量的数组,该数组的作用是:如果商品处于被选中的状态,那么就存储它真实的数量值,
// 如果没有被选中,那么数量就是0
let checkedNumbers = [];
numbersArr.forEach((item, index) => checkedNumbers.push(item * isChecked[index]));
console.log(checkedNumbers);
// 此时,被选中的商品的总数为:
let checkedSum = checkedNumbers.reduce((prev, curr) => prev + curr);
console.log(checkedSum);
// 将获取的数量结果渲染到页面中
document.querySelector('#sum').textContent = checkedSum;
// 下面开始计算被选中的商品的总金额,该总金额等于上面所有被选中的商品的总金额之和。
// 声明一个数组用于存储每一个被选中的商品的总金额
let checkedPrice = [];
checkedNumbers.forEach((item, index) => checkedPrice.push(item * priceArr[index]));
console.log(checkedPrice);
// 计算被选中的商品总金额
let totalAmount = checkedPrice.reduce((prev, curr) => prev + curr);
// 将选中的商品总金额渲染到页面中
document.querySelector('#total-amount').textContent = totalAmount;
}将代码封装后我们会发现,单个商品的总金额,商品总数以及总金额的值都没了,如下图:
封装代码后的效果
这是因为,代码在第一次加载的时候并没有执行封装后的函数,因此需要加一行代码:
// 页面第一次加载的时候自动执行一次。
window.onload = autoCalculate;这样页面中的数据在第一次加载的时候就全部都正常了。
下面完成最后一个功能:调整商品的数量,会自动计算总数和金额。该功能还是通过change事件监听某个表单数据的变化来完成。效果图下图:
// 监听某个控件的事件,首先需要拿到控件元素。
const numInput = document.querySelectorAll('body input[type=number]');
// 上面都用了onchange来监听,这里换个方法使用addEventListener。
numInput.forEach(item => item.addEventListener('change', autoCalculate));但是我们会发现这里有个小bug,就是如果勾选没有选中的商品,并不会自动计算商品数量和总价,原因很简单,我们在监听单个商品选中和全选的时候根本就没有执行自动计算函数,只需要在二者的事件监听中加上自动计算的函数即可。
checkAll.onchange = ev => {
checkItems.forEach(item => item.checked = ev.target.checked);
// 解决勾选全选框不会自动计算的bug
autoCalculate();
};
checkItems.forEach(item => item.onchange = ev => {
checkAll.checked = Array.from(checkItems).every(checkItem => checkItem.checked);
// 解决勾选全选框不会自动计算的bug
autoCalculate();
});写到这里,我们购物车的所有功能都已经完成了。购物车这个模块看似不难,其实这里面的坑也是不少的,例如:
以上就是我个人在写这个购物车功能的全部新的,由于本人也是新手,可能还有其他更简介方便的写法,如果有问题,请各位大佬批评指正,不胜感激。
如果有刚开始学习JS的同学,想要源码的各位亲,可以关注并私信回复“购物车”即可。
单购物车展示
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>购物车</title>
<style type="text/css">
table{
width: 80%;
border: #000000 solid 1px;
border-collapse: collapse; /* 为表格设置合并边框模型 */
}
th{
border: #000000 solid 1px;
}
tr{
border: #000000 solid 1px;
}
</style>
</head>
<body>
<div id="app" >
<table v-if="book.length">
<tr>
<th></th>
<th>书籍名称</th>
<th>出版日期</th>
<th>价格</th>
<th>购买数量</th>
<th>操作</th>
</tr>
<tr v-for="(i,index) in book">
<th>{{i.id}}</th>
<th>{{i.name}}</th>
<th>{{i.date}}</th>
<th>{{i.price}}</th>
<th> <!-- :disabled="i.count <=1"数量小于等于1的时候点击失效 -->
<button @click="jian(index)" :disabled="i.count <=1">-</button>
{{i.count}}
<button @click="jia(index)">+</button>
</th>
<th><button @click="shanchu(index)">移除</button></th>
</tr>
<tr>
<th>总价格:</th>
<th>{{zongjiage | guolv}}</th><!-- 使用过滤器把总价格过滤成我们想要的样式 -->
<th></th>
<th></th>
<th></th>
<th></th>
</tr>
</table>
<h1 v-else>购物车里啥都没有了</h1>
</div>
<script src="../js/vue.js"></script>
<script>
const app = new Vue({
el: "#app",
data:{
book:[
{
id: 1,
name: '《算法导论》',
date: '2006-9',
price: 85.00,
count: 1
},
{
id: 2,
name: '《UNIX编程艺术》',
date: '2006-2',
price: 59.00,
count: 1
},
{
id: 3,
name: '《编程珠玑》',
date: '2008-10',
price: 39.00,
count: 1
},
{
id: 4,
name: '《代码大全》',
date: '2006-3',
price: 128.00,
count: 1
},
]
},
methods:{
jian(index){
this.book[index].count--
},
jia(index){
this.book[index].count++
},
shanchu(index){ // 点击的哪个就把哪个的下标传过来
this.book.splice(index, 1) // 因为所点击的这行比下标多1个数,所以删除传过来的下标这一行的下一行
}
},
computed:{
zongjiage(){
let zong = 0;
for(let i=0; i<this.book.length; i++){
zong += this.book[i].price * this.book[i].count;
}
return zong;
}
},
filters:{ // 定义一个过滤器
guolv(price){ // 过滤函数 参数是价格
return '¥:' + price.toFixed(2) + '元' // toFixed(2)是保留2位小数
}
}
})
</script>
</body>
</html>
所有图片连在一起就是本文的所有代码
*请认真填写需求信息,我们会在24小时内与您取得联系。