小白學程式碼使用原生JS寫購物車模組過程中的踩坑總結

趁著過年放假在家複習了之前學的JS知識,用原生擼了一個購物車模組,下面我來整理一下我的思路分享給大家。

一、功能和效果圖

1.1 廢話不多說,首先上個效果圖,如下:

小白學程式碼使用原生JS寫購物車模組過程中的踩坑總結

購物車功能效果圖

1.2 功能介紹:

點選全選按鈕,每一項商品的複選框處於被勾選的狀態,同時計算出商品數量和商品總價;

點選數量切換的按鈕,能自動計算出修改數量之後的商品數量和價格;

商品的總計數量和總價格應該只計算被勾選的商品的數量和金額。

功能介紹完畢,下面開始介紹我寫這個購物車的步驟。

二、購物車的頁面結構

2.1 HTML程式碼

<!—— 全選複選框 ——>
購物車
圖片 品名 單位 單價/元 數量 金額/元
小白學程式碼使用原生JS寫購物車模組過程中的踩坑總結 iPhone 11 4799 xxxx
小白學程式碼使用原生JS寫購物車模組過程中的踩坑總結 小米pro 11 3999 xxxx
小白學程式碼使用原生JS寫購物車模組過程中的踩坑總結 MacBook Pro 18999 xxxx
小白學程式碼使用原生JS寫購物車模組過程中的踩坑總結 小米75電視 5999 xxxx
小白學程式碼使用原生JS寫購物車模組過程中的踩坑總結 Canon 90D單反 9699 xxxx
總計: xxxx xxxx

2.2 CSS程式碼

table { border-collapse: collapse; width: 90%; text-align: center; margin: auto;}table caption { margin-bottom: 15px; font-size: 1。5rem;}table th, table td { border-bottom: 1px solid #ccc; padding: 5px; font-weight: normal;}table thead tr:first-of-type { background-color: #e6e6e6; height: 3em;}table input[type=“checkbox”] { width: 1。5em; height: 1。5em;}table tbody tr { border-bottom: 1px solid #ccc;}table tbody tr:hover { background-color: #f6f6f6; cursor: pointer;}tbody img { width: 3em;}tbody input[type=“number”] { width: 3em;}button { width: 150px; height: 30px; outline: none; border: none; background-color: teal; color: white; letter-spacing: 5px;}button:hover { opacity: 0。7; cursor: pointer;}

2.3 效果圖

小白學程式碼使用原生JS寫購物車模組過程中的踩坑總結

購物車效果圖

以上就是一個簡單的購物車頁面的HTML和CSS樣式程式碼。

三、完成相關JS程式碼

首先,我們先完成商品的全選與取消全選的功能,所以肯定是需要拿到全選複選框元素和商品前面的複選框元素,程式碼如下:

// 獲取全選複選框,所有的商品都有一個獨立的複選框const checkAll = document。querySelector(‘#check-all’);const checkItems = document。getElementsByName(‘item’);

拿到全選和每個商品的複選框元素之後,給全選框新增一個

change

事件,監聽它的

checked

值的變化。此時全選框的

checked

值可以透過事件監聽回撥函式中的ev引數下的

ev。target。checked

拿到。

checkALl。onchange = ev => { // 如果全選框處於選中狀態,ev。target。checked的值就為true,反之,為false。 console。log(ev。target。checked);};

如果想讓全選框的的狀態和每個商品前的複選框狀態保持一致,那麼就使他們的

checked

值一致即可。因此,我們可以在全選複選框的change事件中遍歷每個商品的複選框元素。

checkALl。onchange = ev => { // 如果全選框處於選中狀態,ev。target。checked的值就為true,反之,為false。 console。log(ev。target。checked); checkItems。forEach(item => item。checked = ev。target。checked);};

這樣點選全選框的時候,就可以實現全部選中,和取消全選的功能了。效果如圖:

小白學程式碼使用原生JS寫購物車模組過程中的踩坑總結

全選與取消全選

全選和取消全選的功能完成之後,下面開始完善逐個勾選商品,直至勾選全部商品,讓全選按鈕自動變成被選中的狀態。

要完成這個功能,我們可以透過對每個商品的複選框新增一個

change

事件來監聽

checked

的變化。因此需要透過

forEach()

方法對遍歷每一個商品。

checkItems。forEach(item => item。onchange = ev => { // 在這裡處理每一項的checked值});

此時,我們可以這樣考慮:

當每個商品的複選框都被勾選,即:所有商品複選框的checked的值全部為true時,全選複選框才會顯示被勾選的狀態,也就是全選複選框的checked的值也要為true。

由於checkAll的狀態依賴於每一項商品的

checked

值,那麼可以利用一個數組函式:

Array。every()

遍歷每一項商品,當所有商品的

checked

值都為

true

時,

every()

方法的返回值就是一個true,然後再賦值給checkAll即可。

注意:由於我們拿到的checkItems是一個NodeList陣列,需要先將其轉換成陣列後再進行操作。

checkItems。forEach(item => item。onchange = ev => { checkAll。checked = Array。from(checkItems)。every(checkItem => checkItem。checked);});

小白學程式碼使用原生JS寫購物車模組過程中的踩坑總結

點選選中每個商品

至此,全選和單選功能全部完成了。下面開始寫自動計算金額的和總數的功能。

購物車的數量和金額不僅包含每一項商品的數量和每一項商品的總金額,還包含了計算選中的商品總數,以及所有選中的商品的總金額。

下面首先完成單個商品的總金額計算,

總金額 = 單價 * 數量

,根據這個公式,我們首先拿到商品的單價和數量元素。

// 獲取單價組成的陣列const priceLists = document。querySelectorAll(‘。price’);// 獲取數量組成的陣列const numberLists = document。querySelectorAll(‘body input[type=number]’);

以上

單價(priceLists)

數量(numberLists)

都是

NodeList

型別的,需要先將它們轉換成陣列,由於表單中獲取的內容都是

string

型別,而參與計算的需要的是整型,所以這裡需要進行一下轉換,使用

parseInt()

方法即可。

// 獲取商品單價組成的陣列const priceLists = document。querySelectorAll(‘。price’);const priceArr = Array。from(priceLists)。map(item => parseInt(item。textContent)); // [ 4799, 3999, 18999, 5999, 9699 ]// 獲取商品數量組成的陣列const numberLists = document。querySelectorAll(‘body input[type=number]’);const numbersArr = Array。from(numberLists)。map(item => parseInt(item。value)); // 預設值:[ 1, 1, 1, 1, 1 ]

注意:商品價格和商品數量在取值時有些不同。商品的單價是普通元素直接使用textContent即可拿到它內部的值,而數量這個用的是表單控制元件,所以需要使用value才可以拿到值。

我剛開始寫這個功能的時候懵逼了半天,此處一定要注意。

拿到商品的單價和數量之後就可以按照上面的公式進行計算了,由於商品的價格和商品的數量都是一個數組,並且價格和數量在陣列中都是一一對應的關係,因此可以使用JS陣列的

reduce()

方法進行遍歷。

let amountArr = [priceArr, numbersArr]。reduce((prev, curr) => { return prev。map((item, index) => { return item * curr[index]; });});

總感覺上述寫法有點怪怪的,是不是可以進行簡化呢?根據箭頭函式的特徵,當只有一條返回語句的時候可以省略掉return關鍵字和大括號,因此上述方法可以簡寫成下面這樣:

let amountArr = [priceArr, numbersArr]。reduce((prev, curr) => prev。map((item, index) => item * curr[index]));console。log(amountArr); // [ 4799, 3999, 18999, 5999, 9699 ]

(PS:上面的方法我一開始也沒有發現可以簡寫,我是把程式碼發給我朋友看了之後,朋友給我點醒了。還是才疏學淺呀。)

這時已經計算出來了每個商品的總金額,那麼我們將其渲染到頁面中。

// 獲取單個商品總金額的元素陣列const amountDOM = document。querySelectorAll(‘。amount’);amountDOM。forEach((item, index) => item。textContent = amountArr[index]);

小白學程式碼使用原生JS寫購物車模組過程中的踩坑總結

計算每個商品的金額並渲染到頁面中

單個商品的總金額渲染到頁面之後,下面就開始計算商品的總數,和總金額了。根據某東、某寶的購物車功能,我們可以發現,總計那裡統計的商品總數是一般是我們勾選上的商品總數,總金額也是一樣的,那麼我們就需要根據商品的狀態來進行計算了。

首先宣告一個數組,用於儲存被選中的商品的狀態,如果被選中,值為1,未被選中,則為0。

let isChecked = [];checkItems。forEach(item => isChecked。push(item。checked === true ? 1 : 0));// 打印出商品狀態值console。log(isChecked);

效果如圖:

小白學程式碼使用原生JS寫購物車模組過程中的踩坑總結

列印商品狀態值

商品的狀態已經記錄好了,那麼現在就需要統計選中的商品對應的數量了。

// 宣告一個用於儲存商品數量的陣列,該陣列的作用是用於與對應的商品的狀態值的陣列進行相乘,得到實際的被選中的商品的陣列。let checkedNumbers = [];numbersArr。forEach((item, index) => checkedNumbers。push(item * isChecked[index]));// 列印被選中的商品的數量console。log(checkedNumbers);

小白學程式碼使用原生JS寫購物車模組過程中的踩坑總結

打印出選中的商品的數量陣列

計算出被選中的商品數量的總數並渲染到頁面中:

let checkedSum = checkedNumbers。reduce((prev, curr) => prev + curr);// 將獲取的數量結果渲染到頁面中document。querySelector(‘#sum’)。textContent = checkedSum;

效果如上圖已經出來了。

下面開始計算被選中的商品的總金額,該總金額等於上面所有被選中的商品的總金額之和。計算出結果之後渲染到頁面中。

// 宣告一個數組用於儲存每一個被選中的商品的總金額let checkedPrice = [];checkedNumbers。forEach((item, index) => checkedPrice。push(item * priceArr[index]));// 列印被選中的每個被選中的商品總金額console。log(checkedPrice);// 計算被選中的商品總金額let totalAmount = checkedPrice。reduce((prev, curr) => prev + curr);// 將選中的商品總金額渲染到頁面中document。querySelector(‘#total-amount’)。textContent = totalAmount;

效果圖:

小白學程式碼使用原生JS寫購物車模組過程中的踩坑總結

將總金額渲染到頁面

至此,關於計算單個商品的總金額以及被選中商品的數量和總金額的功能已經全部完成了,但是我們還需要實現在頁面載入以及更改某個商品數量時自動計算的功能。那麼就需要將上述的計算功能封裝成一個函式,以便後面每一次執行計算時使用。

封裝後的程式碼如下:

function autoCalculate() { // 獲取單價組成的陣列 const priceLists = document。querySelectorAll(‘。price’); const priceArr = Array。from(priceLists)。map(item => parseInt(item。textContent)); // 獲取數量組成的陣列 const numberLists = document。querySelectorAll(‘body input[type=number]’); const numbersArr = Array。from(numberLists)。map(item => parseInt(item。value)); console。log(priceArr, numbersArr); // 由於拿到的表單裡的資料都是string型別的,所以需要先將其轉換成int型別,因此需要使用`map()`方法操作一下 let amountArr = [priceArr, numbersArr]。reduce((prev, curr) => prev。map((item, index) => item * curr[index])); console。log(amountArr); const amountDOM = document。querySelectorAll(‘。amount’); amountDOM。forEach((item, index) => item。textContent = amountArr[index]); // 首先宣告一個數組,用於儲存被選中的商品的狀態,如果被選中,值為1,未被選中,則為0 let isChecked = []; checkItems。forEach(item => isChecked。push(item。checked === true ? 1 : 0)); console。log(isChecked); // 宣告一個用於儲存是商品數量的陣列,該陣列的作用是:如果商品處於被選中的狀態,那麼就儲存它真實的數量值, // 如果沒有被選中,那麼數量就是0 let checkedNumbers = []; numbersArr。forEach((item, index) => checkedNumbers。push(item * isChecked[index])); console。log(checkedNumbers); // 此時,被選中的商品的總數為: let checkedSum = checkedNumbers。reduce((prev, curr) => prev + curr); console。log(checkedSum); // 將獲取的數量結果渲染到頁面中 document。querySelector(‘#sum’)。textContent = checkedSum; // 下面開始計算被選中的商品的總金額,該總金額等於上面所有被選中的商品的總金額之和。 // 宣告一個數組用於儲存每一個被選中的商品的總金額 let checkedPrice = []; checkedNumbers。forEach((item, index) => checkedPrice。push(item * priceArr[index])); console。log(checkedPrice); // 計算被選中的商品總金額 let totalAmount = checkedPrice。reduce((prev, curr) => prev + curr); // 將選中的商品總金額渲染到頁面中 document。querySelector(‘#total-amount’)。textContent = totalAmount;}

將程式碼封裝後我們會發現,單個商品的總金額,商品總數以及總金額的值都沒了,如下圖:

小白學程式碼使用原生JS寫購物車模組過程中的踩坑總結

封裝程式碼後的效果

這是因為,程式碼在第一次載入的時候並沒有執行封裝後的函式,因此需要加一行程式碼:

// 頁面第一次載入的時候自動執行一次。window。onload = autoCalculate;

這樣頁面中的資料在第一次載入的時候就全部都正常了。

下面完成最後一個功能:調整商品的數量,會自動計算總數和金額。該功能還是透過change事件監聽某個表單資料的變化來完成。效果圖下圖:

小白學程式碼使用原生JS寫購物車模組過程中的踩坑總結

程式碼實現:

// 監聽某個控制元件的事件,首先需要拿到控制元件元素。const numInput = document。querySelectorAll(‘body input[type=number]’);// 上面都用了onchange來監聽,這裡換個方法使用addEventListener。numInput。forEach(item => item。addEventListener(‘change’, autoCalculate));

但是我們會發現這裡有個小bug,就是如果勾選沒有選中的商品,並不會自動計算商品數量和總價,原因很簡單,我們在監聽單個商品選中和全選的時候根本就沒有執行自動計算函式,只需要在二者的事件監聽中加上自動計算的函式即可。

checkAll。onchange = ev => { checkItems。forEach(item => item。checked = ev。target。checked); // 解決勾選全選框不會自動計算的bug autoCalculate();};checkItems。forEach(item => item。onchange = ev => { checkAll。checked = Array。from(checkItems)。every(checkItem => checkItem。checked); // 解決勾選全選框不會自動計算的bug autoCalculate();});

寫到這裡,我們購物車的所有功能都已經完成了。購物車這個模組看似不難,其實這裡面的坑也是不少的,例如:

在操作獲取的元素節點時,我們有時候需要將其轉換成一個數組才可以使用陣列函式進行操作,因為我們透過

document.querySelector獲取的元素並不是一個真正的陣列,而是一個類陣列

(NodeList);

案例中使用了多個數組函式,Array。from()、Array。reduce()、Array。every()等等,由此可見,熟練掌握JS常用的陣列也是非常重要的;

掌握事件監聽:addEventListener;

箭頭函式的簡寫方法;

表單事件的監聽,只能透過onchange方法,千萬不要使用onclick。

以上就是我個人在寫這個購物車功能的全部新的,由於本人也是新手,可能還有其他更簡介方便的寫法,如果有問題,請各位大佬批評指正,不勝感激。

如果有剛開始學習JS的同學,想要原始碼的各位親,可以關注並私信回覆“購物車”即可。