作者:?緝熙Soyaine
簡介:JavaScript30 是 Wes Bos 推出的一個(gè) 30 天挑戰(zhàn)奸绷。項(xiàng)目免費(fèi)提供了 30 個(gè)視頻教程躏筏、30 個(gè)挑戰(zhàn)的起始文檔和 30 個(gè)挑戰(zhàn)解決方案源代碼。目的是幫助人們用純 JavaScript 來寫東西赊瞬,不借助框架和庫,也不使用編譯器和引用。現(xiàn)在你看到的是這系列指南的第 10 篇豹障。完整指南在 GitHub,喜歡請(qǐng) Star 哦?(?*)
創(chuàng)建時(shí)間:2017-01-07
最后更新:2017-01-07
實(shí)現(xiàn)效果
初始文檔中提供了一組 checkbox
類型的 input
元素焦匈,選中某個(gè)復(fù)選框時(shí)血公,其 <p>
標(biāo)簽中的文字會(huì)顯示刪除線。最終效果是缓熟,提供按下 Shift 鍵后進(jìn)行多選操作的功能累魔。 在線體驗(yàn)請(qǐng)點(diǎn)這里摔笤。
過程指南
- 獲取所有的
<input>
元素,并添加事件監(jiān)聽const boxs = document.querySelectorAll('.inbox input[type="checkbox"]'); boxs.forEach(box => box.addEventListener('click', handleCheck));
- 編寫 handleCheck 內(nèi)部的處理邏輯(細(xì)節(jié)請(qǐng)看下一部分)
解決思路
在談具體的代碼時(shí)垦写,先講講思路吕世。首先來復(fù)現(xiàn)一下,當(dāng)你按下 Shift 鍵進(jìn)行多選時(shí)梯投,發(fā)生了什么命辖?
- 選中 A 項(xiàng)
- 按下 Shift
- 再選中 B 項(xiàng)
- A-B 之間的所有項(xiàng)都被選中
關(guān)鍵點(diǎn)就在于 A、B 劃出了一個(gè)范圍分蓖,在這個(gè)范圍之內(nèi)的元素狀態(tài)發(fā)生了改變尔艇。A 是上一次操作選中的對(duì)象,B 是此次操作對(duì)象么鹤,之后的內(nèi)容將會(huì)用這兩個(gè)單詞來敘述终娃。下面的方案就依據(jù)劃定范圍的方法不同來進(jìn)行區(qū)分。
方法一
Wes Bos 在文檔里提供了一種解決辦法:用一個(gè)變量蒸甜,來標(biāo)記這個(gè)范圍棠耕。
變量初始值為 false
,當(dāng)按下 Shift 鍵且同時(shí)選中了某個(gè)元素的時(shí)候迅皇,遍歷所有項(xiàng)昧辽,遍歷過程中,若遇到 A 或 B登颓,則將標(biāo)記值取反搅荞。同時(shí),將所有標(biāo)記為 true
的項(xiàng)設(shè)置為選中框咙。
let lastChecked;
// 處理方法一:用變量 inBetween 對(duì)需要選中的元素進(jìn)行標(biāo)記
function handleCheck0(e) {
let inBetween = false;
if(e.shiftKey && this.checked){
boxs.forEach(input => {
console.log(input);
if(input === lastChecked || input ===this) {
inBetween = !inBetween;
}
if(inBetween) {
console.log("on");
input.checked = true;
}
});
}
lastChecked = this;
}
延伸思考
上面會(huì)出現(xiàn)一個(gè)問題咕痛,初次加載頁面時(shí),按住 Shift 再點(diǎn)擊某一項(xiàng)喇嘱,此項(xiàng)之后的元素都會(huì)被選中茉贡。此外,對(duì)于取消選中者铜,無法批量操作腔丧。所以我參照了 Stack Overflow 的一個(gè)答案: How can I shift-select multiple checkboxes like GMail? 改進(jìn)得到第二種解決方案。
方法二
方法一中的 inBetween
僅僅表示此項(xiàng)是否在被選中的范圍中作烟,此處會(huì)賦給它更多的意義愉粤,用它來表示此項(xiàng)是選中還是未選中,而范圍劃定則由數(shù)組來解決拿撩。
首先將獲取到的 <input>
組轉(zhuǎn)化為數(shù)組衣厘,針對(duì)每次操作,獲取 A 和 B,利用 indexOf()
來獲得 A 和 B 在數(shù)組中的索引值影暴,由此即可確定范圍错邦,并能通過 slice()
來直接截取 A-B 的所有 DOM 元素,并進(jìn)行狀態(tài)改變的操作型宙,而變量 onOff
表示 A-B 范圍內(nèi)的狀態(tài)撬呢,true
表示選中,false
表示取消選中早歇。
- 轉(zhuǎn)換 Nodelist 為數(shù)組
const boxs = document.querySelectorAll('.inbox input[type="checkbox"]'); const boxArr = Array.from(boxs);
- 針對(duì)按下了 Shift 鍵的情況倾芝,獲取 A-B 范圍
let start = boxArr.indexOf(this); let end = boxArr.indexOf(lastChecked);
- 截取該范圍內(nèi)的數(shù)組元素讨勤,并改變選中狀態(tài)
boxArr.slice(Math.min(start, end), Math.max(start, end) + 1) .forEach(input => input.checked = onOff);
- 確定選中 or 取消選中
onOff = lastChecked.checked ? true : false;
- 標(biāo)記 A 值
if(!lastChecked) lastChecked = this; /* ... */ lastChecked = this;
注意箭跳,以上幾點(diǎn)是按點(diǎn)抽出的分塊代碼,整合起來的解決辦法如下:
const boxArr = Array.from(boxs);
let lastChecked;
let onOff = false;
// 處理方法二:利用數(shù)組索引獲取需要選中的范圍
function handleCheck1(e) {
if(!lastChecked) lastChecked = this;
onOff = lastChecked.checked ? true : false;
if(e.shiftKey) {
let start = boxArr.indexOf(this);
let end = boxArr.indexOf(lastChecked);
boxArr.slice(Math.min(start, end), Math.max(start, end) + 1)
.forEach(input => input.checked = onOff);
console.log(start + "+" + end);
}
lastChecked = this;
}
這樣一來潭千,挑戰(zhàn) 10 就完谱姓!成!啦刨晴!恭喜走完了 1/3 的路程(o)/~