最终效果
一、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/style.css">
</head>
<body>
<!-- 项目 -->
<div class="container">
<div class="header">
<div class="select">
<input type="checkbox" class="selectall"> 全选
</div>
<span>商品</span>
<span></span>
<span>单价</span>
<span>数量</span>
<span>小计</span>
<span>操作</span>
</div>
<!-- 店铺 -->
<div class="wrapper">
<div class="shop-wrap">
<input type="checkbox" class="shopname">
<strong>魅嫂青音专卖店</strong>
</div>
<div class="product">
<ul>
<li class="list">
<input type="checkbox" class="selectlist">
<img src="./img/s1.jpg" alt="">
<p class="title">结婚礼物团扇结婚 结婚用品 女方手捧花结婚结婚扇子中式团扇秀禾服新娘结</p>
<p class="kind">红色双圈-双飞燕(成品单扇子)</p>
<p class="singleprice">¥248.40</p>
<div class="number">
<button class="reduce">-</button>
<input type="text" class="count" value="4">
<button class="addnum">+</button>
</div>
<p class="countprice">¥993.60</p>
<p class="control">
<span class="deletion">删除</span>
<br>
<span>移入关注</span>
</p>
</li>
<li class="list">
<input type="checkbox" class="selectlist">
<img src="./img/s2.jpg" alt="">
<p class="title">结婚礼物团扇结婚 结婚用品 女方手捧花结婚结婚扇子中式团扇秀禾服新娘结</p>
<p class="kind">红色双圈-双飞燕(成品单扇子)</p>
<p class="singleprice">¥194.40</p>
<div class="number">
<button class="reduce disabled">-</button>
<input type="text" class="count" value="1">
<button class="addnum">+</button>
</div>
<p class="countprice">¥194.40</p>
<p class="control">
<span class="deletion">删除</span>
<br>
<span>移入关注</span>
</p>
</li>
</ul>
</div>
</div>
<div class="wrapper">
<div class="shop-wrap">
<input type="checkbox" class="shopname">
<strong>礼尚往来礼品专营店</strong>
</div>
<div class="product">
<ul>
<li class="list">
<input type="checkbox" class="selectlist">
<img src="./img/s3.jpg" alt="">
<p class="title">结婚礼物团扇结婚 结婚用品 女方手捧花结婚结婚扇子中式团扇秀禾服新娘结</p>
<p class="kind">红色双圈-双飞燕(成品单扇子)</p>
<p class="singleprice">¥119.00</p>
<div class="number">
<button class="reduce disabled">-</button>
<input type="text" class="count" value="1">
<button class="addnum">+</button>
</div>
<p class="countprice">¥119.00</p>
<p class="control">
<span class="deletion">删除</span>
<br>
<span>移入关注</span>
</p>
</li>
<li class="list">
<input type="checkbox" class="selectlist">
<img src="./img/s4.jpg" alt="">
<p class="title">结婚礼物团扇结婚 结婚用品 女方手捧花结婚结婚扇子中式团扇秀禾服新娘结</p>
<p class="kind">红色双圈-双飞燕(成品单扇子)</p>
<p class="singleprice">¥119.00</p>
<div class="number">
<button class="reduce">-</button>
<input type="text" class="count" value="2">
<button class="addnum">+</button>
</div>
<p class="countprice">¥238.00</p>
<p class="control">
<span class="deletion">删除</span>
<br>
<span>移入关注</span>
</p>
</li>
</ul>
</div>
</div>
<div class="wrapper">
<div class="shop-wrap">
<input type="checkbox" class="shopname">
<strong>智慧礼品官方旗舰店</strong>
</div>
<div class="product">
<ul>
<li class="list">
<input type="checkbox" class="selectlist">
<img src="./img/s5.jpg" alt="">
<p class="title">结婚礼物团扇结婚 结婚用品 女方手捧花结婚结婚扇子中式团扇秀禾服新娘结</p>
<p class="kind">红色双圈-双飞燕(成品单扇子)</p>
<p class="singleprice">¥188.00</p>
<div class="number">
<button class="reduce disabled">-</button>
<input type="text" class="count" value="1">
<button class="addnum">+</button>
</div>
<p class="countprice">¥188.00</p>
<p class="control">
<span class="deletion">删除</span>
<br>
<span>移入关注</span>
</p>
</li>
</ul>
</div>
</div>
</div>
<div class="combine">
<p>
已选择
<em class="amount-sum">0</em>
件商品
</p>
<p>
总价
<span class="pricetotal priceShow">¥0.00</span>
</p>
</div>
<script src="./js/main.js"></script>
</body>
</html>
二、CSS部分
/* 格式化*/
* {
margin: 0;
padding: 0;
}
/* 设置img右行盒inline到块盒block*/
img {
display: block;
}
/* 设置列表样式为无样式 */
ul,li {
list-style: none;
}
/* css 添加手状样式,鼠标移上去变小手 */
input {
cursor: pointer;
}
/* css 添加手状样式,鼠标移上去变小手 */
button{
cursor: pointer;
}
/* css 添加手状样式,鼠标移上去变小手 */
.control{
cursor: pointer;
}
.container {
width: 990px;
font-size: 12px;
color: #434343;
margin: 0 auto;
}
.header {
display: flex; /* 设置header为弹性盒子 */
height: 40px;
padding: 5px 0;
line-height: 40px; /* line-height,又称行高,指的是两行文字基线之间的距离 */
margin-bottom: 10px;
}
.select {
width: 133px;
}
/* 对第三个span的设置 */
.header span:nth-child(2) {
width: 208px;
}
.header span:nth-child(3) {
width: 170px;
padding: 0 10px 0 20px;
}
/* text-align用来设置元素中的的文本对齐方式 */
.header span:nth-child(4) {
width: 170px;
padding-right: 50px;
text-align: right;
}
.header span:nth-child(5) {
width: 80px;
text-align: center;
}
.header span:nth-child(6) {
width: 140px;
padding-right: 40px;
text-align: right;
}
.header span:nth-child(7) {
width: 75px;
}
.shop-wrap {
height: 30px;
line-height: 30px;
padding-left: 11px;
margin-bottom: 10px;
border-bottom: 2px solid #aaa;
}
/* 默认 flex-direction: row ;,加上align-items: flex-start; 以为着横行交叉轴的起始位置*/
.list {
display: flex;
align-items: flex-start;
margin-bottom: 10px;
height: 108px;
}
.selectlist {
margin-right: 20px;
}
.list img {
width: 80px;
height: 80px;
margin-right: 10px;
}
/* 给一个元素中设置overflow:hidden,那么该元素的内容若超出
了给定的宽度和高度属性,那么超出的部分将会被隐藏,不占位。 */
.list .title {
font-size: 12px;
width: 204px;
height: 40px;
line-height: 20px;
overflow: hidden;
}
/* 用于设置文本在容器中溢出时的显示方式。当值设置为 ellipsis 时,
文本会以省略号 (...) 的形式来代替被隐藏的文本部分 */
.kind {
width: 160px;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap; /* 用于指定元素内的文本不换行 */
font-size: 12px;
}
.singleprice {
text-align: right;
width: 190px;
padding-right: 40px;
}
.number {
width: 84px;
display: flex;
justify-content: flex-start; /* 在主轴上由左向右排列 */
}
.number button {
width: 17px;
height: 22px;
background-color: #fff;
border: 1px solid #aaa;
}
.count {
width: 46px;
height: 20px;
outline: none;
border: 1px solid #aaa;
text-align: center;
}
.countprice {
width: 140px;
padding-right: 40px;
text-align: right;
font-weight: 900;
}
.control {
width: 75px;
text-align: left;
}
.disabled {
cursor: no-drop; /* 不可拖动光标 */
}
.combine {
width: 990px;
height: 30px;
line-height: 30px;
margin: 0 auto; /* 水平居中 */
display: flex;
font-size: 12px;
color: #434343;
justify-content: flex-start;
}
.amount-sum {
color: #e2231a;
font-style: normal;
font-weight: 900;
}
.combine p {
margin-right: 20px;
}
.pricetotal {
font-size: 16px;
font-weight: 900;
color: #e2231a;
}
三、JS部分
在转载的基础上加入了对删除按钮的操作
let selectall = document.querySelector('.selectall');
// 查找所有的店铺
let shopname = document.querySelectorAll('.shopname');
// 查找所有的商品列表
let selectlist = document.querySelectorAll('.selectlist');
// 查找所有的操作数量按钮
let addnum = document.querySelectorAll('.addnum'); //加
let reduce = document.querySelectorAll('.reduce'); //减
let count = document.querySelectorAll('.count'); //计数
// 获取删除节点
let deletion = document.querySelectorAll('.deletion');
// 获取 单一商品的价钱 小计
let countprice = document.querySelectorAll('.countprice');
// 获取 每个商品的单价
let singleprice = document.querySelectorAll('.singleprice');
// 获取 总件数 总价钱
let amount_sum = document.querySelector('.amount-sum');
let pricetotal = document.querySelector('.pricetotal')
// 实现全选的效果
selectall.onclick = function () {
// 获取全选按钮的状态
let status = this.checked;
// 将全选按钮状态同步到每个店铺
shopname.forEach(function (item, index) {
item.checked = status;
})
// 将全选按钮状态同步到每个商品
selectlist.forEach(function (item, index) {
item.checked = status;
})
// 计算总价和总数
countTotal(count,selectlist)
}
// 点击 加号 改变 商品数量
addnum.forEach(function (item, index) {
item.onclick = function () {
// 获取当前点击的节点所在数量,通过index确定具体是点击那个
let val = +count[index].value;
// 点击一次调用一次,数量加一
val++;
// 将最终的数量赋值给count所在节点的value
count[index].value = val;
// 因为value初始值最小为1,增加数量的时候去掉不能点击限制
reduce[index].classList.remove('disabled');
// 计算小计
countSinglePrice(index, val);
// 获取店铺所有商品节点列表HTMLCollection(2) [li.list, li.list]
let shop_all_product = this.parentElement.parentElement.parentElement.children;
// 获取店铺名所在节点 <input type="checkbox" class="shopname">
let shop_parent = this.parentElement.parentElement.parentElement.parentElement.previousElementSibling.children[0];
// 点击增加数量默认商品变为选择态,同时判断店铺是否为选择态
selectShopName(shop_all_product, shop_parent);
// 判断全选是否变为选择态
selectAllButton();
// 计算选择数量和总价
countTotal(count,selectlist)
}
})
// 点击 减号 改变 商品数量
reduce.forEach(function (item, index) {
item.onclick = function () {
// + 实现隐式类型转换string变number,也可用number函数转换
// 获取当前点击的节点所在数量,通过index确定具体是点击那个
let val = +count[index].value;
// 数量最小为1
if (val == 1) {
val = 1;
// 数量不是1,随着点击减少
} else {
val--;
}
// 如果小于1,增加disabled属性,变为不可选状态,此处通过多类名实现
if (val <= 1) {
this.classList.add('disabled')
}
// 将最终的数量赋值给count所在节点的value
count[index].value = val;
// 计算小计
countSinglePrice(index, val);
// 获取店铺所有商品节点列表HTMLCollection(2) [li.list, li.list]
let shop_all_product = this.parentElement.parentElement.parentElement.children;
// 获取店铺名所在节点 <input type="checkbox" class="shopname">
let shop_parent = this.parentElement.parentElement.parentElement.parentElement.previousElementSibling.children[0]
// 点击删除数量默认商品变为选择态,同时判断店铺是否为选择态
selectShopName(shop_all_product, shop_parent);
// 判断全选是否变为选择态
selectAllButton();
// 计算选择数量和总价
countTotal(count,selectlist)
}
})
// 实现删除效果
deletion.forEach(function(item,index){
item.onclick = function(){
// 获取店铺商品列表节点
const shop_list_product = this.parentElement.parentElement.parentElement;
console.log(shop_list_product)
// 获取商品节点
const shop_product = this.parentElement.parentElement;
// 暂存店铺商品列表的原始长度
// 获取店铺的所有商品节点,shop_list_product.children结果为HTMLCollection(2) [li.list, li.list],数组里面的内容是对象
const shop_lenth = shop_list_product.children.length;
// 获取店铺父节点
const shop_wrapper = this.parentElement.parentElement.parentElement.parentElement.previousElementSibling.parentElement
// 获取店铺节点
const ship_wrap = this.parentElement.parentElement.parentElement.parentElement.previousElementSibling
// 删除商品
shop_list_product.removeChild(shop_product);
// 如果店铺商品列表的原始长度等于1,说明此次删除操作完再没有商品,同时删除店铺列表
if(shop_lenth == 1){
shop_wrapper.removeChild(ship_wrap)
}
// 更新商品选择列表
let selectlist_update = document.querySelectorAll('.selectlist');
// 更新商品计数列表
let count_update = document.querySelectorAll('.count');
// 计算选择数量和总价
countTotal(count_update, selectlist_update);
}
})
// 选择店铺 实现选中该店铺所有的商品
shopname.forEach(function (item, index) {
item.onclick = function () {
let shop_status = this.checked; //获取当前店铺的选取状态
// 获取店铺商品列表HTMLCollection(2) [li.list, li.list]
let product_list = this.parentElement.nextElementSibling.children[0].children;
// 让商品的选择状态和店铺的选择状态保持一致
for (let i = 0; i < product_list.length; i++) {
product_list[i].children[0].checked = shop_status
}
// 选择所有店铺,全选按钮改变选择状态
let shop_number = 0;
// 每次选择一个店铺的时候对所有店铺的选择状态做遍历
shopname.forEach(function (item_status, index) {
if (item_status.checked) {
shop_number += 1; // 如果是选择态shop_number加1
}
})
// 如果所有店铺都是选择态,全选按钮全选
if (shopname.length == shop_number) {
selectall.checked = true;
} else {
selectall.checked = false;
}
countTotal(count,selectlist)
}
})
// 选择商品 实现店铺选中的效果 函数封装
// 参数 ele: 店铺下的所有的商品 ; shop_parent 店铺名所在节点<input type="checkbox" class="shopname">
function selectShopName(ele, shop_parent) {
let list_number = 0;
for (let i = 0; i < ele.length; i++) {
if (ele[i].children[0].checked) {
list_number++;
}
}
if (list_number == ele.length) {
shop_parent.checked = true;
} else {
shop_parent.checked = false;
}
}
// 选择单一商品 实现店铺的选中 和 全选按钮的选中
selectlist.forEach(function (item, index) {
item.onclick = function () {
// 店铺下的所有的商品 HTMLCollection(2) [li.list, li.list]
let shop_all_product = this.parentElement.parentElement.children;
// 店铺名所在节点<input type="checkbox" class="shopname">
let shop_parent = this.parentElement.parentElement.parentElement.previousElementSibling.children[0];
console.log(shop_parent)
selectShopName(shop_all_product, shop_parent);
selectAllButton();
countTotal(count,selectlist)
}
})
// 实现单一商品的价钱计算
function countSinglePrice(index, val) {
let s_price = Number(singleprice[index].innerHTML.substring(1))
let single_count_price = (s_price * val).toFixed(2);
countprice[index].innerHTML = `¥ ${single_count_price}`;
// 默认选中该商品
selectlist[index].checked = true;
}
// 选中所有单一商品 实现全选按钮选中
function selectAllButton() {
let single_product = 0;
selectlist.forEach(function (single_status, index) {
if (single_status.checked) { //检查每一个商品的选择状态
single_product++
}
})
if (single_product == selectlist.length) {
selectall.checked = true;
} else {
selectall.checked = false;
}
}
// 删除后实现商品总价钱和 总数量的计算
// coun:所有商品数量 sele:所有商品的选择节点
function countTotal(coun, sele) {
let countTotalNumber = 0; // 选中的商品的总数量
let countTotalPrice = 0; // 选中是商品的总价钱
coun.forEach(function (item, index) {
if (sele[index].checked) {
// 计算商品数量
let num = +item.value;
countTotalNumber += num;
// 计算总价钱
let single_price = Number(countprice[index].innerHTML.substring(1));
countTotalPrice += single_price;
}
})
amount_sum.innerHTML = countTotalNumber;
pricetotal.innerHTML = `¥${countTotalPrice}`;
}