Web前端—(原生JS)购物车效果

目录

  • 购物车效果
    • 分析数据
      • 单件商品的数据
      • 整个界面的数据
    • 分析界面
    • 分析事件

购物车效果

在这里插入图片描述

先准备好原始数据和素材
在下面数据的基础上,编写index.js
在这里插入图片描述

分析数据

  • 编写程序要从数据入手,从数据到界面最后到事件
  • 在分析数据的过程中,要分析数据是通过属性出现还是通过方法出现

单件商品的数据

  • 我们观察data.js中的数据:商品数组goods

    • pic:图片
    • title:标题
    • desc:描述
    • sellNumber:月售
    • favorRate:好评率
    • price:价格
  • 为了避免改变原始数据。我们创建一个单件商品的数据的类(class)
    在这里插入图片描述

  • 代码如下:
// 单件商品的数据
class UIGoods{
    constructor(g){
        this.data = g;
        this.choose = 0; // 每件商品被选中的数量
    }
    // 获取商品的总价
    getTotalPrice(){
        return this.data.price * this.choose;
    }
    // 是否选中此商品
    isChoose(){
        return this.choose > 0;
    }
    // 商品选择数量+1
    increase(){
        this.choose++;
    }
    // 商品选择数量-1
    decrease(){
        this.choose--;
    }

}
  • 对这个类进行测试 var uig = new UIGoods(goods[0]);
  • 测试成功进行下一步在这里插入图片描述

整个界面的数据

分析整个页面需要用到的数据,创建UIData类,并进行测试(大家可以自行在控制台进行测试)
在这里插入图片描述

  • 代码如下
// 整个界面的数据
class UIData{
    constructor(){
        var uiGoods = [];
        for(let i=0; i<goods.length; i++){
            let uig = new UIGoods(goods[i]);
            uiGoods.push(uig);
        }
        this.uiGoods = uiGoods;
        this.deliveryThreshold = 30; //起送费
        this.deliverPrice = 5; //配送费
    }

    // 获取总价
    getTotalPrice(){
        var sum = 0;
        for(let i=0; i<this.uiGoods.length; i++){
            let g = this.uiGoods[i];
            sum += g.getTotalPrice();
        }
        return sum;
    }

    // 增加某件商品的数量
    increase(index){
        this.uiGoods[index].increase();
    }
    // 减少某件商品的数量
    decrease(index){
        this.uiGoods[index].decrease();
    }
    // 得到总共的选中数量
    getTotalChooseNumber(){
        var sum = 0;
        for(let i=0; i<this.uiGoods.length; i++){
            sum += this.uiGoods[i].choose;
        }
        return sum;
    }
    // 判断购物车中有没有商品
    hasGoodsInCar(){
        return this.getTotalChooseNumber() > 0;
    }
    // 判断是否跨过了配送标准
    isCrossDeliveryThreshold(){
        return this.getTotalPrice() >= this.deliveryThreshold;
    }
    // 判断该商品是否被选中
    isChoose(index){
        return this.uiGoods[index].isChoose();
    }

}

分析界面

在分析完数据逻辑之后,我们来分析界面之间的逻辑关系
创建一个UI类

  • 代码如下:
// 整个界面
class UI{
    constructor(){
        this.uiData = new UIData();
        this.doms = {
            goodsContainer:document.querySelector('.goods-list'),
            deliverPrice:document.querySelector('.footer-car-tip'),
            footerPay:document.querySelector('.footer-pay'),
            footerPayInnerSpan:document.querySelector('.footer-pay span'),
            totalPrice:document.querySelector('.footer-car-total'),
            car:document.querySelector('.footer-car'),
            badge:document.querySelector('.footer-car-badge')
        }

        var carRect = this.doms.car.getBoundingClientRect();
        var jumpTarget = {
            x: carRect.left + carRect.width / 2,
            y: carRect.top + carRect.height / 5,
        };
        this.jumpTarget = jumpTarget;

        this.createHTML();
        this.updateFooter();
        this.listenEvent();

    }
    // 监听各种事件
    listenEvent(){
        this.doms.car.addEventListener('animationend', function(){
            this.classList.remove('animate');
        });
    }
    // 根据商品数据,创建商品列表
    createHTML(){
        // 1. 生成html字符串(parse html) 执行效率低,开发效率高
        // 2. 一个一个创建元素 执行效率高,开发效率低
        // 这里我们采用第一种方式
        var html = '';
        for(let i=0; i<this.uiData.uiGoods.length; i++){
            var g = this.uiData.uiGoods[i];
            html += `<div class="goods-item">
            <img src="${g.data.pic}" alt="" class="goods-pic" />
            <div class="goods-info">
              <h2 class="goods-title">${g.data.title}</h2>
              <p class="goods-desc">
                ${g.data.desc}
              </p>
              <p class="goods-sell">
                <span>月售 ${g.data.sellNumber}</span>
                <span>好评率${g.data.favorRate}</span>
              </p>
              <div class="goods-confirm">
                <p class="goods-price">
                  <span class="goods-price-unit">¥</span>
                  <span>${g.data.price}</span>
                </p>
                <div class="goods-btns">
                  <i index="${i}" class="iconfont i-jianhao"></i>
                  <span>${g.choose}</span>
                  <i index="${i}" class="iconfont i-jiajianzujianjiahao"></i>
                </div>
              </div>
            </div>
          </div>`;
        }
        this.doms.goodsContainer.innerHTML = html;
    }

    // 界面的增加减少
    increase(index){
        this.uiData.increase(index);
        this.updateGoodsItem(index);
        this.updateFooter();
        this.jump(index);
    }
    decrease(index){
        this.uiData.decrease(index);
        this.updateGoodsItem(index);
        this.updateFooter();
    }

    // 更新某个商品元素的显示状态
    updateGoodsItem(index){
        var goodsDom = this.doms.goodsContainer.children[index];
        if(this.uiData.isChoose(index)){
            goodsDom.classList.add('active');
        }else{
            goodsDom.classList.remove('active');
        }
        var span = goodsDom.querySelector('.goods-btns span');
        span.textContent = this.uiData.uiGoods[index].choose;
    }

    // 更新页脚
    updateFooter(){
        var total = this.uiData.getTotalPrice(); 
        this.doms.deliverPrice.textContent = `配送费¥${this.uiData.deliverPrice}`;
        if(this.uiData.isCrossDeliveryThreshold()){
            // 到达起送点
            this.doms.footerPay.classList.add('active');
        }else{
            this.doms.footerPay.classList.remove('active');
            // 更新还差多少钱
            var dis = this.uiData.deliveryThreshold - total;
            dis = Math.round(dis); 
            this.doms.footerPayInnerSpan.textContent = `还差¥${dis}元起送`;
        }
        // 总价元素,设置总价
        this.doms.totalPrice.textContent = total.toFixed(2);
        // 设置购物车的样式状态
        if(this.uiData.hasGoodsInCar()){
            this.doms.car.classList.add('active');
        }else{
            this.doms.car.classList.remove('active');
        }
        // 设置购物车中的数量
        this.doms.badge.textContent = this.uiData.getTotalChooseNumber();
    }
    // 购物车动画
    carAnimate(){
        this.doms.car.classList.add('animate');
    }
    // 抛物线跳跃的元素
    jump(index){
        // 找到对应商品的加号
        var btnAdd = this.doms.goodsContainer.children[index].querySelector('.i-jiajianzujianjiahao');
        var rect = btnAdd.getBoundingClientRect();
        var start = {
            x:rect.left,
            y:rect.top
        };
        // 跳
        var div = document.createElement('div');
        div.className = 'add-to-car';
        var i = document.createElement('i');
        i.className = 'iconfont i-jiajianzujianjiahao';
        // 设置初始位置
        div.style.transform = `translateX(${start.x}px)`;
        i.style.transform = `translateY(${start.y}px)`;
        div.appendChild(i);
        document.body.appendChild(div);
        // 强行渲染
        div.clientWidth;
        // 设置结束位置
        div.style.transform = `translateX(${this.jumpTarget.x}px)`;
        i.style.transform = `translateY(${this.jumpTarget.y}px)`;
        var that = this;
        div.addEventListener(
            'transitionend',
            function () {
                div.remove();
                that.carAnimate();
            },
            {
                once: true, // 事件仅触发一次
            }
        );
    }
    
}

分析事件

到这里为止界面上的逻辑已经全部完成,开始添加事件
在这里,为了获取我们到底是点击了哪个,可以添加一个自定义属性来获取index

  • 代码如下
var ui = new UI();

// 事件
ui.doms.goodsContainer.addEventListener('click', function (e) {
    if (e.target.classList.contains('i-jiajianzujianjiahao')) {
      var index = +e.target.getAttribute('index');
      ui.increase(index);
    } else if (e.target.classList.contains('i-jianhao')) {
      var index = +e.target.getAttribute('index');
      ui.decrease(index);
    }
  });
  
  window.addEventListener('keypress', function (e) {
    if (e.code === 'Equal') {
      ui.increase(0);
    } else if (e.code === 'Minus') {
      ui.decrease(0);
    }
  });

到这里,就实现了购物车的全部效果,我会上传我的资源,大家自行下载。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

C# 批量删除Excel重复项

当从不同来源导入Excel数据时&#xff0c;可能存在重复的记录。为了确保数据的准确性&#xff0c;通常需要删除这些重复的行。 手动查找并删除可能会非常耗费时间&#xff0c;而通过编程脚本则可以实现在短时间内处理大量数据。本文将提供一个使用C# 快速查找并删除Excel重复项…

JAVAEE之Cookie/Session

1.Cookie HTTP 协议自身是属于 "无状态" 协议. "无状态" 的含义指的是: 默认情况下 HTTP 协议的客户端和服务器之间的这次通信, 和下次通信之间没有直接的联系. 但是实际开发中, 我们很多时候是需要知道请求之间的关联关系的. 例如登陆网站成功后, 第二…

WE博客代码系统

WE博客代码系统 说明文档 运行前附加数据库.mdf&#xff08;或sql生成数据库&#xff09; 主要技术&#xff1a; 基于asp.net mvc架构和sql server数据库&#xff0c;并采用EF实体模型开发。 三层架构并采用EF实体模型开发 功能模块&#xff1a; WE博客代码系统 WE博客代码系…

全自动封箱机的工作原理:科技与效率的完美结合

随着科技的不断发展&#xff0c;越来越多的自动化设备走进了我们的日常生活和工业生产中。其中&#xff0c;全自动封箱机作为物流包装领域的重要一环&#xff0c;凭借其高效、精准的工作性能&#xff0c;正逐渐成为提升生产效率、降低劳动成本的得力助手。星派就来与大家深入探…

HarmonyOS NEXT应用开发之状态管理优秀实践

为了帮助应用程序开发人员提高其应用程序质量&#xff0c;特别是在高效的状态管理方面。本章节面向开发者提供了多个在开发ArkUI应用中常见的低效开发的场景&#xff0c;并给出了对应的解决方案。此外&#xff0c;还提供了同一场景下&#xff0c;推荐用法和不推荐用法的对比和解…

代码随想录Day28:回溯算法Part4

Leetcode 93. 复原IP地址 讲解前&#xff1a; 这道题其实在做完切割回文串之后&#xff0c;学会了使用切割的方法来找到字符串的possible 子串之后&#xff0c;思路就会很快找到&#xff0c;细想一下其实无非也就是对given string然后进行切割&#xff0c;只是深度是固定的因…

GLP-1药物固相合成法-载体树脂及层析填料

摘要&#xff1a;在生物医药GLP-1药物制备领域不仅可提供高稳定性载体树脂&#xff0c;还可根据客户需求&#xff0c;合成定制化载体&#xff08;如预接氨基酸固相合成载体、特殊溶胀度或基团负载量的载体、负载特殊基团的载体、清除树脂等&#xff09;。同时&#xff0c;海普专…

深入剖析主机安全中的零信任机制及其实施原理

引言 在数字化转型加速与云端服务普及的大背景下&#xff0c;传统依赖边界的网络安全模式逐渐显露出其局限性。面对愈发复杂多变的威胁环境&#xff0c;零信任安全架构作为新一代的安全范式应运而生&#xff0c;尤其是在主机层面的安全实践中&#xff0c;零信任机制正扮演着至…

docker安装wekan

安装mongodb 注意这里用端口映射方法将db的端口映射到宿主机。并且注意自己的映射目录&#xff0c;如果不需要映射目录直接删除-v /home/data/project/wekan/wekandb/db:/data/db -v /home/data/project/wekan/wekandb/configdb:/data/configdb sudo docker run -d --name we…

Golang | Leetcode Golang题解之第7题整数反转

题目&#xff1a; 题解&#xff1a; func reverse(x int) (rev int) {for x ! 0 {if rev < math.MinInt32/10 || rev > math.MaxInt32/10 {return 0}digit : x % 10x / 10rev rev*10 digit}return }

第十三届蓝桥杯大赛软件赛省赛CC++大学B组

第十三届蓝桥杯大赛软件赛省赛CC 大学 B 组 文章目录 第十三届蓝桥杯大赛软件赛省赛CC 大学 B 组1、九进制转十进制2、顺子日期3、刷题统计4、修建灌木5、x进制减法6、统计子矩阵7、积木画8、扫雷9、李白打酒加强版10、砍竹子 1、九进制转十进制 计算器计算即可。2999292。 2、…

如何在 Visual Studio for Mac 中使用 .NET 8 上的 FastReport Avalonia

FastReport Business Graphics .NET&#xff0c;是一款基于fastreport报表开发控件的商业图形库&#xff0c;借助 FastReport 商业图形库&#xff0c;您可以可视化不同的分层数据&#xff0c;构建业务图表以进行进一步分析和决策。利用数据呈现领域专家针对 .NET 7、.NET Core、…

阿里云服务器租用价格,2024年新版活动报价及租用收费标准

2024年阿里云服务器租用费用&#xff0c;云服务器ECS经济型e实例2核2G、3M固定带宽99元一年&#xff0c;轻量应用服务器2核2G3M带宽轻量服务器一年61元&#xff0c;ECS u1服务器2核4G5M固定带宽199元一年&#xff0c;2核4G4M带宽轻量服务器一年165元12个月&#xff0c;2核4G服务…

解决el-table设置固定高度后,展示不同列时表格高度变小bug

解决el-table设置固定高度后&#xff0c;展示不同列时表格高度变小bug 1、需求分析2、解决方案 1、需求分析 在el-table使用过程中&#xff0c;选择多个参数展示更多列时会出现高度变小问题究其原因可知是el-table列动态发生变化后&#xff0c;el-table__body-wrapper的高度变…

HTML - 你是如何理解link和@import的

难度级别:初级及以上 提问概率:55% link是我们非常熟悉的一个HTML标签,用于引入CSS文件,而@import则存在于CSS文件内部,用于再次引入其他的CSS文件。所以很显然,执行顺序上,link标签会随着HTML文档加载,开始触发下载CSS文件的操作。…

clickhouse 源码编译部署

clickhouse 源码编译部署 版本 21.7.9.7 点击build project&#xff0c;编译工程&#xff0c;经过一定时间&#xff08;第一次编译可能几个小时&#xff0c;后续再编译&#xff0c;只编译有改动的文件&#xff09;生成release目录 在cmake-build-release → programs目录下…

【机器学习】科学库使用第3篇:机器学习概述,学习目标【附代码文档】

机器学习&#xff08;科学计算库&#xff09;完整教程&#xff08;附代码资料&#xff09;主要内容讲述&#xff1a;机器学习&#xff08;常用科学计算库的使用&#xff09;基础定位、目标&#xff0c;机器学习概述定位,目标,学习目标,学习目标,1 人工智能应用场景,2 人工智能小…

【generate】如何维护一套icon组件库,直接输出svg为react component

https://github.com/ant-design/ant-design-web3/pull/761/files 实现了icon-preview(通过jsdoc, 鼠标放在组件上可以看到icon的样式)&#xff0c;因为打包方式、产物以及命名上有一些不同&#xff0c;可能需要稍加改造。 这个同步脚本应该后续也用得上&#xff0c;略加改造同步…

git分支-基本分支与合并

问题假设 让我们通过一个简单的分支和合并的例子&#xff0c;演示在实际工作中可能会使用的工作流程。将按照以下步骤进行&#xff1a; 在网站上进行一些工作。为正在开发的新用户故事创建一个分支。在该分支上进行一些工作。 在这个阶段&#xff0c;我们可能会接到一个电话…

原型链污染攻击也称JavaScript Prototype 污染攻击

JavaScript数据类型 let和var关键字的区别 使用var或let关键字可以定义变量 let和var的区别如下&#xff1a; var是全局作用域&#xff0c;let 只在当前代码块内有效 当在代码块外访问let声明的变量时会报错 var有变量提升&#xff0c;let没有变量提升 let必须先声明…