前端学习——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 选择和过滤方法代码总结
Nov 19 Javascript
javascript自动改变文字大小和颜色的效果的小例子
Aug 02 Javascript
js获取指定的cookie的具体实现
Feb 20 Javascript
基于JS实现PHP的sprintf函数实例
Nov 14 Javascript
JavaScript类型系统之基本数据类型与包装类型
Jan 06 Javascript
JavaScript ES6中CLASS的使用详解
Nov 22 Javascript
Bootstrap输入框组件简单实现代码
Mar 06 Javascript
微信小程序实战之登录页面制作(5)
Mar 30 Javascript
详解BootStrap表单验证中重置BootStrap-select验证提示不清除的坑
Sep 17 Javascript
Vue利用Blob下载原生二进制数组文件
Sep 25 Javascript
typescript配置alias的详细步骤
Aug 12 Javascript
原生JS实现弹幕效果的简单操作指南
Nov 10 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过滤敏感词的示例
2014/03/31 PHP
php访问数组最后一个元素的函数end()用法
2015/03/18 PHP
php实现图片上传并利用ImageMagick生成缩略图
2016/03/14 PHP
PHP之十六个魔术方法详细介绍
2016/11/01 PHP
thinkPHP5.0框架API优化后的友好性分析
2017/03/17 PHP
PHP实现执行外部程序的方法详解
2017/08/17 PHP
Yii2.0实现的批量更新及批量插入功能示例
2019/01/29 PHP
JavaScript窗口功能指南之在窗口中书写内容
2006/07/21 Javascript
JavaScript 学习小结(适合新手参考)
2009/07/30 Javascript
Nodejs关于gzip/deflate压缩详解
2015/03/04 NodeJs
通过隐藏iframe实现无刷新上传文件操作
2016/03/16 Javascript
jQuery获取cookie值及删除cookie用法实例
2016/04/15 Javascript
JS弹出层遮罩,隐藏背景页面滚动条细节优化分析
2016/04/29 Javascript
微信小程序 地图定位简单实例
2016/10/14 Javascript
bootstrap Validator 模态框、jsp、表单验证 Ajax提交功能
2017/02/17 Javascript
vue封装第三方插件并发布到npm的方法
2017/09/25 Javascript
react-native android状态栏的实现
2018/06/15 Javascript
小程序getLocation需要在app.json中声明permission字段
2019/04/04 Javascript
JavaScript+HTML5 canvas实现放大镜效果完整示例
2019/05/15 Javascript
通过vue写一个瀑布流插件代码实例
2019/09/07 Javascript
解决layer.confirm快速点击会重复触发事件的问题
2019/09/23 Javascript
[02:56]《DAC最前线》之国外战队抵达上海备战亚洲邀请赛
2015/01/28 DOTA
[00:33]DOTA2上海特级锦标赛 CDEC战队宣传片
2016/03/04 DOTA
[28:05]完美世界DOTA2联赛循环赛Inki vs DeMonsTer 第一场 10月30日
2020/10/31 DOTA
python求crc32值的方法
2014/10/05 Python
python图像处理之镜像实现方法
2015/05/30 Python
用python3教你任意Html主内容提取功能
2018/11/05 Python
Pytorch 实现数据集自定义读取
2020/01/18 Python
python 使用事件对象asyncio.Event来同步协程的操作
2020/05/04 Python
Python如何把字典写入到CSV文件的方法示例
2020/08/23 Python
html5音频_动力节点Java学院整理
2018/08/22 HTML / CSS
Timex手表官网:美国运动休闲手表品牌
2017/01/28 全球购物
大学生个人总结的自我评价
2013/10/05 职场文书
2016年感恩节寄语
2015/12/07 职场文书
Jupyter notebook 不自动弹出网页的解决方案
2021/05/21 Python
Nginx开源可视化配置工具NginxConfig使用教程
2022/06/21 Servers