JS购物车开发:更新二维数组商品数量同步sessionStorage
问题描述
- 需求:将下拉式购物车内的全量商品数据,传递到带结算功能的独立HTML购物车页面
- 现有方案:将购物车详情存入二维数组,写入
sessionStorage后在结算页读取展示 - 现存bug:下拉购物车内用户修改后的最新商品数量,无法同步存入对应二维数组
- 阻碍原因:
quantityChanged()、updateCartTotal()、addItemToCart()分别绑定不同事件、逻辑独立,无法直接将用户输入的数量作为参数,传入负责写入sessionStorage的addItemToCart()函数
现有实现代码
/*Cart Funtion*/ /*Store items to cart*/ if (document.readyState == 'loading') { document.addEventListener('DOMContentLoaded', ready) } else { ready(); } function ready() { var removeCartItemButtons = document.getElementsByClassName('cart-remove-btn') for (var i = 0; i < removeCartItemButtons.length; i++) { var button = removeCartItemButtons[i] button.addEventListener('click', removeCartItem) } var quantityInputs = document.getElementsByClassName('cart-quantity-input') for (var i = 0; i < quantityInputs.length; i++) { var input = quantityInputs[i] input.addEventListener('change', quantityChanged) } var addToCartButtons = document.getElementsByClassName('addCart_button'); for (var i = 0; i < addToCartButtons.length; i++) { var button = addToCartButtons[i]; button.addEventListener('click', addToCartClicked); } } function removeCartItem(event) { var buttonClicked = event.target buttonClicked.parentElement.parentElement.remove() updateCartTotal() } function quantityChanged(event) { var input = event.target if (isNaN(input.value) || input.value <= 0 || input.value % 1 != 0) { input.value = 1 } updateCartTotal() } function updateCartTotal() { var cartItemContainer = document.getElementsByClassName('cart-items')[0] var cartRows = cartItemContainer.getElementsByClassName('cart-row') var subtotal = 0 var total = 0 for (var i = 0; i < cartRows.length; i++) { var cartRow = cartRows[i] var priceElement = cartRow.getElementsByClassName('cart-price')[0] var quantityElement = cartRow.getElementsByClassName('cart-quantity-input')[0] var price = parseFloat(priceElement.innerText.replace('RM', '')) var quantity = quantityElement.value subtotal = price * quantity total = total + (price * quantity) document.getElementsByClassName('cart-subtotal')[i].innerText = 'RM' + subtotal } total = Math.round(total * 100) / 100 if (total % 1 != 0){ document.getElementsByClassName('cart-total-price')[0].innerText = total } else{ document.getElementsByClassName('cart-total-price')[0].innerText = total+ '.00' } document.getElementsByClassName('label')[0].innerText = 'My Cart (' + cartRows.length +')' } var a=0; var cartArray = [[]] /*Add To Cart Funtion*/ function addToCartClicked(addCart) { var button = addCart.target; var shopItem = button.parentElement.parentElement; var imageSrc = shopItem.getElementsByClassName('item-img')[0].src; var title = shopItem.getElementsByClassName('item-title')[0].innerText; var price = shopItem.getElementsByClassName('item-price')[0].innerText; cartArray[a] = [imageSrc,title, price] a += 1 console.log(cartArray) addItemToCart(imageSrc, title, price, cartArray); updateCartTotal(); } var addItemId = 0; function addItemToCart(imageSrc, title, price, cartArray) { //cart drop down var cartRow = document.createElement('div'); cartRow.classList.add('cart-row') var cartItems = document.getElementsByClassName('cart-items')[0]; var cartItemNames = cartItems.getElementsByClassName('cart-item-title'); for (var i = 0; i < cartItemNames.length; i++){ if (cartItemNames[i].innerText == title){ alert('This package is already added to the cart.') return } } var cartRowContents = ` <div class="cart-item cart-column"> <span class="cart-item-title">${title}</span> </div> <span class="cart-price cart-column">RM ${price}</span> <div class="cart-quantity cart-column"> <input class="cart-quantity-input" type="number" value="1" min="1"> </div> <span class="cart-subtotal cart-column">RM ${price}</span> <span class="cart-remove-btn cart-column"><i class="fa fa-trash" aria-hidden="true"></i></span>` cartRow.innerHTML = cartRowContents cartItems.append(cartRow) cartRow.getElementsByClassName('cart-remove-btn')[0].addEventListener('click',removeCartItem) cartRow.getElementsByClassName('cart-quantity-input')[0].addEventListener('change',quantityChanged) //saving the values in session storage sessionStorage.setItem("productArray", JSON.stringify(cartArray)); }
效果参考截图


解决方案
问题根源有两个:
- 存储的商品数组结构缺失数量字段,加购时只存了图、标题、价格,默认数量1没有写入数组
- 存储逻辑只在加购时触发一次,后续用户修改数量、删除商品时,没有同步更新数组和存储,靠全局索引变量维护数组很容易出现数据和页面对应不上的问题
不需要在各个独立函数之间传参,直接利用现有代码的逻辑特性改就行——所有购物车操作(加购、改数量、删商品)最后都会调用updateCartTotal(),直接把全量数据同步的逻辑放到这个函数最后,每次计算总价时遍历当前页面所有购物车行,重新收集最新的商品数据(包含用户改后的数量)再存到sessionStorage,完全不会有数据不同步的问题。
具体修改步骤:
- 删除全局无用代码:删掉
var a=0;、var cartArray = [[]]、var addItemId = 0;这几个全局变量,后续数组在updateCartTotal()内部生成即可 - 修改
addItemToCart()里的购物车渲染模板,补上商品图标签,否则后续收集数据时拿不到商品图地址,同时删掉函数末尾写sessionStorage的代码 - 重写
updateCartTotal()函数,在遍历购物车行计算价格的同时,收集每一行的最新商品图、标题、价格、数量,生成最新的二维数组,计算完总价后统一写入sessionStorage - 删掉
addToCartClicked()里给cartArray赋值、递增索引a的冗余代码
修改后的核心代码:
// 重写后的updateCartTotal function updateCartTotal() { var cartItemContainer = document.getElementsByClassName('cart-items')[0] var cartRows = cartItemContainer.getElementsByClassName('cart-row') var subtotal = 0 var total = 0 // 初始化空数组,每次重新收集最新数据 var cartArray = [] for (var i = 0; i < cartRows.length; i++) { var cartRow = cartRows[i] var priceElement = cartRow.getElementsByClassName('cart-price')[0] var quantityElement = cartRow.getElementsByClassName('cart-quantity-input')[0] var titleElement = cartRow.getElementsByClassName('cart-item-title')[0] var imgElement = cartRow.getElementsByClassName('cart-item-img')[0] var price = parseFloat(priceElement.innerText.replace('RM', '')) var quantity = parseInt(quantityElement.value) subtotal = price * quantity total = total + subtotal // 把当前行最新数据存入数组 cartArray.push([imgElement.src, titleElement.innerText, price, quantity]) document.getElementsByClassName('cart-subtotal')[i].innerText = 'RM' + subtotal } total = Math.round(total * 100) / 100 document.getElementsByClassName('cart-total-price')[0].innerText = total % 1 != 0 ? total : total + '.00' document.getElementsByClassName('label')[0].innerText = 'My Cart (' + cartRows.length +')' // 统一写入最新数据到sessionStorage sessionStorage.setItem("productArray", JSON.stringify(cartArray)); } // 简化后的addToCartClicked function addToCartClicked(addCart) { var button = addCart.target; var shopItem = button.parentElement.parentElement; var imageSrc = shopItem.getElementsByClassName('item-img')[0].src; var title = shopItem.getElementsByClassName('item-title')[0].innerText; var price = shopItem.getElementsByClassName('item-price')[0].innerText.replace('RM', ''); addItemToCart(imageSrc, title, price); updateCartTotal(); } // 调整addItemToCart入参,去掉不需要传的cartArray,补上商品图渲染 function addItemToCart(imageSrc, title, price) { var cartRow = document.createElement('div'); cartRow.classList.add('cart-row') var cartItems = document.getElementsByClassName('cart-items')[0]; var cartItemNames = cartItems.getElementsByClassName('cart-item-title'); for (var i = 0; i < cartItemNames.length; i++){ if (cartItemNames[i].innerText == title){ alert('This package is already added to the cart.') return } } var cartRowContents = ` <div class="cart-item cart-column"> <img class="cart-item-img" src="${imageSrc}" style="width:50px;height:50px;object-fit:cover;"> <span class="cart-item-title">${title}</span> </div> <span class="cart-price cart-column">RM ${price}</span> <div class="cart-quantity cart-column"> <input class="cart-quantity-input" type="number" value="1" min="1"> </div> <span class="cart-subtotal cart-column">RM ${price}</span> <span class="cart-remove-btn cart-column"><i class="fa fa-trash" aria-hidden="true"></i></span>` cartRow.innerHTML = cartRowContents cartItems.append(cartRow) cartRow.getElementsByClassName('cart-remove-btn')[0].addEventListener('click',removeCartItem) cartRow.getElementsByClassName('cart-quantity-input')[0].addEventListener('change',quantityChanged) }
改完之后不管用户怎么调整商品数量、删除商品、加新商品,跳转到结算页前sessionStorage里存的永远是和下拉购物车显示完全一致的最新数据,结算页直接读取解析就能用。
内容的提问来源于stack exchange,提问作者Kitteyn
相关产品推荐
相关产品推荐

