使用原生JS實現(xiàn)移動端購物車效果案例
實現(xiàn)效果
- 顯示所有商品列表
- 基本的加減商品、商品數(shù)量、計算價格功能
- 頁腳有商品時顯示高亮樣式、配送費等

素材、文件展示

代碼展示
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>進(jìn)店必喝</span></div>
<div class="menu-item"><span>只喝美式</span></div>
<div class="menu-item"><span>酷爽特調(diào)水果冰萃</span></div>
<div class="menu-item"><span>經(jīng)典奶咖</span></div>
<div class="menu-item"><span>創(chuàng)意奶咖</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="">去結(jié)算</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不展示完整代碼,可根據(jù)喜好調(diào)整
- 此處放一張,未加入index.js的展示圖

js部分
- 僅展示index.js文件
單個商品數(shù)據(jù)
class UIGoods {
constructor(g) {
this.data = g
this.choose = 0
}
// 獲取總價
getTotalPrice() {
return this.data.price * this.choose
}
// 是否選擇了商品
isChoose() {
return this.choose > 0
}
// 選擇的數(shù)量+1
increase() {
this.choose++
}
// 選擇的數(shù)量-1
decrease() {
if (this.choose === 0) {
return
}
this.choose--
}
}
整個界面的數(shù)據(jù)
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)
}
// 增加某件商品的選中數(shù)量
increase(index) {
this.uiGoods[index].increase()
}
// 減少某件商品的選中數(shù)量
decrease(index) {
this.uiGoods[index].decrease()
}
// 獲取全部商品的選中數(shù)量
getTotalGoodsNumber() {
return this.uiGoods.reduce((acc, cur) => {
return acc + cur.choose
}, 0)
}
// 是否選擇了商品
hasGoodsInCar() {
return this.getTotalGoodsNumber() > 0
}
// 是否到達(dá)起送費
isCrossDeliveryThreshold() {
return this.getTotalPrice() >= this.deliveryThreshold
}
}
界面
class UI {
constructor() {
// 界面數(shù)據(jù)
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')
}
// 計算跳躍坐標(biāo)
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
}
// 監(jiān)聽事件
listenEvent() {
this.doms.car.addEventListener('animationend', function () {
this.classList.remove('animate')
})
}
// 創(chuàng)建商品元素
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()
}
// 更新商品的顯示狀態(tài)
updateGoodsItem(index) {
let goodsDom = this.doms.goodsContainer.children[index]
// 移除添加類
if (this.uiData.uiGoods[index].isChoose()) {
goodsDom.classList.add('active')
} else {
goodsDom.classList.remove('active')
}
// 數(shù)量顯示
let span = goodsDom.querySelector('.goods-btns span')
span.textContent = this.uiData.uiGoods[index].choose
}
// 更新頁腳
updateFooter() {
// 得到總價數(shù)據(jù)
let total = this.uiData.getTotalPrice()
// 設(shè)置配送費
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}元起送`
}
// 設(shè)置總價
this.doms.totalPrice.textContent = total.toFixed(2)
// 設(shè)置購物車樣式狀態(tài)
if (this.uiData.hasGoodsInCar()) {
this.doms.car.classList.add('active')
} else {
this.doms.car.classList.remove('active')
}
// 設(shè)置購物車數(shù)量
this.doms.badge.textContent = this.uiData.getTotalGoodsNumber()
}
// 購物車動畫
carAnimate() {
this.doms.car.classList.add('animate')
}
// 跳躍動畫
jump(index) {
// 獲取坐標(biāo)
let btnAdd = this.doms.goodsContainer.children[index].querySelector('.i-jiajianzujianjiahao')
let rect = btnAdd.getBoundingClientRect()
let start = {
x: rect.left,
y: rect.top
}
// 創(chuàng)建跳躍元素
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)
// 設(shè)置初始位置
div.style.transform = `translateX(${start.x}px)`
i.style.transform = `translateY(${start.y}px)`
// 渲染動畫效果(觸發(fā)reflow即可)
div.clientWidth
// 設(shè)置結(jié)束位置
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)
}
})
總結(jié)
到此這篇關(guān)于使用原生JS實現(xiàn)移動端購物車效果案例的文章就介紹到這了,更多相關(guān)JS移動端購物車效果內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
js print打印網(wǎng)頁指定區(qū)域內(nèi)容的簡單實例
下面小編就為大家?guī)硪黄猨s print打印網(wǎng)頁指定區(qū)域內(nèi)容的簡單實例。小編覺得挺不錯的,現(xiàn)在就分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2016-11-11
前端實現(xiàn)ios26最新液態(tài)玻璃效果的步驟記錄
這篇文章主要介紹了前端實現(xiàn)ios26最新液態(tài)玻璃效果的步驟記錄,定義玻璃元素樣式并應(yīng)用液態(tài)濾鏡,通過濾鏡的動態(tài)變化模擬液體流動感,結(jié)合CSS或JavaScript實現(xiàn)原理,最終呈現(xiàn)視覺效果,需要的朋友可以參考下2025-07-07
前端監(jiān)控上報:Script Error問題的解決方法
在微前端和多國業(yè)務(wù)的場景下,我們經(jīng)常會遇到HTML頁面域名和靜態(tài)資源域名不統(tǒng)一的情況,這種架構(gòu)雖然帶來了部署和CDN優(yōu)化的便利,但也引入了一個常見的問題,跨域Script Error,本文將詳細(xì)介紹這個問題的原因、影響,以及一套完整的解決方案,需要的朋友可以參考下2025-12-12
在Chrome DevTools中調(diào)試JavaScript的實現(xiàn)
這篇文章主要介紹了在Chrome DevTools中調(diào)試JavaScript的實現(xiàn),文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2020-04-04
新年快樂! javascript實現(xiàn)超級炫酷的3D煙花特效
這篇文章主要為大家詳細(xì)介紹了javascript實現(xiàn)超級炫酷的3D煙花特效,文中示例代碼介紹的非常詳細(xì),具有一定的參考價值,感興趣的小伙伴們可以參考一下2019-01-01
基于BootStrap Metronic開發(fā)框架經(jīng)驗小結(jié)【五】Bootstrap File Input文件上傳插件的用法
本文主要基于我自己的框架代碼案例,介紹其中文件上傳插件File Input的使用,非常具有參考借鑒價值,感興趣的朋友一起學(xué)習(xí)吧2016-05-05

