使用原生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移动端购物车效果内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!
相关文章
在Chrome DevTools中调试JavaScript的实现
这篇文章主要介绍了在Chrome DevTools中调试JavaScript的实现,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧2020-04-04
基于BootStrap Metronic开发框架经验小结【五】Bootstrap File Input文件上传插件的用法
本文主要基于我自己的框架代码案例,介绍其中文件上传插件File Input的使用,非常具有参考借鉴价值,感兴趣的朋友一起学习吧2016-05-05




最新评论