【学习笔记46】JavaScript购物车的实现

news2025/1/18 11:00:38

一、案例效果

1、将通过数据重构页面

  • 查询数据, 渲染页面
    在这里插入图片描述

2、全选

  1. 选中全选按钮后, 根据全选按钮的选中状态, 修改所有商品的选中状态
  2. 重新渲染视图

在这里插入图片描述

在这里插入图片描述

3、清空购物车

  1. 清空商品数据
  2. 重新渲染视图

在这里插入图片描述

在这里插入图片描述

4、结算

  1. 找到所有选中的商品
  2. 计算所有选中商品各自的总价
  3. 计算所有选中商品的总价之和

在这里插入图片描述

5、删除已选中

  1. 在原数组中, 找到选中的商品, 然后删除
  2. 重新渲染视图

在这里插入图片描述

在这里插入图片描述

6、商品数量调整

  1. 找到对应的商品, 修改收藏数量
  2. 重新渲染视图

在这里插入图片描述

在这里插入图片描述

7、选中商品

  1. 找到对应的商品, 修改选中状态
  2. 重新渲染视图

在这里插入图片描述

8、删除某一项

  1. 找到对应商品, 将删除
  2. 重新渲染视图

在这里插入图片描述

在这里插入图片描述

9、数据持久化 (浏览器关闭, 数据能保存)

  • 本地存储
    在这里插入图片描述

二、案例分析

1. 数组数据分析

  1. id: 数据的唯一值
  2. status: true代表该商品被选中, false则为没被选中
  3. pic: 图片地址
  4. name: 商品名
  5. price: 价格
  6. number: 商品收藏数量
  7. total: 库存

2. 数据驱动视图

  • 查: 查询数据, 渲染到页面
  • 增删改: 找到源数据, 然后对源数据做修改, 修改完成, 重新渲染页面

3. 逻辑思维

  1. 准备一个渲染函数
  2. 首次打开页面时 调用
  3. 在各种事件触发之后, 重新调用

三、html代码

		<div class="header">页面顶部</div>
		<!-- 动态生成数据 -->
		<div class="content"></div>
		<div class="footer">页面底部</div>
		<script src="./index.js"></script>

四、css代码

		* {
		    margin: 0;
		    padding: 0;
		}
		
		ul,ol,li {
		    list-style: none;
		}
		
		.header,.footer {
		    width: 1200px;
		    height: 100px;
		    background-color: skyblue;
		    color: #fff;
		    font-size: 50px;
		    display: flex;
		    justify-content: center;
		    align-items: center;
		    margin: 0 auto;
		}
		
		.footer {
		    height: 400px;
		}
		
		.content {
		    width: 1200px;
		    margin: 0 auto;
		    padding: 10px 0;
		}
		
		.content > .top,.content > .bottom {
		    height: 50px;
		    background-color: pink;
		    display: flex;
		    align-items: center;
		}
		
		.content > .bottom {
		    justify-content: space-between;
		    box-sizing: border-box;
		    padding: 0 10px;
		}
		
		.content > .bottom > .totalPrice > span {
		    font-size: 20px;
		    color: red;
		}
		
		.content > .bottom > .btns > button {
		    font-size: 18px;
		    padding: 5px 10px;
		    cursor: pointer;
		}
		
		.content > .top > input {
		    width: 30px;
		    height: 30px;
		    margin: 0 15px 0 50px;
		}
		
		.content > ul {
		    padding-top: 10px;
		}
		
		.content > ul > li {
		    width: 100%;
		    border: 1px solid #333;
		    box-sizing: border-box;
		    height: 100px;
		    margin-bottom: 10px;
		
		    display: flex;
		}
		
		.content > ul > li > div {
		    display: flex;
		    justify-content: center;
		    align-items: center;
		    border-right: 1px solid #333;
		}
		
		.content > ul > li > div:last-child {
		    border: none;
		}
		
		.content > ul > li > .show,
		.content > ul > li > .status {
		    width: 100px;
		}
		
		.content > ul > li > .status > input {
		    width: 30px;
		    height: 30px;
		}
		
		.content > ul > li > .show > img {
		    width: 100%;
		    height: 100%;
		    display: block;
		}
		
		.content > ul > li > .price,
		.content > ul > li > .sub {
		    width: 200px;
		    color: red;
		    font-size: 20px;
		}
		
		.content > ul > li > .title {
		    width: 300px;
		    align-items: flex-start;
		    justify-content: flex-start;
		    box-sizing: border-box;
		    padding: 5px;
		}
		
		.content > ul > li > .number {
		    width: 230px;
		}
		
		.content > ul > li > .number > input {
		    width: 50px;
		    height: 30px;
		    text-align: center;
		    margin: 0 5px;
		    border: none;
		    outline: none;
		    font-size: 18px;
		}
		
		.content > ul > li > .number > button {
		    width: 30px;
		    height: 30px;
		    cursor: pointer;
		}
		
		.content > ul > li > .destroy {
		    flex: 1;
		}
		
		.content > ul > li > .destroy > button {
		    padding: 5px;
		    font-size: 18px;
		    cursor: pointer;
		}

五、js代码的实现

// 获取localStorage的数据时, 为了避免首次进入页面没有数据, 所以通过逻辑或给一个本地数据兜底
var cartList = JSON.parse(window.localStorage.getItem("cartList")) || [
    // 每一个对象就是一个购物车内容的数据
    {
        id: 111234,
        status: true,
        pic: "https://img1.baidu.com/it/u=2511310783,721605137&fm=253&fmt=auto&app=138&f=JPEG?w=500&h=332",
        name: "我是一个手机, 不知道是啥",
        price: 100,
        number: 3,
        total: 16,
    },
    {
        id: 123456,
        status: false,
        pic: "https://img1.baidu.com/it/u=1537709578,2453227648&fm=253&fmt=auto&app=120&f=JPEG?w=809&h=500",
        name: "我是一个电脑, 不知道是啥",
        price: 98.72,
        number: 1,
        total: 7,
    },
    {
        id: 965874,
        status: true,
        pic: "https://img2.baidu.com/it/u=3561506717,735421650&fm=253&fmt=auto&app=138&f=JPEG?w=750&h=500",
        name: "我是一个手纸, 不知道是啥",
        price: 356.21,
        number: 2,
        total: 22,
    },
];


// 1. 获取标签对称
const oContent = document.querySelector('.content');

// 2. 封装函数 渲染页面
function myPage() {
    // 定义变量 存储数据
    let selectItem = 0;       //存储选中商品的数量
    let selectTotalNum = 0;   //存储选中商品的总数量
    let totalPrice = 0;       //存储选中商品的总价格

    // 找到选中的商品
    cartList.forEach(function (item) {
        if (item.status == true) {
            selectItem++;
            selectTotalNum += item.number;
            totalPrice += item.price * item.number;
        }
    })

    // 查找数据 渲染页面
    // 选中的商品数量 如果代表商品总量 代表所有商品被选中
    var str = `
        <div class="top">
			<input type="checkbox" class="checkAll" ${selectItem === cartList.length ? "checked" : ""}> 全选
		</div>
    `;

    cartList.forEach(function (item) {
        str += `
        <ul>
            <li>
                <div class="status">
                    <input type="checkbox" class="checkOther" data-id="${item.id}"  ${item.status ? "checked" : ""}>
                </div>
                <div class="show">
                    <img src="${item.pic}" alt="">
                </div>
                <div class="title">${item.name}</div>
                <div class="price">¥ ${item.price.toFixed(2)}</div>
                <div class="number">
                    <button class="reduce-btn"  data-id="${item.id}">-</button>
                    <input type="text" value="${item.number}">
                    <button class="increase-btn" data-id="${item.id}">+</button>
                </div>
                <div class="sub">¥ ${(item.number * item.price).toFixed(2)}</div>
                <div class="destroy">
                    <button class="del" data-id="${item.id}">删除</button>
                </div>
            </li>
        </ul>
    `;
    })

    str += `
		<div class="bottom">
        <div class="totalNum">总件数 : ${selectTotalNum}</div>
        <div class="btns">
            <button class="clearShopCart">清空购物车</button>
            <button class="payment" data-totalPrice="${totalPrice}"">去结算</button>
            <button class="delCheck">删除所有已选中</button>
        </div>
        <div class="totalPrice">
            总价格 : ¥ <span>${totalPrice.toFixed(2)}</span>
        </div>
    </div>
    `;
    oContent.innerHTML = str;

    // 将数据存储到localStorage中 便于下次进入可以获取上一次的数据
    window.localStorage.cartList = JSON.stringify(cartList);
    // window.localStorage.setItem("cartList", JSON.stringify(cartList));

}

// 调用函数 渲染页面(页面首次打开页面)
myPage()

// 3. 购物车的功能实现

// 利用事件冒泡 把事件委托给统一的父级
oContent.addEventListener('click', function (e) {
    // 全选效果
    if (e.target.className === 'checkAll') {

        cartList.forEach(function (item) {
            console.log(e.target.checked);
            item.status = e.target.checked;
        })

        // 渲染页面
        myPage()
    }


    // 清空购物车
    if (e.target.className === 'clearShopCart') {
        var warn = confirm('您确定要清空购物车吗')
        if (warn) {
            //购物车的数据为空
            cartList = [];
            // 渲染页面
            myPage()
        }
    }

    // 结算(将选择的商品总价计算打印到控制台)
    if (e.target.className === 'payment') {
        // console.log(e.target.dataset.totalprice);
        var price = e.target.dataset.totalprice;
        price = Math.round(price)
        confirm(`总价格为:${price}`)
    }

    // 删除所有已选中(没有选中时 禁止执行)
    if (e.target.className === 'delCheck') {
        var warn = confirm('您确定要删除当前选择的吗')
        if (!warn) return;
        // 过滤数组 只留下选中状态为false
        cartList = cartList.filter(function (item) {
            return !item.status
        })

        // 渲染页面
        myPage()
    }

    // 减少商品的数量
    if (e.target.className === 'reduce-btn') {
        // 减少商品数量 不能为0
        cartList.forEach(function (item) {
            // 通过商品的ID找到点击的是哪一个商品 累减操作
            if (item.id == e.target.dataset.id && item.number >= 2) item.number--;
        })

        // 渲染页面
        myPage();
    }

    // 增加商品的数量
    if (e.target.className === 'increase-btn') {
        cartList.forEach(function (item) {
            // 通过商品的ID找到点击的是哪一个商品 累加操作
            if (item.id == e.target.dataset.id && item.number < item.total) item.number++;
        })

        // 渲染页面
        myPage();
    }

    // 选中商品
    if (e.target.className === 'checkOther') {
        // 遍历数组找到要修改的商品
        cartList.forEach(function (item) {
            if (item.id == e.target.dataset.id) {
                // 修改商品的选中状态
                item.status = !item.status;
            }
        })
        // 渲染页面
        myPage()
    }

    // 删除某一项商品
    if (e.target.className === 'del') {
        var warn = confirm('您确定要删除当前的商品吗');
        if(!warn) return;
        // 遍历数组 找到需要删除的项 将其过滤
        cartList = cartList.filter(function(item){
            return item.id != e.target.dataset.id;
        })
        // 渲染页面
        myPage()
    }
})

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/41458.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【MySQL】MVCC原理分析 + 源码解读 -- 必须说透

文章目录前言一、MVCC 介绍二、MySQL MVCC 介绍三、MySQL MVCC实现原理源码分析3.1 隐式字段源码验证3.2 undo logundo log格式undo log源码验证写insert undo log源码写update undo log源码写undo log源码roll_ptr是如何指向insert undo log的?roll_ptr是如何指向update undo…

Thymeleaf模板

Thymeleaf可用于前后端分离&#xff0c; 下图&#xff0c;value"aa"&#xff0c; 在本地静态资源可以改变值&#xff0c;但是在web端不可以 前端可以在本地测试&#xff0c;有数据了显示数据 所以前后端分离 th属性 常用th属性解读html有的属性&#xff0c;Thymel…

集合框架----源码解读LinkedList篇

1.LinkedList官方介绍 双链表实现的list和Deque接口。实现所有可选的列表操作&#xff0c;并允许所有元素(包括null)。 所有的操作都按照双链表的预期执行。索引到列表中的操作将从列表的开始或结束遍历列表&#xff0c;以更接近指定索引的为准。 注意&#xff0c;这个实现不是…

全球价值链GVC总出口分解(2011-2014年)

1、数据来源&#xff1a;&#xff29;&#xff23;&#xff29;&#xff2f;数据库 2、时间跨度&#xff1a;2011-2014年 3、区域范围&#xff1a;世界 4、指标说明&#xff1a; 全球价值链分析(Global Value Chain analysis&#xff0c;简称GVC分析)为解决传统贸易统计中…

数据库 1.关系

从关系开始&#xff1a; Table的严格定义&#xff1a; 域就是&#xff1a;学生表{名字&#xff08;char(20),学号(int20)&#xff09;}里面的char20,int20,是用来标记列的数据类型&#xff0c;或者说取值范围的。这个取值范围有一个大小&#xff0c;这个大小就是基数。 就是每种…

世界各国自然资源租金面板数据

1、数据来源&#xff1a;世界银行《世界发展指标数据库》 2、时间跨度&#xff1a;1970-2018年 3、区域范围&#xff1a;全球 4、指标说明&#xff1a; 自然资源租金总额是石油租金、天然气租金、煤炭&#xff08;硬煤和软煤&#xff09;租金、矿产租金和森林租金之和。 …

11.20 至 11.27 五道典型题记录: 贪心 | 应用题 | 脑筋急转弯 | 区间问题 | 双指针

11.20 至 11.27 五道典型题记录&#xff1a; 贪心 | 应用题 | 脑筋急转弯 | 区间问题 | 双指针 松懈了最近&#xff0c;要时刻保持警醒啊&#xff01;学习不能停&#xff0c;说那么多的借口不如花一些心思去学一些知识&#xff0c;之所以学到的内容不成体系&#xff0c;一方面就…

【Java集合】集合是什么?为什么要用集合?

> 集合是什么&#xff1f;为什么要用集合&#xff1f; 保存数据会经常使用到数组&#xff0c;但数组存在以下几个缺陷: 长度开始时必须指定&#xff0c;且一旦指定&#xff0c;不能更改&#xff1b;保存的必须为同一类型的元素&#xff1b;使用数组进行增加元素的步骤比较麻…

MySQL 8.0 Data Dictionary显示

数据字典 对于MySQL的系统库都不会陌生&#xff0c;因为是基本框架&#xff0c;支撑着MySQL有效运行。这些系统库提供诸多功能&#xff0c;如&#xff1a;账号&#xff0c;表&#xff0c;存储过程&#xff0c;表空间&#xff0c;性能监控&#xff0c;配置 等基础信息。系统库目…

【前沿技术RPA】 一文了解UiPath 使用Git管理项目

&#x1f40b;作者简介&#xff1a;博主是一位.Net开发者&#xff0c;同时也是RPA和低代码平台的践行者。 &#x1f42c;个人主页&#xff1a;会敲键盘的肘子 &#x1f430;系列专栏&#xff1a;UiPath &#x1f980;专栏简介&#xff1a;UiPath在传统的RPA&#xff08;Robotic…

【soc】— spluboot校验方法

【soc】— spl/uboot校验方法 一.常规校验/外部有存储介质 针对外部有存储介质的如nandFlash&#xff0c;norFlash&#xff0c;emmc&#xff0c;Sd等&#xff0c;常用的校验方法为&#xff1a;headerspl/uboot header&#xff1a;可定义为结构体&#xff0c;内容包括&#x…

全国366个市县日度空气质量数据(2016-2020年)(AQI,SO2,NO2,PM2.5,PM10)

数据集名称&#xff1a;全国366个市县日度空气质量数据 时间范围&#xff1a;2016-2020年 相关说明&#xff1a;共收录366个市县全年全日数据&#xff0c;其中浓度为日均值&#xff0c;IAQI由浓度推算而来。IAQI为各空气质量指标对应的空气质量指数&#xff0c;用于对应AQI与…

多线程的初识

目录多线程线程的引入进程和线程的关系多线程可能存在的问题多线程程序的创建Thread创建第一个多线程程序线程的抢占式执行查看java进程中的所有线程用Thread的其他方法创建多线程实现Runnable接口使用匿名内部类&#xff0c;继承Thread使用匿名内部类实现Runnable使用Lambda表…

嵌入式Linux驱动开发笔记(未完待续。。。)

一、Git仓库用法 1、linu终端输入下面命令安装 git clone https://e.coding.net/weidongshan/linux_course/linux_basic_develop.git2、 进入到GIT仓库目录 cd /D/abc/doc_and_source_for_mcu_mpu在doc_and_source_for_mcu_mpu目录下&#xff0c;执行以下命令获得资料的最新…

【1752. 检查数组是否经排序和轮转得到】

来源&#xff1a;力扣&#xff08;LeetCode&#xff09; 描述&#xff1a; 给你一个数组 nums 。nums 的源数组中&#xff0c;所有元素与 nums 相同&#xff0c;但按非递减顺序排列。 如果 nums 能够由源数组轮转若干位置&#xff08;包括 0 个位置&#xff09;得到&#xf…

appnium环境搭建

一、安装JDK 官网下载对应版本直接安装 二、安装Android SDK 官网下载对应版本直接安装 https://www.androiddevtools.cn/ 三、安装安卓模拟器 我使用的是夜神模拟器&#xff0c;官网下载直接安装 夜神安卓模拟器-安卓模拟器电脑版下载_安卓手游模拟器_手机模拟器_官网 …

springboot整合SpringSecurity并实现简单权限控制

目录 一、SpringSecurity介绍 案例效果&#xff1a; 二、环境准备 2.1 数据库 2.2 项目准备 三、确保项目没问题后开始使用 3.1、Security的过滤链&#xff1a; 3.2、自定义用户名密码登录&#xff1a; 方式1&#xff1a;将用户名密码写在配置文件里 方式2&#xff1a;使…

刷题之莲子的软件工程学和机械动力学以及物理热力学

目录 1、莲子的软件工程学 1&#xff09;题目 2&#xff09;题目解析 3&#xff09;代码 2、莲子的机械动力学 2&#xff09;题目解析 3&#xff09;代码 3、莲子的物理热力学 1&#xff09;、题目 2&#xff09;题目解析 1、莲子的软件工程学 1&#xff09;题目 题目背景…

Linux下的进程控制-进程程序替换

这篇主要说一下Linux下的进程控制中最后一部分内容&#xff1a;进程程序替换。 文章目录1. 进程程序替换1.1 为什么要进程程序替换1.2 替换原理1.3 如何进行程序替换1.3.1 execl函数1.3.2 引入子进程的程序替换1.3.3 execv函数1.3.4 execlp函数和execvp函数1.3.5 如何执行其它…

Flutter自定义对话框返回相关问题汇总

Flutter自定义对话框&#xff0c;禁用系统返回按钮 - WillPopScope 使用WillPopScope即可&#xff0c;重点onWillPop方法: Future<bool> _onWillPop()>new Future.value(false); 由于要弹出dialog&#xff0c;我这里是禁掉返回按钮&#xff0c;当然也可以在这里做一下…