使用原生JS实现移动端购物车效果案例

 更新时间:2026年02月06日 10:13:51   作者:苦瓜大王~  
原生JavaScript作为前端开发的基石,在构建交互式网页应用中扮演着至关重要的角色,下面这篇文章主要介绍了使用原生JS实现移动端购物车效果案例的相关资料,文中通过代码介绍的非常详细,需要的朋友可以参考下

实现效果

  • 显示所有商品列表
  • 基本的加减商品、商品数量、计算价格功能
  • 页脚有商品时显示高亮样式、配送费等

素材、文件展示

代码展示

html部分

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>外卖</title>
    <link rel="shortcut icon" href="./assets/favicon.ico" rel="external nofollow"  type="image/x-icon" />
    <link rel="stylesheet" href="./css/common.css" rel="external nofollow"  />
    <link rel="stylesheet" href="./css/container.css" rel="external nofollow"  />
    <link rel="stylesheet" href="./css/footer.css" rel="external nofollow"  />
    <link rel="stylesheet" href="./css/add-to-car.css" rel="external nofollow"  />
  </head>
  <body>
    <div class="container">
      <div class="menu">
        <div class="menu-item active"><span>推荐</span></div>
        <div class="menu-item"><span>热销</span></div>
        <div class="menu-item"><span>折扣</span></div>
        <div class="menu-item"><span>夏日冰咖必喝榜</span></div>
        <div class="menu-item"><span>进店必喝</span></div>
        <div class="menu-item"><span>只喝美式</span></div>
        <div class="menu-item"><span>酷爽特调水果冰萃</span></div>
        <div class="menu-item"><span>经典奶咖</span></div>
        <div class="menu-item"><span>创意奶咖</span></div>
        <div class="menu-item"><span>瑞纳冰季</span></div>
        <div class="menu-item"><span>不喝咖啡</span></div>
        <div class="menu-item"><span>轻食甜品</span></div>
        <div class="menu-item"><span>热卖套餐</span></div>
      </div>
      <div class="goods-list">
      </div>
    </div>
    <div class="footer">
      <div class="footer-car-container">
        <div class="footer-car">
          <i class="iconfont i-gouwuchefill"></i>
          <span class="footer-car-badge">0</span>
        </div>
        <div class="footer-car-price">
          <span class="footer-car-unit">¥</span>
          <span class="footer-car-total">0.00</span>
        </div>
        <div class="footer-car-tip">配送费¥0</div>
      </div>
      <div class="footer-pay">
        <a href="">去结算</a>
        <span>还差¥0元起送</span>
      </div>
    </div>

    <div class="add-to-car">
      <i class="iconfont i-jiajianzujianjiahao"></i>
    </div>

    <script src="./js/data.js"></script>
    <script src="./js/index.js"></script>
  </body>
</html>

css部分

  • 由于篇幅有限,css不展示完整代码,可根据喜好调整
  • 此处放一张,未加入index.js的展示图

js部分

  • 仅展示index.js文件

单个商品数据

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() {
    if (this.choose === 0) {
      return
    }
    this.choose--
  }
}

整个界面的数据

class UIData {
  constructor() {
    // 所有商品
    const getTotalGoods = goods.map(function (goods) {
      return new UIGoods(goods)
    })
    this.uiGoods = getTotalGoods
    // 起送费
    this.deliveryThreshold = 30
    // 配送费
    this.deliveryPrice = 5
  }
  // 总价
  getTotalPrice() {
    return this.uiGoods.reduce((acc, cur) => {
      return acc + cur.getTotalPrice()
    }, 0)
  }
  // 增加某件商品的选中数量
  increase(index) {
    this.uiGoods[index].increase()
  }
  // 减少某件商品的选中数量
  decrease(index) {
    this.uiGoods[index].decrease()
  }
  // 获取全部商品的选中数量
  getTotalGoodsNumber() {
    return this.uiGoods.reduce((acc, cur) => {
      return acc + cur.choose
    }, 0)
  }
  // 是否选择了商品
  hasGoodsInCar() {
    return this.getTotalGoodsNumber() > 0
  }
  // 是否到达起送费
  isCrossDeliveryThreshold() {
    return this.getTotalPrice() >= this.deliveryThreshold
  }
}

界面

class UI {
  constructor() {
    // 界面数据
    this.uiData = new UIData()
    // 获取dom元素对象
    this.doms = {
      goodsContainer: document.querySelector('.goods-list'),
      deliveryPrice: 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')
    }

    // 计算跳跃坐标
    let carRect = this.doms.car.getBoundingClientRect()
    let jumpTarget = {
      x: carRect.left + carRect.width / 2,
      y: carRect.top + carRect.height / 5
    }

    this.createHTML()
    this.updateFooter()
    this.listenEvent()
    this.jumpTarget = jumpTarget
  }
  // 监听事件
  listenEvent() {
    this.doms.car.addEventListener('animationend', function () {
      this.classList.remove('animate')
    })
  }
  // 创建商品元素
  createHTML() {
    let html = ''
    for (let i = 0; i < this.uiData.uiGoods.length; i++) {
      let 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>0</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) {
    let goodsDom = this.doms.goodsContainer.children[index]
    // 移除添加类
    if (this.uiData.uiGoods[index].isChoose()) {
      goodsDom.classList.add('active')
    } else {
      goodsDom.classList.remove('active')
    }
    // 数量显示
    let span = goodsDom.querySelector('.goods-btns span')
    span.textContent = this.uiData.uiGoods[index].choose
  }
  // 更新页脚
  updateFooter() {
    // 得到总价数据
    let total = this.uiData.getTotalPrice()
    // 设置配送费
    this.doms.deliveryPrice.textContent = `配送费${this.uiData.deliveryPrice}`
    // 移除添加类
    if (this.uiData.isCrossDeliveryThreshold()) {
      this.doms.footerPay.classList.add('active')
    } else {
      this.doms.footerPay.classList.remove('active')
      // 起送差价
      let dis = this.uiData.deliveryThreshold - total
      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.getTotalGoodsNumber()
  }
  // 购物车动画
  carAnimate() {
    this.doms.car.classList.add('animate')
  }
  // 跳跃动画
  jump(index) {
    // 获取坐标
    let btnAdd = this.doms.goodsContainer.children[index].querySelector('.i-jiajianzujianjiahao')
    let rect = btnAdd.getBoundingClientRect()
    let start = {
      x: rect.left,
      y: rect.top
    }
    // 创建跳跃元素
    let div = document.createElement('div')
    div.className = 'add-to-car'
    let i = document.createElement('i')
    i.className = 'iconfont i-jiajianzujianjiahao'
    div.appendChild(i)
    document.body.appendChild(div)
    // 设置初始位置
    div.style.transform = `translateX(${start.x}px)`
    i.style.transform = `translateY(${start.y}px)`
    // 渲染动画效果(触发reflow即可)
    div.clientWidth
    // 设置结束位置
    div.style.transform = `translateX(${this.jumpTarget.x}px)`
    i.style.transform = `translateY(${this.jumpTarget.y}px)`

    div.addEventListener('transitionend',
      () => {
        div.remove()
        this.carAnimate()
      },
      {
        once: true
      }
    )
  }
}
const ui = new UI()

事件

ui.doms.goodsContainer.addEventListener('click', function(e){
  if (e.target.classList.contains('i-jiajianzujianjiahao')){
    let index = +e.target.getAttribute('index')
    ui.increase(index)
  } else if (e.target.classList.contains('i-jianhao')) {
    let index = +e.target.getAttribute('index')
    ui.decrease(index)
  }
})

总结 

到此这篇关于使用原生JS实现移动端购物车效果案例的文章就介绍到这了,更多相关JS移动端购物车效果内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • 如何在 JavaScript 中更好地利用数组

    如何在 JavaScript 中更好地利用数组

    这篇文章主要介绍了如何在 JavaScript 中更好地使用数组,本文通过实例代码给大家介绍的非常详细,具有一定的参考借鉴价值,需要的朋友可以参考下
    2018-09-09
  • 解决layui的table.checkStatus失效问题

    解决layui的table.checkStatus失效问题

    这篇文章主要介绍了解决layui的table.checkStatus失效问题,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2022-10-10
  • js print打印网页指定区域内容的简单实例

    js print打印网页指定区域内容的简单实例

    下面小编就为大家带来一篇js print打印网页指定区域内容的简单实例。小编觉得挺不错的,现在就分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2016-11-11
  • 前端实现ios26最新液态玻璃效果的步骤记录

    前端实现ios26最新液态玻璃效果的步骤记录

    这篇文章主要介绍了前端实现ios26最新液态玻璃效果的步骤记录,定义玻璃元素样式并应用液态滤镜,通过滤镜的动态变化模拟液体流动感,结合CSS或JavaScript实现原理,最终呈现视觉效果,需要的朋友可以参考下
    2025-07-07
  • 5 种JavaScript编码规范

    5 种JavaScript编码规范

    编码规范就是指导如何编写和组织代码的一系列标准,下面通过本文给大家带来了5 种JavaScript编码规范,需要的朋友参考下
    2018-01-01
  • 前端监控上报:Script Error问题的解决方法

    前端监控上报:Script Error问题的解决方法

    在微前端和多国业务的场景下,我们经常会遇到HTML页面域名和静态资源域名不统一的情况,这种架构虽然带来了部署和CDN优化的便利,但也引入了一个常见的问题,跨域Script Error,本文将详细介绍这个问题的原因、影响,以及一套完整的解决方案,需要的朋友可以参考下
    2025-12-12
  • 在Chrome DevTools中调试JavaScript的实现

    在Chrome DevTools中调试JavaScript的实现

    这篇文章主要介绍了在Chrome DevTools中调试JavaScript的实现,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2020-04-04
  • 新年快乐! javascript实现超级炫酷的3D烟花特效

    新年快乐! javascript实现超级炫酷的3D烟花特效

    这篇文章主要为大家详细介绍了javascript实现超级炫酷的3D烟花特效,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2019-01-01
  • JavaScript防止重复提交的三种解决方案

    JavaScript防止重复提交的三种解决方案

    防止JavaScript重复点击和提交需结合前端禁用按钮、节流和标志位,这篇文章主要介绍了JavaScript防止重复提交的三种解决方案,文中通过代码介绍的非常详细,需要的朋友可以参考下
    2025-07-07
  • 基于BootStrap Metronic开发框架经验小结【五】Bootstrap File Input文件上传插件的用法详解

    基于BootStrap Metronic开发框架经验小结【五】Bootstrap File Input文件上传插件的用法

    本文主要基于我自己的框架代码案例,介绍其中文件上传插件File Input的使用,非常具有参考借鉴价值,感兴趣的朋友一起学习吧
    2016-05-05

最新评论