
批改狀態(tài):合格
老師批語:其實,全選案例的判斷單個復選框狀態(tài),使用every()方法一行就可以搞定了, 不需要計算, 我昨天將源碼放到了群中, 不過,你的思路也非常的棒, 畢竟代碼的可讀性應該放在第一位
- 全選復選框勾選時,所有商品單項全選,全選不勾選時,所有商品單項也不勾選
- 商品單項有一項不勾選時,全選復選框自動不勾選,當所有商品單項勾選時,全選復選框則自動勾選
- 商品單項數(shù)量影響商品單項總價
- 商品單項數(shù)量影響商品總數(shù)量
- 商品單項數(shù)量影響商品總價格
- 商品單項是否勾選影響商品總數(shù)量
- 商品單項是否勾選影響商品總價格
雖然后面采用了原生JS、jquery和vuejs實現(xiàn),但基本思路都是一樣,尤其是核心問題的解決問題的思路。
第1項 其實就是將全選復選框的狀態(tài)賦值給每一個單項選擇即可,這樣全選和單選就同步了,無論是勾選還是不選。
第2項 我的思路和老師一樣,不過實現(xiàn)方式不一樣,老師是使用計數(shù)器,通過計數(shù)器是否等于總數(shù)來決定全選是否勾選。我的是直接 統(tǒng)計勾選的個數(shù),和總數(shù)比較 ,相等則自動勾選全選,不等則不勾選。
第3、4、5項 為所有商品數(shù)量輸入框添加事件,在事件函數(shù)中可對商品單項總價、商品總數(shù)量和商品總價格計算。 難點在于獲取是第幾個商品數(shù)量輸入框,這樣可改變對應商品單項總價 。老師所有都是采用數(shù)組遍歷,我認為不合理,因為商品單項數(shù)量改變時其單項總價只要計算它就可以,所有都計算一遍,目前只有幾個商品沒關系,若是幾百個就會浪費不必要的時間了,降低了效率。這時就需要知道是哪個商品數(shù)量改變了,這就需要事件監(jiān)聽器添加索引就可以,可惜直接添加是無效的。下面是正確添加方式,大家可以試著理解為什么這樣就可以了??jquery也有類似實現(xiàn),至于vuejs則是雙向綁定就簡單多了。
// 監(jiān)聽事件傳參不能直接傳參數(shù),而是通過function((ev){函數(shù)名(ev,自定義參數(shù))}) ******
for (let [index, item] of checkItemBtns.entries()) {
item.addEventListener( 'click', function (ev) { checkItem(ev, index); }, false );
}
第5、6項 商品單項是否勾選將影響商品總數(shù)量和總價格,至于數(shù)量和商品單項總價則不受影響。我的解決思路就是設置開關,對每個商品設置開關,若勾選則為1時計算它,不勾選則為0不計算它。這里說明下原生JS和jquery中目前唯一問題是開關對單項商品總價有影響,當然可以加兩行代碼就解決了。至于vuejs則全部實現(xiàn)了所有功能。
這里原生JS實現(xiàn)購物車基本就是按上面思路來完成的,具體實現(xiàn)可看下面源碼。這里唯一強調(diào)一點就是商品單項數(shù)量改變是只會改變它的單項總價、總數(shù)量和總價格這三個位置。實現(xiàn)就是用上面原理中說的給事件監(jiān)聽器的回調(diào)函數(shù)傳遞參數(shù),也是事件監(jiān)聽器的進階用法了,大家可以參考下。
至于我勾選單項影響單項總價、總數(shù)量和總價格三個位置,如果你認為不合理可以簡單修改就可以。下面jq也是如此效果,vuejs則只影響總數(shù)量和總價格了。
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
table {
border-collapse: collapse;
width: 90vw;
min-width: 680px;
text-align: center;
margin: auto;
}
table caption {
font-size: 1.5em;
margin-bottom: 15px;
}
table th,
table td {
border: 1px solid;
padding: 5px;
}
table thead tr {
background-color: lightblue;
}
table tbody tr:nth-child(even) {
background-color: lightcyan;
}
table input {
text-align: center;
line-height: 2em;
}
table input[type='checkbox'] {
width: 1.5em;
height: 1.5em;
}
.btns {
width: 90%;
margin: 1em auto;
display: flex;
justify-content: flex-end;
}
button {
width: 8em;
height: 2em;
outline: none;
border: none;
background-color: seagreen;
color: white;
letter-spacing: 1em;
}
</style>
<table>
<caption>購物車</caption>
<thead>
<tr>
<th>
<input type="checkbox" name="checkAll" id="check-all" checked />
<label for="check-all">全選</label>
</th>
<th>ID</th>
<th>品名</th>
<th>單位</th>
<th>單價/元</th>
<th>數(shù)量</th>
<th>金額/元</th>
</tr>
</thead>
<tbody>
<tr>
<td>
<input type="checkbox" name="itemId" value="SN-1010" checked />
</td>
<td>SN-1010</td>
<td>MacBook Pro電腦</td>
<td>臺</td>
<td>18999</td>
<td>
<input type="number" name="counter" value="1" min="1" step="1" />
</td>
<td></td>
</tr>
<tr>
<td>
<input type="checkbox" name="itemId" value="SN-1020" checked />
</td>
<td>SN-1020</td>
<td>iPhone手機</td>
<td>部</td>
<td>4999</td>
<td>
<input type="number" name="counter" value="2" min="1" step="1" />
</td>
<td></td>
</tr>
<tr>
<td>
<input type="checkbox" name="itemId" value="SN-1030" checked />
</td>
<td>SN-1030</td>
<td>智能AI音箱</td>
<td>只</td>
<td>399</td>
<td>
<input type="number" name="counter" value="3" min="1" step="1" />
</td>
<td></td>
</tr>
<tr>
<td>
<input type="checkbox" name="itemId" value="SN-1040" checked />
</td>
<td>SN-1040</td>
<td>SSD移動硬盤</td>
<td>個</td>
<td>888</td>
<td>
<input type="number" name="counter" value="4" min="1" step="1" />
</td>
<td></td>
</tr>
<tr>
<td>
<input type="checkbox" name="itemId" value="SN-1050" checked />
</td>
<td>SN-1050</td>
<td>黃山毛峰</td>
<td>斤</td>
<td>999</td>
<td>
<input type="number" name="counter" value="5" min="1" step="1" />
</td>
<td></td>
</tr>
</tbody>
<tfoot>
<tr>
<td colspan="5">總計:</td>
<td id="total-num"></td>
<td id="total-amount"></td>
</tr>
</tfoot>
</table>
<div class="btns">
<button>結算</button>
</div>
<script>
// 為了同步選擇項,建立數(shù)組,其值為1為選中,0為未選中,(實現(xiàn)5,6,通過1或0判斷單項是否有效來控制)
let checkArr = [];
// 一、全選和自選(實現(xiàn)1,2)
// 獲取全選按鈕
const checkAllBtn = document.querySelector('#check-all');
// 獲取單項選擇按鈕
const checkItemBtns = document.querySelectorAll('input[name="itemId"]');
// 全選按鈕添加點擊事件,將它的狀態(tài)賦值給所有單項選擇按鈕
checkAllBtn.addEventListener('click', checkAll, false);
function checkAll(ev) {
for (let [index, item] of checkItemBtns.entries()) {
checkArr[index] = ev.target.checked ? 1 : 0;
item.checked = ev.target.checked;
}
autoCount();
}
// 所有單項選擇按鈕添加點擊事件,老師用change,這里用click事件感覺更為合理,當然效果目前是一樣。
// 監(jiān)聽事件傳參不能直接傳參數(shù),而是通過function((ev){函數(shù)名(ev,自定義參數(shù))}) ******
for (let [index, item] of checkItemBtns.entries()) {
// 初始化單項狀態(tài)
checkArr[index] = item.checked ? 1 : 0;
item.addEventListener(
'click',
function (ev) {
checkItem(ev, index);
},
false
);
}
function checkItem(ev, index) {
checkArr[index] = checkItemBtns[index].checked ? 1 : 0;
// 單項選擇按鈕點擊時,就獲取單項選擇按鈕選中的個數(shù)
const selectItemBtns = document.querySelectorAll('input[name="itemId"]:checked');
// 若選中個數(shù)等于總個數(shù)則全選,否則取消全選。老師是用計數(shù)器的方法,感覺沒這種方法簡潔明了。
checkAllBtn.checked = selectItemBtns.length == checkItemBtns.length ? true : false;
autoCount();
}
// 二、自動計算(實現(xiàn)3,4,5)
// 獲取單價元素組
const unitPriceArr = document.querySelectorAll('tbody>tr>td:nth-child(5)');
// console.log(unitPriceArr[0].innerHTML);
// 獲取數(shù)量輸入框元素組
const inputCounterArr = document.querySelectorAll('input[name="counter"]');
// console.log(inputCounterArr[1].value);
// 獲取單項總價元素組
const unitPriceTotalArr = document.querySelectorAll('tbody>tr>td:nth-child(7)');
// console.log(unitPriceTotalArr[unitPriceTotalArr.length-1].innerHTML);
// 為數(shù)量輸入框添加變化事件
for (let inputCounter of inputCounterArr) {
inputCounter.addEventListener('change', autoCount, false);
}
window.addEventListener('load', autoCount, false);
function autoCount() {
console.log(checkArr);
// 獲取輸入數(shù)量數(shù)組,parseInt轉(zhuǎn)換為整數(shù)壓入數(shù)組
let inputArr = [];
let unitTotal = 0;
for (let [index, inputCounter] of inputCounterArr.entries()) {
inputArr.push(parseInt(inputCounter.value) * checkArr[index]);
}
// 計算數(shù)量總和并賦值給統(tǒng)計的總數(shù),數(shù)組reduce是求和的利器。
document.querySelector('#total-num').innerHTML = inputArr.reduce((a, b) => a + b, 0);
// 改變單項總價并累加到總價上
for (let [index, unitPriceTotal] of unitPriceTotalArr.entries()) {
unitTotal += inputArr[index] * unitPriceArr[index].innerHTML * checkArr[index];
unitPriceTotal.innerHTML = inputArr[index] * unitPriceArr[index].innerHTML;
}
// 給總價賦值
document.querySelector('#total-amount').innerHTML = unitTotal;
}
</script>
jquery中頁面和CSS都和原生一樣,這里就不重復了。這里重點說下幾個注意點:
- input的checked獲取和設置問題 我們知道input的checked是布爾值,但attr()獲取是字符串,這里是使用prop代替解決問題,記得也有問這個問題,查了網(wǎng)上資料也是難懂,還是拿出我們的利器:自己測試嗎!
<label for="c1">c1:</label>
<input id="c1" name="checkbox" type="checkbox" checked="checked" />
<label for="c2">c2:</label>
<input id="c2" name="checkbox" type="checkbox" checked="true" />
<label for="c3">c3:</label>
<input id="c3" name="checkbox" type="checkbox" checked="" />
<label for="c4">c4:</label>
<input id="c4" name="checkbox" type="checkbox" checked />
<label for="c5">c5:</label>
<input id="c5" name="checkbox" type="checkbox" />
<label for="c6">c6:</label>
<input id="c6" name="checkbox" type="checkbox" checked="false" />
<script>
// attr
console.log('c1 attr => ',$('#c1').attr('checked'));
console.log('c2 attr => ',$('#c2').attr('checked'));
console.log('c3 attr => ',$('#c3').attr('checked'));
console.log('c4 attr => ',$('#c4').attr('checked'));
console.log('c5 attr => ',$('#c5').attr('checked'));
console.log('c6 attr => ',$('#c6').attr('checked'));
// prop
console.log('c1 prop => ',$('#c1').prop('checked'));
console.log('c2 prop => ',$('#c2').prop('checked'));
console.log('c3 prop => ',$('#c3').prop('checked'));
console.log('c4 prop => ',$('#c4').prop('checked'));
console.log('c5 prop => ',$('#c5').prop('checked'));
console.log('c6 prop => ',$('#c6').prop('checked'));
</script>
這里要說明下,上面是測試代碼是改進于腳本之家網(wǎng)站一篇文章,但是文章作者的結論我不贊同,我增加了內(nèi)置屬性和自定義屬性測試,得出了attr和prop的區(qū)別:
- 元素 內(nèi)置屬性并且指明 時,attr和prop都是返回屬性值。
- 元素 內(nèi)置屬性未指明時,attr返回undefined,prop返回false。
- 元素 內(nèi)置屬性的屬性值只有true或false時,如checked、selected和disabled等,attr返回屬性名字符串,指明屬性名時prop返回true,未指明時返回false。
- 元素 自定義屬性如data-xxx等,attr正常獲取屬性值,而prop則無效,返回undefined。
- jQuery自帶迭代(自帶循環(huán))如何獲取第幾個成員? 當然可以使用原生JS中for…of方法獲取,這里說下jq本身就支持的簡便方法 index ,它 返回數(shù)組中某成員的索引 。代碼就是:
let index=$('selector').index(this);
該解決的問題都進行了單獨說明,下面就是實現(xiàn)了,基本是由原生JS轉(zhuǎn)換過來的,除了上面兩點,其它也沒有什么了。
let checkArr = [];
$('input[name="itemId"]').each((index, item) => (checkArr[index] = $(item).prop('checked') ? 1 : 0));
// 一、全選和自選(實現(xiàn)1、2)
// 獲取單值如checked
$('#check-all').click(function () {
$('input[name="itemId"]').prop('checked', $(this).prop('checked'));
$('input[name="itemId"]').each((index, item) => (checkArr[index] = $(item).prop('checked') ? 1 : 0));
autoCount();
});
$('input[name="itemId"]').click(function () {
// jq中快速獲取是第幾個成員的方法
let index = $('input[name="itemId"]').index(this);
checkArr[index] = $(this).prop('checked') ? 1 : 0;
$('#check-all').prop('checked', $('input[name="itemId"]:checked').length == $('input[name="itemId"]').length);
autoCount();
});
// 二、自動計算(實現(xiàn)3,4,5)
window.addEventListener('load', autoCount, false);
// 獲取單價元素組
let unitPriceArr = [];
$('tbody>tr>td:nth-child(5)').each((index, item) => (unitPriceArr[index] = item.innerHTML * 1));
$('input[name="counter"]').each((index, item) => { $(item).change(function (ev) { autoCount(ev, index); }); });
function autoCount(ev, index) {
let goodNumArr = [];
let totalAmount = 0;
$('input[name="counter"]').each((index, item) => (goodNumArr[index] = $(item).val() * 1 * checkArr[index]));
$('#total-num').html(goodNumArr.reduce((a, b) => a + b, 0));
$('tbody>tr>td:nth-child(7)').each((index, item) => {
totalAmount += unitPriceArr[index] * goodNumArr[index] * checkArr[index];
$(item).html(unitPriceArr[index] * goodNumArr[index]);
});
$('#total-amount').html(totalAmount);
}
本來是完成老師作業(yè)就完事了,后來又聽了老師vuejs的課,尤其是老師用原生JS模擬實現(xiàn)和解釋vuejs本質(zhì)后,讓我思路一下就開闊了,原生JS是這些一切的根基,jquery函數(shù)庫、vuejs、react都是在其基礎上進行了封裝,更便于開發(fā)者開發(fā),同時也降低了開發(fā)者對原生JS理解。在這些庫或框架中都可混用原生JS,也可用原生JS實現(xiàn),掌握了原生JS學習這些框架或庫都是隨手拈來。通過這幾天原生JS學習,算是原生JS真正入門了,學習jquery和vuejs比以前只知道使用深刻多了,不再出現(xiàn)使用什么解決問題了,具體查手冊就可以了,隨著累積相信可以應付絕大部分問題。
vuejs在數(shù)據(jù)處理方面最強大就是雙向綁定了,而購物車則也是這方面需求,我就試著改造成vuejs實現(xiàn),通過測試終于完成了,下面進行簡單說明
頁面中數(shù)據(jù)雙向綁定 數(shù)據(jù)雙向綁定一定要使用v-model ,我開發(fā)過uniapp,使用最多的變量方式是:冒號,結果測試時只能隨著vuejs中data數(shù)據(jù)而改變,它不能改變綁定該變量的值,后來還是使用v-model實現(xiàn)的,這里只是強調(diào),雙向綁定v-model指令不可少。
計算屬性如何傳參數(shù) 老師演示了計算屬性并解釋了本質(zhì),在本案例中計算單項商品總價格時,只想改變單項商品對應的總價格,如文章開頭所說,需要知道索引,查了vuejs官方文檔,知道了計算屬性默認是get方法,還有set方法,但是不滿足本題要求,后來突然想到不能傳遞參數(shù)是因為 計算屬性一般情況下返回值是表達式,所以不能接受參數(shù) ,那么 如果定義為返回值是函數(shù)呢?測試了下,成功 。一個非常有用的技巧呢!?。≌б豢春蜕厦姹O(jiān)聽事件中的回調(diào)函數(shù)傳遞參數(shù)實現(xiàn)類似,都是將返回值轉(zhuǎn)成函數(shù),從而接受參數(shù)。
本實現(xiàn)方法中全選和自選都是能完監(jiān)聽數(shù)據(jù)變化來完成,這樣就存在一個問題,就是自選導致全選監(jiān)聽再一次執(zhí)行全選監(jiān)聽,所有我們必須知道全選變化是手動還是自動,通過給全選添加自定義方法,設置開關就解決了。
<table>
<caption>購物車</caption>
<thead>
<tr>
<th>
<input type="checkbox" name="checkAll" id="check-all" v-model="checkAll" v-on:input="chekcAllStatus" />
<label for="check-all">全選</label>
</th>
<th>ID</th>
<th>品名</th>
<th>單位</th>
<th>單價/元</th>
<th>數(shù)量</th>
<th>金額/元</th>
</tr>
</thead>
<tbody>
<tr>
<td>
<input type="checkbox" name="itemId" value="SN-1010" v-model="checkArr[0]" />
</td>
<td>SN-1010</td>
<td>MacBook Pro電腦</td>
<td>臺</td>
<td>{{unitPriceArr[0]}}</td>
<td>
<input type="number" name="counter" v-model:value="inputArr[0]" min="1" step="1" />
</td>
<td>{{unitTotalPrice(0)}}</td>
</tr>
<tr>
<td>
<input type="checkbox" name="itemId" value="SN-1020" v-model="checkArr[1]" />
</td>
<td>SN-1020</td>
<td>iPhone手機</td>
<td>部</td>
<td>{{unitPriceArr[1]}}</td>
<td>
<input type="number" name="counter" v-model:value="inputArr[1]" min="1" step="1" />
</td>
<td>{{unitTotalPrice(1)}}</td>
</tr>
<tr>
<td>
<input type="checkbox" name="itemId" value="SN-1030" v-model="checkArr[2]" />
</td>
<td>SN-1030</td>
<td>智能AI音箱</td>
<td>只</td>
<td>{{unitPriceArr[2]}}</td>
<td>
<input type="number" name="counter" v-model:value="inputArr[2]" min="1" step="1" />
</td>
<td>{{unitTotalPrice(2)}}</td>
</tr>
<tr>
<td>
<input type="checkbox" name="itemId" value="SN-1040" v-model="checkArr[3]" />
</td>
<td>SN-1040</td>
<td>SSD移動硬盤</td>
<td>個</td>
<td>{{unitPriceArr[3]}}</td>
<td>
<input type="number" name="counter" v-model:value="inputArr[3]" min="1" step="1" />
</td>
<td>{{unitTotalPrice(3)}}</td>
</tr>
<tr>
<td>
<input type="checkbox" name="itemId" value="SN-1050" v-model="checkArr[4]" />
</td>
<td>SN-1050</td>
<td>黃山毛峰</td>
<td>斤</td>
<td>{{unitPriceArr[4]}}</td>
<td>
<input type="number" name="counter" v-model:value="inputArr[4]" min="1" step="1" />
</td>
<td>{{unitTotalPrice(4)}}</td>
</tr>
</tbody>
<tfoot>
<tr>
<td colspan="5">總計:</td>
<td id="total-num">{{totalNum}}</td>
<td id="total-amount">{{totalPrice}}</td>
</tr>
</tfoot>
</table>
<script>
const vm = new Vue({
el: 'table',
data: {
checkAll: true,
checkArr: [true, true, true, true, true],
// 開關,防止全選和單選沖突
checkStatus: false,
unitPriceArr: [18999, 4999, 399, 888, 999],
inputArr: [3, 2, 3, 4, 5],
},
computed: {
totalNum: function () {
// return this.inputArr.reduce((a, b) => a * 1 + b * 1, 0);
let total = 0;
for (let [index, input] of this.inputArr.entries()) {
total += input * 1 * (this.checkArr[index] ? 1 : 0);
}
return total;
},
unitTotalPrice: function () {
return index => {
return this.unitPriceArr[index] * 1 * this.inputArr[index];
};
},
totalPrice: function () {
let total = 0;
for (let [index, input] of this.inputArr.entries()) {
total += this.unitPriceArr[index] * 1 * input * (this.checkArr[index] ? 1 : 0);
}
return total;
},
},
watch: {
checkAll(newVal) {
// 雙向綁定數(shù)組無法直接賦值,需要轉(zhuǎn)換為普通的,計算完后,使用vuejs改變即可
if (this.checkStatus) {
let checkArr = JSON.parse(JSON.stringify(this.checkArr));
for (let index of checkArr.keys()) {
checkArr[index] = newVal;
}
this.checkArr = checkArr;
this.checkStatus = false;
}
},
checkArr() {
let checkArr = JSON.parse(JSON.stringify(this.checkArr));
let checkOkArr = checkArr.filter(item => item == true);
this.checkAll = checkArr.length == checkOkArr.length ? true : false;
},
},
methods: {
chekcAllStatus: function () {
// 區(qū)分是用戶選擇全選,還是單項導致自動全選
this.checkStatus = true;
},
},
});
</script>
原生JS、jquery函數(shù)庫和vuejs框架基本學完了,前端也快基本告以段落了,朱老師深入淺出的授課讓我對前端也完成了系統(tǒng)的梳理,相比報前最煩惱的就是前端,無論是CSS還是JS,都感覺亂,需要就百度來東拼本西湊,最后一團亂麻。現(xiàn)在對前端已經(jīng)有了系統(tǒng)的認識,現(xiàn)在無論是語義化元素,還是CSS布局,或是JS的使用,都能快速找到解決思路,個別不清楚查下手冊就可以了。我的理解都已經(jīng)寫在我的博文中了,可以說好多都比網(wǎng)上大部分文章介紹更系統(tǒng)更深入,相信你看了之后也會對前端有了系統(tǒng)的認識。
Copyright 2014-2025 http://ipnx.cn/ All Rights Reserved | php.cn | 湘ICP備2023035733號