前端学习——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实现仿银行密码输入框效果的代码
Dec 13 Javascript
鼠标划过实现延迟加载并隐藏层的js代码
Oct 11 Javascript
JQuery中DOM事件合成用法实例分析
Jun 13 Javascript
jQuery实现鼠标悬停背景翻转的黑色导航菜单代码
Sep 14 Javascript
基于jQuery和hwSlider实现内容左右滑动切换效果附源码下载(一)
Jun 22 Javascript
jQuery自定义组件(导入组件)
Nov 08 Javascript
JavaScript闭包_动力节点Java学院整理
Jun 27 Javascript
原生JS实现逼真的图片3D旋转效果详解
Feb 16 Javascript
JavaScript定时器设置、使用与倒计时案例详解
Jul 08 Javascript
layui 实现二级弹窗弹出之后 关闭一级弹窗的方法
Sep 18 Javascript
详解JavaScript中的数据类型,以及检测数据类型的方法
Sep 17 Javascript
浅析JavaScript中的变量提升
Jun 01 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配置参数总结
2013/06/14 PHP
php单一接口的实现方法
2015/06/20 PHP
PHP+Ajax+JS实现多图上传
2016/05/07 PHP
PHP微信红包生成代码分享
2016/10/06 PHP
PHP最常用的正则表达式
2017/02/13 PHP
PHPMailer使用QQ邮箱实现邮件发送功能
2017/08/18 PHP
JavaScript 动态添加表格行 使用模板、标记
2009/10/24 Javascript
自己动手手写jQuery插件总结
2015/01/20 Javascript
jquery-tips悬浮提示插件分享
2015/07/31 Javascript
js实现带缓冲效果的仿QQ面板折叠菜单代码
2015/09/06 Javascript
JavaScript统计网站访问次数的实现代码
2015/11/18 Javascript
jQuery无刷新上传之uploadify3.1简单使用
2016/06/18 Javascript
AngularJS基于ui-route实现深层路由的方法【路由嵌套】
2016/12/14 Javascript
在 Angular 中使用Chart.js 和 ng2-charts的示例代码
2017/08/17 Javascript
基于vue实现web端超大数据量表格的卡顿解决
2019/04/02 Javascript
Angular8基础应用之表单及其验证
2019/08/11 Javascript
Vue 动态路由的实现及 Springsecurity 按钮级别的权限控制
2019/09/05 Javascript
jquery实现直播视频弹幕效果
2020/02/25 jQuery
Python的Flask框架中Flask-Admin库的简单入门指引
2015/04/07 Python
在Python中处理字符串之ljust()方法的使用简介
2015/05/19 Python
Python2实现的图片文本识别功能详解
2018/07/11 Python
Python实现根据日期获取当天凌晨时间戳的方法示例
2019/04/09 Python
python获取点击的坐标画图形的方法
2019/07/09 Python
pandas DataFrame 警告(SettingWithCopyWarning)的解决
2019/07/23 Python
Python使用get_text()方法从大段html中提取文本的实例
2019/08/27 Python
迪卡侬荷兰官网:Decathlon荷兰
2017/10/29 全球购物
经济学人订阅:The Economist
2018/07/19 全球购物
TCP/IP中的TCP和IP分别承担什么责任
2012/04/21 面试题
应届生妇产科护士求职信
2013/10/27 职场文书
公司合作协议书范本
2014/04/18 职场文书
双方协议书
2014/04/22 职场文书
最常使用的求职信
2014/05/25 职场文书
硕士生找工作求职信
2014/07/05 职场文书
工作批评与自我批评范文
2014/10/16 职场文书
2019最新版劳务派遣管理制度
2019/08/16 职场文书
sql中mod()函数取余数的用法
2021/05/29 SQL Server