前端学习——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 相关文章推荐
JavaScript 语言的递归编程
May 18 Javascript
JavaScript的类型简单说明
Sep 03 Javascript
汉化英文版的Dreamweaver CS5并自动提示jquery
Nov 25 Javascript
Jquery chosen动态设置值实例介绍
Aug 08 Javascript
JavaScript中的工厂函数(推荐)
Mar 08 Javascript
react高阶组件经典应用之权限控制详解
Sep 07 Javascript
jQuery实现判断上传图片类型和大小的方法示例
Apr 11 jQuery
AngularJS使用$http配置对象方式与服务端交互方法
Aug 13 Javascript
js嵌套的数组扁平化:将多维数组变成一维数组以及push()与concat()区别的讲解
Jan 19 Javascript
javascript实现对话框功能警告(alert 消息对话框)确认(confirm 消息对话框)
May 07 Javascript
新手如何快速理解js异步编程
Jun 24 Javascript
vue开发简单上传图片功能
Jun 30 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接收App端发送文件流的方法
2016/09/23 PHP
PHP字符串和十六进制如何实现互相转换
2020/07/16 PHP
JavaScript使用过程中需要注意的地方和一些基本语法
2010/08/26 Javascript
javascript (用setTimeout而非setInterval)
2011/12/28 Javascript
基于mouseout和mouseover等类似事件的冒泡问题解决方法
2013/11/18 Javascript
asm.js使用示例代码
2013/11/28 Javascript
js/jQuery简单实现选项卡功能
2014/01/02 Javascript
js使用递归解析xml
2014/12/12 Javascript
JavaScript实现三阶幻方算法谜题解答
2014/12/29 Javascript
js实现浏览器窗口大小被改变时触发事件的方法
2015/02/02 Javascript
jquery图片切换插件
2015/03/16 Javascript
jQuery中hover与mouseover和mouseout的区别分析
2015/12/24 Javascript
JS中如何实现点击a标签返回页面顶部的问题
2017/01/19 Javascript
Angularjs中使用轮播图指令swiper
2017/05/30 Javascript
vue使用vue-cli快速创建工程
2017/07/28 Javascript
angular或者js怎么确定选中ul中的哪几个li
2017/08/16 Javascript
解决vue单页使用keep-alive页面返回不刷新的问题
2018/03/13 Javascript
Vue2.0学习系列之项目上线的方法步骤(图文)
2018/09/25 Javascript
小程序实现授权登陆的解决方案
2018/12/02 Javascript
JavaScript数组、json对象、eval()函数用法实例分析
2019/02/21 Javascript
巧用Python装饰器 免去调用父类构造函数的麻烦
2012/05/18 Python
Fiddler如何抓取手机APP数据包
2016/01/22 Python
python实现远程通过网络邮件控制计算机重启或关机
2018/02/22 Python
pyqt5 禁止窗口最大化和禁止窗口拉伸的方法
2019/06/18 Python
python计算二维矩形IOU实例
2020/01/18 Python
快速解决pymongo操作mongodb的时区问题
2020/12/05 Python
HTML5 Canvas之测试浏览器是否支持Canvas的方法
2015/01/01 HTML / CSS
Myprotein意大利官网:欧洲第一运动营养品牌
2018/11/22 全球购物
全国优秀辅导员事迹材料
2014/05/14 职场文书
红色旅游心得体会
2014/09/03 职场文书
2014中学教师节广播稿
2014/09/10 职场文书
2014年纪委工作总结
2014/12/05 职场文书
高三复习计划
2015/01/19 职场文书
大学生党课心得体会
2016/01/07 职场文书
《普罗米修斯》教学反思
2016/02/22 职场文书
解决numpy数组互换两行及赋值的问题
2021/04/17 Python