前端学习——JavaScript原生实现购物车案例


Posted in Javascript onMarch 31, 2021

一. 购物车案例

1.1 案例介绍

今天我们来写另外一个购物车案例,说实话对于我来说这个是花了将近三个小时的时间然后才做出来的,里面可能还存在一些我没有发现的问题,但是能完成基本的功能,对于一些基本的需求都是可以完成的,下面照旧是案例实现的gif图片

前端学习——JavaScript原生实现购物车案例

根据上图我们可以看到,每个购物车的选项都是互不影响的,每个商店也都是互不影响的,单独运算,每个店的总计也是单独计算的,有一个计算复选框总价的功能,而且这是个通用的模板,不论有多少个店铺,店铺里面的商品有多少个,只要html的结构不变都是可以通用的,那么不多说,下面开始分析案例!

1.2 案例分析

下面给出相应的html结构的代码,便于我们进行分析

<!doctype html>
<html>
<head>
<meta charset="utf-8">
<meta name="format-detection" content="telephone=no">
<meta name="viewport" content="user-scalable=no, width=device-width, initial-scale=1.0, maximum-scale=1.0">
<title>购物车</title>


<link type="text/css" rel="stylesheet" href="css/base.css" />
<link type="text/css" rel="stylesheet" href="css/module.css"  />

</head>
<body>

<!--头部开始-->
<div class="header">
   <h1>购物车</h1>
   <a href="#" class="back"><span></span></a>
   <a href="#" class=""></a>
</div>
<!--头部结束-->
<div class="shopping">
   
   <div class="shop-group-item">
      <div class="shop-name">
         <input type="checkbox" class="check goods-check shopCheck">
         <h4><a href="#">苹果专卖店</a></h4>
         <div class="coupons"><span>领券</span><em>|</em><span>编辑</span></div>
      </div>
      <ul>
         <li>
            <div class="shop-info">
               <input type="checkbox" class="check goods-check goodsCheck">
               <div class="shop-info-img"><a href="#"><img src="images/computer.jpg" /></a></div>
               <div class="shop-info-text">
                  <h4>Apple MacBook Air 13.3英寸笔记本电脑 银色(Core i5 处理器/8GB内存/128GB SSD闪存 MMGF2CH/A)</h4>
                  <div class="shop-brief"><span>重量:3.3kg</span><span>颜色:银色</span><span>版本:13.3英寸</span></div>
                  <div class="shop-price">
                     <div class="shop-pices">¥<b class="price">5899.00</b></div>
                     <div class="shop-arithmetic">
                        <a href="javascript:;" class="minus">-</a>
                        <input class="num" value="1"/>
                        <a href="javascript:;" class="plus">+</a>
                     </div>
                  </div>
               </div>
            </div>
         </li>
         <li>
            <div class="shop-info">
               <input type="checkbox" class="check goods-check goodsCheck">
               <div class="shop-info-img"><a href="#"><img src="images/ipad.jpg" /></a></div>
               <div class="shop-info-text">
                  <h4>Apple iPad Pro 平板电脑 10.5 英寸(64G WLAN版/A10X芯片/Retina屏/Multi-Touch技术 MQDX2CH/A)金色</h4>
                  <div class="shop-brief"><span>重量:0.85kg</span><span>颜色:金色</span><span>版本:64G WLAN版</span></div>
                  <div class="shop-price">
                     <div class="shop-pices">¥<b class="price">4788.00</b></div>
                     <div class="shop-arithmetic">
                        <a href="javascript:;" class="minus">-</a>
                        <input class="num" value="1"/>
                        <a href="javascript:;" class="plus">+</a>
                     </div>
                  </div>
               </div>
            </div>
         </li>
         <li>
            <div class="shop-info">
               <input type="checkbox" class="check goods-check goodsCheck">
               <div class="shop-info-img"><a href="#"><img src="images/iphone.jpg" /></a></div>
               <div class="shop-info-text">
                  <h4>苹果 iPhone7 Plus 手机 红色特别版 全网通 128GB</h4>
                  <div class="shop-brief"><span>重量:0.18kg</span><span>颜色:红色特别版</span><span>版本:全网通 128G</span></div>
                  <div class="shop-price">
                     <div class="shop-pices">¥<b class="price">6326.00</b></div>
                     <div class="shop-arithmetic">
                        <a href="javascript:;" class="minus">-</a>
                        <input class="num" value="1"/>
                        <a href="javascript:;" class="plus">+</a>
                     </div>
                  </div>
               </div>
            </div>
         </li>
      </ul>
      <div class="shopPrice">本店总计:¥<span class="shop-total-amount ShopTotal">0.00</span></div>
   </div>
   
   <div class="shop-group-item">
      <div class="shop-name">
         <input type="checkbox" class="check goods-check shopCheck">
         <h4><a href="#">小米专卖店</a></h4>
         <div class="coupons"><span>领券</span><em>|</em><span>编辑</span><!--<span class="shop-total-amount ShopTotal">0</span>--></div>
      </div>
      <ul>
         <li>
            <div class="shop-info">
               <input type="checkbox" class="check goods-check goodsCheck">
               <div class="shop-info-img"><a href="#"><img src="images/computer.jpg" /></a></div>
               <div class="shop-info-text">
                  <h4>小米(MI)小米电视4A 标准版 65英寸 HDR 2GB+8GB 四核64位高性能处理器 4K超高清智能语音网络液晶平板电视机(L65M5-AZ)</h4>
                  <div class="shop-brief"><span>重量:16.3kg</span><span>颜色:黑色</span><span>版本:4A标准版</span></div>
                  <div class="shop-price">
                     <div class="shop-pices">¥<b class="price">5999.00</b></div>
                     <div class="shop-arithmetic">
                        <a href="javascript:;" class="minus">-</a>
                        <input class="num" value="1"/>
                        <a href="javascript:;" class="plus">+</a>
                     </div>
                  </div>
               </div>
            </div>
         </li>
         <li>
            <div class="shop-info">
               <input type="checkbox" class="check goods-check goodsCheck">
               <div class="shop-info-img"><a href="#"><img src="images/computer.jpg" /></a></div>
               <div class="shop-info-text">
                  <h4>小米(MI) 定制版Ninebot 九号平衡车 智能代步电动体感车(白)</h4>
                  <div class="shop-brief"><span>重量:15.9kg</span><span>颜色:白色</span><span>版本:标准版</span></div>
                  <div class="shop-price">
                     <div class="shop-pices">¥<b class="price">1949.00</b></div>
                     <div class="shop-arithmetic">
                        <a href="javascript:;" class="minus">-</a>
                        <input class="num" value="1"/>
                        <a href="javascript:;" class="plus">+</a>
                     </div>
                  </div>
               </div>
            </div>
         </li>
      </ul>
      <div class="shopPrice">本店总计:¥<span class="shop-total-amount ShopTotal">0.00</span></div>
   </div>
   <div class="shop-group-item">
      <div class="shop-name">
         <input type="checkbox" class="check goods-check shopCheck">
         <h4><a href="#">苹果专卖店</a></h4>
         <div class="coupons"><span>领券</span><em>|</em><span>编辑</span></div>
      </div>
      <ul>
         <li>
            <div class="shop-info">
               <input type="checkbox" class="check goods-check goodsCheck">
               <div class="shop-info-img"><a href="#"><img src="images/computer.jpg" /></a></div>
               <div class="shop-info-text">
                  <h4>Apple MacBook Air 13.3英寸笔记本电脑 银色(Core i5 处理器/8GB内存/128GB SSD闪存 MMGF2CH/A)</h4>
                  <div class="shop-brief"><span>重量:3.3kg</span><span>颜色:银色</span><span>版本:13.3英寸</span></div>
                  <div class="shop-price">
                     <div class="shop-pices">¥<b class="price">5899.00</b></div>
                     <div class="shop-arithmetic">
                        <a href="javascript:;" class="minus">-</a>
                        <input class="num" value="1"/>
                        <a href="javascript:;" class="plus">+</a>
                     </div>
                  </div>
               </div>
            </div>
         </li>
         <li>
            <div class="shop-info">
               <input type="checkbox" class="check goods-check goodsCheck">
               <div class="shop-info-img"><a href="#"><img src="images/ipad.jpg" /></a></div>
               <div class="shop-info-text">
                  <h4>Apple iPad Pro 平板电脑 10.5 英寸(64G WLAN版/A10X芯片/Retina屏/Multi-Touch技术 MQDX2CH/A)金色</h4>
                  <div class="shop-brief"><span>重量:0.85kg</span><span>颜色:金色</span><span>版本:64G WLAN版</span></div>
                  <div class="shop-price">
                     <div class="shop-pices">¥<b class="price">4788.00</b></div>
                     <div class="shop-arithmetic">
                        <a href="javascript:;" class="minus">-</a>
                        <input class="num" value="1"/>
                        <a href="javascript:;" class="plus">+</a>
                     </div>
                  </div>
               </div>
            </div>
         </li>
         <li>
            <div class="shop-info">
               <input type="checkbox" class="check goods-check goodsCheck">
               <div class="shop-info-img"><a href="#"><img src="images/iphone.jpg" /></a></div>
               <div class="shop-info-text">
                  <h4>苹果 iPhone7 Plus 手机 红色特别版 全网通 128GB</h4>
                  <div class="shop-brief"><span>重量:0.18kg</span><span>颜色:红色特别版</span><span>版本:全网通 128G</span></div>
                  <div class="shop-price">
                     <div class="shop-pices">¥<b class="price">6326.00</b></div>
                     <div class="shop-arithmetic">
                        <a href="javascript:;" class="minus">-</a>
                        <input class="num" value="1"/>
                        <a href="javascript:;" class="plus">+</a>
                     </div>
                  </div>
               </div>
            </div>
         </li>
      </ul>
      <div class="shopPrice">本店总计:¥<span class="shop-total-amount ShopTotal">0.00</span></div>
   </div>
</div>

<div class="payment-bar">
   <div class="all-checkbox"><input type="checkbox" class="check goods-check" id="AllCheck">全选</div>
   <div class="shop-total">
      <strong>总价:<i class="total" id="AllTotal">0.00</i></strong>
      <span>减免:123.00</span>
   </div>
   <a href="#" class="settlement">结算</a>
</div>
</body>
<script src="js/index.js"></script>
</html>

这个HTML文件不是我写的,只是一个案例的小样,这篇我们主要编写的是JS,对html和css没有任何操作

通过上面的html结构我们可以看出,每个商店都有一个class属性为.shop-group-item的div标签包裹住,每个商店里面的购物项都有一个li标签包裹住,那么我们就可以知道如果我们根据.shop-group-item和li标签获取DOM元素的话,得到的肯定不是一个唯一值,而是一个数组值

前端学习——JavaScript原生实现购物车案例

所以首先我们需要将他们区分开,那么怎么区分开呢,这个时候涉及到一个知识点,获取当前标签下面的父元素节点,然后通过这个父元素节点对于整个元素进行操作,这样就可以得到我们要获取的唯一值了,整个案例的核心思想就是这个,通过多个子节点获取唯一父节点的DOM对象,然后根据这个父节点完成我们的需求操作。

1.3 代码编写

1.3.1 商品数量添加的处理

前端学习——JavaScript原生实现购物车案例

再写复选框之前,我们先对商品数量添加的处理,因为这个涉及到求和的问题,所以我们需要先计算出她所需要求的数量才能走到求和的问题

/*计算value值*/
//获取减号按钮
let minus = document.querySelectorAll('.shop-group-item .shop-info .shop-price .minus');
//获取加号按钮
let plus = document.querySelectorAll('.shop-group-item .shop-info .shop-price .plus');
for (let i = 0; i < plus.length; i++) {
    /*加法的计算*/
    plus[i].onclick = function () {
        //通过点击的元素获取到value值
        let num = parseInt(this.parentElement.querySelector('.num').value);
        console.log(num);
        this.parentElement.querySelector('.num').value = num + 1;
        //获取到当前节点下的所有复选框
        getSumByCheck(this);
        getSum();
    }
    /*减法计算*/
    minus[i].onclick = function () {
        //通过点击的元素获取到value值
        let num = parseInt(this.parentElement.querySelector('.num').value);
        console.log(num);
        //判断如果num的最小值为0
        if (num > 0) {
            this.parentElement.querySelector('.num').value = num - 1;
        }
        //获取到当前节点下的所有复选框
        getSumByCheck(this);
        getSum();
    }
}

以上就是对于加号减号的处理,求数量的处理

1.3.2 求和处理

这里的求和处理我封装成了两个方法,之前本来想封装成一个方法的,但是发现方法并不能通用,封装成两个方法也比较好处理

一个是求总价的方法

前端学习——JavaScript原生实现购物车案例

/*总价求和*/
function getSum() {
    //先获取每个info
    let infos = document.querySelectorAll('.shop-group-item .shop-info');
    //设置总价格
    let totalCash = 0;
    for (let i = 0; i < infos.length; i++) {
        //再获取对应个info下面的价格
        let money = parseFloat(infos[i].querySelector('.price').innerText);
        //console.log(money);
        //获取对应info下面的value值
        let value = parseInt(infos[i].querySelector('.num').value);
        //console.log(value);
        //获取到复选框对象
        let check = infos[i].querySelector('.check');
        //如果被选中的话就进行计算
        if (check.checked) {
            totalCash += money * value;
        }
        /*infos[i].closest('.shop-group-item').querySelector('.shopPrice .shop-total-amount').innerText = totalCash;*/
    }
    document.querySelector('.payment-bar .total').innerText = totalCash;
    //将总价放在每一个总计标签里面
}

一个是求每个商店价格的方法,这个需要传进当前操作的this对象
前端学习——JavaScript原生实现购物车案例

/*根据check子节点求和*/
function getSumByCheck(_this) {
    //其实我们传过来的所有this对象他们都是有一个共同的父级,也就是shop-group-item这个div标签
    //上面有个对于总复选框进行判断的一个点击事件,之所以没有使用这个方法的原因是,他和shop-group-item这个div标签是平级的,所以无法获取到这个DOM对象,所以就把他单拎出来了
    let item = _this.closest('.shop-group-item');
    //获取到当前this对象对应的shop-group-item下的所有复选框
    let check = item.querySelectorAll('.shop-info .check');

    //初始化总价
    let totalCash = 0;
    //遍历求和
    for (let j = 0; j < check.length; j++) {
        //获取每一个的单价
        let money = parseFloat(check[j].closest('.shop-info').querySelector('.price').innerText);
        //获取每一个的数量
        let count = parseInt(check[j].closest('.shop-info').querySelector('.num').value);
        //判断是否被选中,被选中则进入计算
        if (check[j].checked) {
            totalCash += money * count;
        }
    }
    //将总价添加到本店总计里面
    item.querySelector('.shopPrice .shop-total-amount').innerText = totalCash;
}

这里取巧的就是利用他操作的每一个复选框他都有一个共同的父级元素,然后再对父级元素进行操作。

1.3.3 购物项复选框选中的处理

前端学习——JavaScript原生实现购物车案例

通过上面的分析,我们可以先获取所有的购物项对象,然后遍历购物项对象,并且将其遍历绑定点击事件

下面是代码

//通过单个点击获取到所有,获取到所有的购物项
let checks = document.querySelectorAll('.shop-group-item ul li .check');
//遍历所有购物项,目的是给所有购物项里面的某一个购物项绑定点击事件,然后获取他的唯一父元素
for (let i = 0; i < checks.length; i++) {
    //绑定点击事件,你点击哪一个,咱就能获取到哪一个的商店的头复选框
    checks[i].onclick = function () {
        //通过选中的复选框找到当前节点下item
        let item = this.closest('.shop-group-item');
        console.log(item);
        //再通过item来获取到当前节点下有多少个复选框
        let check = item.querySelectorAll('ul li .check');
        console.log(check.length);
        //通过item获取到当前节点下的总复选框
        let checkTotal = item.querySelector('.shop-name .check');
        console.log(checkTotal);
        //这里相当于是一个监听的效果,每点击一次,就会监听当前点击对象对应的父对象下面的子复选框是否全部被选中
        //说白了也就是判断当前点击元素平级的复选框是否全部被选中
        let checkNum = 0;
        for (let j = 0; j < check.length; j++) {
            //如果被选中一个,那么checkNum就会自动+1
            if (check[j].checked) {
                checkNum += 1;
            }
        }
        //如果当前点击元素平级的复选框被选中的值和该数组的长度一样的话,就让当前商店的头复选框选中
        checkTotal.checked = checkNum === check.length;

        //这里也相当于是一个监听的效果,每点击一次监听一下所有的购物项的复选框是否被选中
        let allCheckNum = 0;
        for (let j = 0; j < checks.length; j++) {
            //如果被选中的话就+1
            if (checks[j].checked) {
                allCheckNum += 1;
            }
        }
        console.log('allCheckNum:' + allCheckNum);
        //如果当前所有购物项被选中的复选框和所有购物项的复选框长度一致的话就让总的复选框选中,否则则不会被选中
        allChecked.checked = allCheckNum === checks.length;

        //通过传入一个this对象进行求和,后面在这个函数里面会统一解决这个this对象
        getSumByCheck(this);

        //求总和
        getSum();
    }
}

以上问题解决的是点击购物项的按钮复选框就会监听当前对应的商店的总复选框下的所以购物项的复选框是否全部被选中,如果全部被选中,则将每个商店的总复选框选中

1.3.4 商店复选框选中的处理

商店复选框的处理也是也不是很难,大概也就是点击之后有个监听的效果,点击一次判断一次,某一个商店的复选框被选中后,他的购物项也会被选中,所有商店的复选框被选中后,总的复选框也会被选中

//获取到每一个商店的头复选框
let checkAll = document.querySelectorAll('.shop-name .check');
//获取总复选框
let allChecked = document.querySelector('#AllCheck');
//遍历每一个商店的头复选框
for (let i = 0; i < checkAll.length; i++) {
    //给每一个商店的头复选框绑定点击事件
    checkAll[i].onclick = function () {
        //先通过父节点寻找到外层的div
        let item = checkAll[i].closest('.shop-group-item');
        //在通过外层的div找到ul下的所有复选框
        let check = item.querySelectorAll('.shop-group-item ul li .check');
        //遍历这个商店底下的所有复选框
        for (let j = 0; j < check.length; j++) {
            //如果这个商店下的所有复选框都被选中,那么这个商店的头复选框也被选中
            check[j].checked = this.checked;
        }
        //判断总的复选框按钮
        let num = 0;
        //遍历所有商店的头复选框
        for (let j = 0; j < checkAll.length; j++) {
            //判断头复选框是否被选中,如果被选中的话就让num+1
            if (checkAll[j].checked) {
                num += 1;
            }
        }
        //如果num和所有商店的头复选框数组长度一样的话,就返回true,然后总计按钮就是被选中的
        allChecked.checked = num === checkAll.length;

        //传入当前的对象,进行求和
        getSumByCheck(this);

        //求总和
        getSum();
    }
}

以上代码是解决了某一个商店的复选框被选中后,他的购物项也会被选中,所有商店的复选框被选中后,总的复选框也会被选中,并进行了求和,求和会在后面的代码中写出

1.3.5 总复选框的处理

总复选框实现的需求就是点击总复选框的时候,他会选择上面的所有复选框,上面所有复选框被选中的时候,总复选框也会被选中

实现的大概就是这样一个效果

//对总复选框进行绑定一个点击事件
allChecked.onclick = function () {
    //遍历除总复选框以外的所有复选框
    for (let i = 0; i < allCheck.length; i++) {
        //这里是如果总复选框被选中的话,那么所有复选框也会被选中
        allCheck[i].checked = this.checked;

        //下面的操作是为了当总复选框被选中后,计算每个商店的总计值,然后添加到每个商店的总计数量里面去
        //我本来想给他封装成一个方法的,因为之前的所有求和都是封装成方法了,但是这个this指向的问题,没有办法把这个和上面那些求和方法封装在一个方法里面
        //所以如果为了这一个计算而把他单拎出来封装成方法的话,没有太大的必要,所以就写在这个点击事件里面了

        //获取商店复选框的shop-group-item父节点
        let item = allCheck[i].closest('.shop-group-item');
        //然后通过父节点获取每一个商店下面的复选框,这里面是获取所有的除商店复选框以外的所有复选框
        //因为我们需要通过这个对象去获取当前购物项的价格和数量,并进行计算
        let check = item.querySelectorAll('.shop-info .check');

        //判断总的复选框是否被选中,被选中则进入计算,不被选中则不会进入计算
        if (this.checked) {
            //初始化每个商店的总价
            let totalCash = 0;
            //遍历
            for (let j = 0; j < check.length; j++) {
                //通过上面获取到的购物项的复选框去找每个购物项的价格
                let money = parseFloat(check[j].closest('.shop-info').querySelector('.price').innerText);
                //通过上面获取到的购物项的复选框去找每个购物项的数量
                let count = parseInt(check[j].closest('.shop-info').querySelector('.num').value);
                //判断购物项的复选框是否被选中,如果被选中则进入计算
                if (check[j].checked) {
                    totalCash += money * count;
                }
            }
            //将总价添加到本店总计里面
            item.querySelector('.shopPrice .shop-total-amount').innerText = totalCash;
        }else {
            //如果总复选框没有被选中的话,则让所有的购物项的值都变成0
            item.querySelector('.shopPrice .shop-total-amount').innerText = 0;
        }
    }
    //求总价
    getSum();
}

以上求和方法我是单独拎出来求和的,这个选择可能不是很明智,因为它的代码和上面传参的求和函数的代码是差不多的,不过是传入的this指向有些问题,因为他们没有共同的父级元素,只有一个body,对于body操作就有点不大好了,所以我就把他单拎出来了,这是我目前能想到的唯一方法,大家有什么好的方法也可以多多提出

1.4 JS代码

window.onload = function () {
    /*计算value值*/
    //获取减号按钮
    let minus = document.querySelectorAll('.shop-group-item .shop-info .shop-price .minus');
    //获取加号按钮
    let plus = document.querySelectorAll('.shop-group-item .shop-info .shop-price .plus');
    for (let i = 0; i < plus.length; i++) {
        /*加法的计算*/
        plus[i].onclick = function () {
            //通过点击的元素获取到value值
            let num = parseInt(this.parentElement.querySelector('.num').value);
            console.log(num);
            this.parentElement.querySelector('.num').value = num + 1;
            //获取到当前节点下的所有复选框
            getSumByCheck(this);
            getSum();
        }
        /*减法计算*/
        minus[i].onclick = function () {
            //通过点击的元素获取到value值
            let num = parseInt(this.parentElement.querySelector('.num').value);
            console.log(num);
            //判断如果num的最小值为0
            if (num > 0) {
                this.parentElement.querySelector('.num').value = num - 1;
            }
            //获取到当前节点下的所有复选框
            getSumByCheck(this);
            getSum();
        }
    }

    //获取到每一个商店的头复选框
    let checkAll = document.querySelectorAll('.shop-name .check');
    //获取总复选框
    let allChecked = document.querySelector('#AllCheck');
    //遍历每一个商店的头复选框
    for (let i = 0; i < checkAll.length; i++) {
        //给每一个商店的头复选框绑定点击事件
        checkAll[i].onclick = function () {
            //先通过父节点寻找到外层的div
            let item = checkAll[i].closest('.shop-group-item');
            //在通过外层的div找到ul下的所有复选框
            let check = item.querySelectorAll('.shop-group-item ul li .check');
            //遍历这个商店底下的所有复选框
            for (let j = 0; j < check.length; j++) {
                //如果这个商店下的所有复选框都被选中,那么这个商店的头复选框也被选中
                check[j].checked = this.checked;
            }
            //判断总的复选框按钮
            let num = 0;
            //遍历所有商店的头复选框
            for (let j = 0; j < checkAll.length; j++) {
                //判断头复选框是否被选中,如果被选中的话就让num+1
                if (checkAll[j].checked) {
                    num += 1;
                }
            }
            //如果num和所有商店的头复选框数组长度一样的话,就返回true,然后总计按钮就是被选中的
            allChecked.checked = num === checkAll.length;

            //传入当前的对象,进行求和
            getSumByCheck(this);

            //求总和
            getSum();
        }
    }

    //通过单个点击获取到所有,获取到所有的购物项
    let checks = document.querySelectorAll('.shop-group-item ul li .check');
    //遍历所有购物项,目的是给所有购物项里面的某一个购物项绑定点击事件,然后获取他的唯一父元素
    for (let i = 0; i < checks.length; i++) {
        //绑定点击事件,你点击哪一个,咱就能获取到哪一个的商店的头复选框
        checks[i].onclick = function () {
            //通过选中的复选框找到当前节点下item
            let item = this.closest('.shop-group-item');
            console.log(item);
            //再通过item来获取到当前节点下有多少个复选框
            let check = item.querySelectorAll('ul li .check');
            console.log(check.length);
            //通过item获取到当前节点下的总复选框
            let checkTotal = item.querySelector('.shop-name .check');
            console.log(checkTotal);
            //这里相当于是一个监听的效果,每点击一次,就会监听当前点击对象对应的父对象下面的子复选框是否全部被选中
            //说白了也就是判断当前点击元素平级的复选框是否全部被选中
            let checkNum = 0;
            for (let j = 0; j < check.length; j++) {
                //如果被选中一个,那么checkNum就会自动+1
                if (check[j].checked) {
                    checkNum += 1;
                }
            }
            //如果当前点击元素平级的复选框被选中的值和该数组的长度一样的话,就让当前商店的头复选框选中
            checkTotal.checked = checkNum === check.length;

            //这里也相当于是一个监听的效果,每点击一次监听一下所有的购物项的复选框是否被选中
            let allCheckNum = 0;
            for (let j = 0; j < checks.length; j++) {
                //如果被选中的话就+1
                if (checks[j].checked) {
                    allCheckNum += 1;
                }
            }
            console.log('allCheckNum:' + allCheckNum);
            //如果当前所有购物项被选中的复选框和所有购物项的复选框长度一致的话就让总的复选框选中,否则则不会被选中
            allChecked.checked = allCheckNum === checks.length;

            //通过传入一个this对象进行求和,后面在这个函数里面会统一解决这个this对象
            getSumByCheck(this);

            //求总和
            getSum();
        }
    }
    //通过点击总价获得选中所有复选框
    //获取除总复选框以外的所有复选框
    let allCheck = document.querySelectorAll('.shop-group-item .check');

    //对总复选框进行绑定一个点击事件
    allChecked.onclick = function () {
        //遍历除总复选框以外的所有复选框
        for (let i = 0; i < allCheck.length; i++) {
            //这里是如果总复选框被选中的话,那么所有复选框也会被选中
            allCheck[i].checked = this.checked;

            //下面的操作是为了当总复选框被选中后,计算每个商店的总计值,然后添加到每个商店的总计数量里面去
            //我本来想给他封装成一个方法的,因为之前的所有求和都是封装成方法了,但是这个this指向的问题,没有办法把这个和上面那些求和方法封装在一个方法里面
            //所以如果为了这一个计算而把他单拎出来封装成方法的话,没有太大的必要,所以就写在这个点击事件里面了

            //获取商店复选框的shop-group-item父节点
            let item = allCheck[i].closest('.shop-group-item');
            //然后通过父节点获取每一个商店下面的复选框,这里面是获取所有的除商店复选框以外的所有复选框
            //因为我们需要通过这个对象去获取当前购物项的价格和数量,并进行计算
            let check = item.querySelectorAll('.shop-info .check');

            //判断总的复选框是否被选中,被选中则进入计算,不被选中则不会进入计算
            if (this.checked) {
                //初始化每个商店的总价
                let totalCash = 0;
                //遍历
                for (let j = 0; j < check.length; j++) {
                    //通过上面获取到的购物项的复选框去找每个购物项的价格
                    let money = parseFloat(check[j].closest('.shop-info').querySelector('.price').innerText);
                    //通过上面获取到的购物项的复选框去找每个购物项的数量
                    let count = parseInt(check[j].closest('.shop-info').querySelector('.num').value);
                    //判断购物项的复选框是否被选中,如果被选中则进入计算
                    if (check[j].checked) {
                        totalCash += money * count;
                    }
                }
                //将总价添加到本店总计里面
                item.querySelector('.shopPrice .shop-total-amount').innerText = totalCash;
            }else {
                //如果总复选框没有被选中的话,则让所有的购物项的值都变成0
                item.querySelector('.shopPrice .shop-total-amount').innerText = 0;
            }
        }
        //求总价
        getSum();
    }

    /*总价求和*/
    function getSum() {
        //先获取每个info
        let infos = document.querySelectorAll('.shop-group-item .shop-info');
        //设置总价格
        let totalCash = 0;
        for (let i = 0; i < infos.length; i++) {
            //再获取对应个info下面的价格
            let money = parseFloat(infos[i].querySelector('.price').innerText);
            //console.log(money);
            //获取对应info下面的value值
            let value = parseInt(infos[i].querySelector('.num').value);
            //console.log(value);
            //获取到复选框对象
            let check = infos[i].querySelector('.check');
            //如果被选中的话就进行计算
            if (check.checked) {
                totalCash += money * value;
            }
            /*infos[i].closest('.shop-group-item').querySelector('.shopPrice .shop-total-amount').innerText = totalCash;*/
        }
        document.querySelector('.payment-bar .total').innerText = totalCash;
        //将总价放在每一个总计标签里面
    }

    /*根据check子节点求和*/
    function getSumByCheck(_this) {
        //其实我们传过来的所有this对象他们都是有一个共同的父级,也就是shop-group-item这个div标签
        //上面有个对于总复选框进行判断的一个点击事件,之所以没有使用这个方法的原因是,他和shop-group-item这个div标签是平级的,所以无法获取到这个DOM对象,所以就把他单拎出来了
        let item = _this.closest('.shop-group-item');
        //获取到当前this对象对应的shop-group-item下的所有复选框
        let check = item.querySelectorAll('.shop-info .check');

        //初始化总价
        let totalCash = 0;
        //遍历求和
        for (let j = 0; j < check.length; j++) {
            //获取每一个的单价
            let money = parseFloat(check[j].closest('.shop-info').querySelector('.price').innerText);
            //获取每一个的数量
            let count = parseInt(check[j].closest('.shop-info').querySelector('.num').value);
            //判断是否被选中,被选中则进入计算
            if (check[j].checked) {
                totalCash += money * count;
            }
        }
        //将总价添加到本店总计里面
        item.querySelector('.shopPrice .shop-total-amount').innerText = totalCash;
    }
}

里面的注释其实挺全的,代码的每一步都有注释,大家看起来也方便

到这里基本所有的代码都写完了,其实也不是很多,逻辑也不是很复杂,就是一些细节处理方面可能比较麻烦。

1.5 CSS代码

base.css

@charset "utf-8";
html,body,div,p,form,label,ul,li,dl,dt,dd,ol,img,button,b,em,strong,small,h1,h2,h3,h4,h5,h6{margin:0;padding:0;border:0;list-style:none;font-style:normal;}
body{font-family:SimHei,'Helvetica Neue',Arial,'Droid Sans',sans-serif;font-size:14px;color:#333;background:#f2f2f2;}
a, a.link{color:#666;text-decoration:none;font-weight:500;}
a, a.link:hover{color:#666;}
h1,h2,h3,h4,h5,h6{font-weight: normal;}
/*头部开始*/
.header{position:relative;width:100%;height:44px;background:#fff;border-bottom:1px solid #e0e0e0;}
.header h1{font-size:16px;color:#333;height:44px;line-height:44px;display:block;text-align:center;}
.header a{position: absolute;top:0;display:block;height:44px;line-height:44px;}
.header a.back{left:0px;}
.header a.back span{display:inline-block;width:25px;height:25px;margin:10px 5px;background: url("../img/icon/icon-back.png") no-repeat;background-size:100%;}
.header .home{}
/*头部结束*/

input[type="checkbox"]{-webkit-appearance:none;outline: none;}
input.check{background:url(../img/icon/icon_radio3.png) no-repeat center left;background-size:20px 20px;position:absolute;top:50%;left:10px;margin-top:-18px;width:20px;height:35px;}
input.check:checked{background:url(../img/icon/icon_radio4.png) no-repeat center left;background-size:20px 20px;}
input.goodsCheck:checked{background:url(../img/icon/icon_radio4.png) no-repeat center left;background-size:20px 20px;}
input.check:checked{background:url(../img/icon/icon_radio4.png) no-repeat center left;background-size:20px 20px;}
.checked{background:url(../img/icon/icon_radio4.png) no-repeat left center;background-size:20px 20px;position:absolute;top:50%;left:15px;margin-top:-18px;width:20px;height:35px;}

/*尾部开始*/
.footer .copyright{height:44px;line-height:44px;text-align:center;color:#848689;font-size:12px;}
/*尾部结束*/

module.css

@charset "utf-8";
/* CSS Document */
/*购物车*/
.shopping{clear:both;overflow:hidden;height:auto;padding-bottom: 60px;}
.shop-group-item{margin-bottom:5px;}
.shop-group-item ul li{border-bottom:1px solid #fff;}
.shop-group-item ul li:last-child{border-bottom:none;}

.shop-name{background:#fff;height:35px;line-height:35px;padding:0 15px;position:relative;}
.shop-name h4{float:left;font-size:14px;background:url(../img/icon/icon-kin.png) no-repeat left center;background-size:20px 20px;padding-left:25px;margin-left: 28px;}
.shop-name .coupons{float:right;}
.shop-name .coupons span{display:inline-block;padding:0 5px;}
.shop-name .coupons em{color:#e0e0e0;}

.shop-info{background:#f5f5f5;height:120px;padding:0 15px;position:relative;}
.shop-info .checkbox{background:url(../img/icon/icon_radio3.png) no-repeat left center;background-size:20px 20px;position:absolute;top:50%;left:15px;margin-top:-60px;width:20px;height:120px;}
.shop-info .checkbox1{background:url(../img/icon/icon_radio4.png) no-repeat left center;background-size:20px 20px;position:absolute;top:50%;left:15px;margin-top:-60px;width:20px;height:120px;}
.shop-info .shop-info-img{position:absolute;top:15px;left:45px;width:90px;height:90px;}
.shop-info .shop-info-img img{width:100%;height:100%;}
.shop-info .shop-info-text{margin-left:130px;padding:15px 0;}
.shop-info .shop-info-text h4{font-size:14px;display:-webkit-box;-webkit-box-orient:vertical;-webkit-line-clamp:2;overflow: hidden;}
.shop-info .shop-info-text .shop-brief{height:25px;line-height:25px;font-size:12px;color:#81838e;white-space:nowrap;}
.shop-info .shop-info-text .shop-brief span{display:inline-block;margin-right:8px;}
.shop-info .shop-info-text .shop-price{height:24px;line-height:24px;position:relative;}
.shop-info .shop-info-text .shop-price .shop-pices {color:red;font-size:16px;}
.shop-info .shop-info-text .shop-arithmetic{position:absolute;right:0px;top:0;width:84px;box-sizing:border-box;white-space:nowrap;height:100%;border:1px solid #e0e0e0;}
.shop-info .shop-info-text .shop-arithmetic a{display:inline-block;width:23px;height:22px;line-height:22px;text-align:center;background:#fff;font-size:16px;}
.shop-info .shop-info-text .shop-arithmetic .minus{border-right:1px solid #e0e0e0;}
.shop-info .shop-info-text .shop-arithmetic .failed{color:#d1d1d1;}
.shop-info .shop-info-text .shop-arithmetic .plus{border-left:1px solid #e0e0e0;}
.shop-info .shop-info-text .shop-arithmetic .num{width:32px;text-align:center;border:none;display: inline-block;height:100%;box-sizing:border-box;vertical-align:top;margin:0 -6px;}
.shopPrice{background:#fff;height:35px;line-height:35px;padding:0 15px;text-align:right;}
.shopPrice span{color:#f00;}




.payment-bar{clear:both;overflow:hidden;width:100%;height:49px;position:fixed;bottom:0;border-top:1px solid #e0e0e0;background:#fff;}
.payment-bar .all-checkbox{float:left;line-height:49px;padding-left:40px;}
.payment-bar .shop-total{float:left;-webkit-box-flex:1.0;box-flex:1.0;margin:9px 20px 9px 35px;}
.payment-bar .shop-total strong{display:block;font-size:16px;}
.payment-bar .shop-total span{display:block;font-size:12px;}
.payment-bar .settlement{display:inline-block;float:right;width:100px;height:49px;line-height:49px;text-align:center;color:#fff;font-size:16px;background:#f23030;}

这里面的样式和HTML结构都不是我写的,我只写了JS的代码,大家有需要可以自提,然后练习对DOM对象的操作

二. 总结

以上便是这篇博文的全部内容,主要内容就是对购物车案例升级的编写,和上一篇写的不同点在于,这篇博文将购物车里面进行了分类处理,分类处理每一个商店里面的每一个购物项,主要难点在于对DOM对象的操作,调用父级DOM对象和通过父级的DOM对象再次调用子级的DOM对象,相当于是一个DOM树之间的互相调用的处理方式,这两天的博客都是一些案例,案例主要练习的也就是对JavaScript中DOM对象的操作练习。

以上就是全部内容,我只写了JS代码,HTML和CSS都是案例的提供的文件,有时间的话我也会尝试着将整个案例完整的写下来,以上JS代码中,对于逻辑的处理是我目前想到的还不错的写法,可能代码中还会存在着一些没有发现的问题,大家看了之后发现有什么错误,或者看完之后有什么更好的想法也可以多多提出,大家一起学习,共同进步!!!

Javascript 相关文章推荐
jQuery.each()用法分享
Jul 31 Javascript
js简易namespace管理器 实例代码
Jun 21 Javascript
jquery获取当前元素索引值用法实例
Jun 10 Javascript
javascript实现下班倒计时效果的方法(可桌面通知)
Jul 10 Javascript
Javascript中常用类型的格式化方法小结
Dec 26 Javascript
微信小程序 选项卡的简单实例
May 24 Javascript
十大 Node.js 的 Web 框架(快速提升工作效率)
Jun 30 Javascript
微信小程序实现通过js操作wxml的wxss属性示例
Dec 06 Javascript
JS中getElementsByClassName与classList兼容性问题解决方案分析
Aug 07 Javascript
通过vue写一个瀑布流插件代码实例
Sep 07 Javascript
vue实现二级导航栏效果
Oct 19 Javascript
JS实现网页时钟特效
Mar 25 Javascript
JavaScript中关于预编译、作用域链和闭包的理解
JavaScript 去重和重复次数统计
Mar 31 #Javascript
vue中三级导航的菜单权限控制
Mar 31 #Vue.js
jQuery class属性操作addClass()与removeClass()、hasClass()、toggleClass()
vue3中的组件间通信
vue前端工程的搭建
JS数组的常用方法整理
Mar 31 #Javascript
You might like
php递归json类实例
2014/12/02 PHP
Yii框架中memcache用法实例
2014/12/03 PHP
Laravel jwt 多表(多用户端)验证隔离的实现
2019/12/18 PHP
struts2+jquery+json实现异步加载数据(自写)
2013/06/24 Javascript
JavaScript字符串对象的concat方法实例(用于连接两个或多个字符串)
2014/10/16 Javascript
EasyUI中实现form表单提交的示例分享
2015/03/01 Javascript
JS实现控制表格只显示行边框或者只显示列边框的方法
2015/03/31 Javascript
js+HTML5实现canvas多种颜色渐变效果的方法
2015/06/05 Javascript
jQuery插件实现带圆点的焦点图片轮播切换
2016/01/18 Javascript
Bootstrap 粘页脚效果
2016/03/28 Javascript
Javascript函数中的arguments.callee用法实例分析
2016/09/16 Javascript
vue2.0使用swiper组件实现轮播的示例代码
2018/03/03 Javascript
Vue中 v-if/v-show/插值表达式导致闪现的原因及解决办法
2018/10/12 Javascript
使用webpack打包后的vue项目如何正确运行(express)
2018/10/26 Javascript
微信小程序实现单选选项卡切换效果
2020/06/19 Javascript
史上最为详细的javascript继承(推荐)
2019/05/18 Javascript
nodejs使用socket5进行代理请求的实现
2020/02/21 NodeJs
[50:01]Ti4 冒泡赛第二天 NEWBEE vs Titan
2014/07/15 DOTA
Python存取XML的常见方法实例分析
2017/03/21 Python
python3+PyQt5+Qt Designer实现堆叠窗口部件
2018/04/20 Python
python模块导入的细节详解
2018/12/10 Python
python opencv 读取本地视频文件 修改ffmpeg的方法
2019/01/26 Python
python中with用法讲解
2020/02/07 Python
如何用python处理excel表格
2020/06/09 Python
利用html5 canvas动态画饼状图的示例代码
2018/04/02 HTML / CSS
使用HTML5的链接预取功能(link prefetching)给网站提速
2012/12/13 HTML / CSS
Bealls Florida百货商店:生活服饰、家居装饰和鞋子
2018/02/23 全球购物
入党自我评价范文
2014/02/02 职场文书
《邮票齿孔的故事》教学反思
2014/02/22 职场文书
一份文言文检讨书
2014/09/13 职场文书
医院志愿者活动总结
2015/05/06 职场文书
导游词之秦皇岛燕塞湖
2020/01/03 职场文书
Python-typing: 类型标注与支持 Any类型详解
2021/05/10 Python
如何在向量化NumPy数组上进行移动窗口
2021/05/18 Python
Django中session进行权限管理的使用
2021/07/09 Python
Python线程池与GIL全局锁实现抽奖小案例
2022/04/13 Python