Theme NexT works best with JavaScript enabled

ShunNien's Blog

不積跬步,無以致千里;不積小流,無以成江海。

0%

JS30紀錄 10-Hold Shift and Check Checkboxes

目標

target
類似 Gmail 信箱的 checkbox 多選功能,當選取一個 checkbox 後,按住 shift 不放,在選取另一 checkbox ,此兩個 checkbox 中間的 checkbox 皆會選取。

Demo | Github

處理步驟

與範本的方法與步驟可能不太相同,依照個人的方式去解決就好。

步驟 1.

取得所有的 checkbox DOM 並綁定 click 事件,建立的 handleCheck function 中可以利用 console 查看一下 e 的物件,為 MouseEvent

1
2
3
4
5
6
const checkboxes = document.querySelectorAll('.inbox input[type="checkbox"]');
function handleCheck(e) {
// e is MouseEvent
console.log(e);
}
Array.from(checkboxes).map(ele => ele.addEventListener("click", handleCheck));

步驟 2.

開始針對 handleCheck function 撰寫判斷,首先是 shift 鍵按下並選取該 checkbox 時,進行全部的 checkbox DOM 巡覽。
這時候調整一下 checkboxes 變數,因為 querySelectorAll 取得的結果不是 Array ,所以先轉換

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
const checkboxes = Array.from(
document.querySelectorAll('.inbox input[type="checkbox"]')
);

function handleCheck(e) {
// e is MouseEvent
//console.log(e);
const self = this;
if (e.shiftKey && e.target.checked) {
console.info("this is shift & checked");
checkboxes.map(ele => {
});
}
}

checkboxes.map(ele => ele.addEventListener("click", handleCheck));

步驟 3.

在巡覽的過程中,加入判斷式,決定要變更 checkbox 選取狀態的變更。

  • 加入 checkEle 變數紀錄 click 每次觸發後,上次的選取 DOM
  • 判斷 目前觸發元素是否等於巡覽的元素,或是 checkEle 是否等於巡覽的元素,並且 checkEle 不能等於目前觸發元素
  • 加入 isBetween 變數,用來記錄是否符合上述的條件
  • 最後將巡覽的所有元素 checked 都設定為 true 的值
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
let checkEle = null;
function handleCheck(e) {
// e is MouseEvent
//console.log(e);
const self = this;
if (e.shiftKey && e.target.checked) {
//console.info("this is shift & checked");
let isBetween = false;
checkboxes.map(ele => {
// 避免選取第一個項目就全選
if ((self === ele || checkEle === ele) && checkEle && checkEle !== self) {
// console.info("slef equal ele");
// console.dir(ele);
isBetween = !isBetween;
}
if (isBetween) ele.checked = true;
});
}
checkEle = self.checked ? self : null;
}

checkboxes.map(ele => ele.addEventListener("click", handleCheck));

延伸部分

此解法,是按照範例的方式去處理;延伸課題是仿照類似 Gmail 的選取方式選取區間,所以需要取得距離目前選取最近的選取框

  • 建立 extenCheck function
  • 找尋索引小於目前選取元素的索引,並且有選取的 checkbox ,取得此索引
  • 找尋索引大於目前選取元素的索引,並且有選取的 checkbox ,取得此索引
  • 判斷前 2 操作都是有取得索引後,直接更新選取狀態
  • 抽取選取狀態 function
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
/**
* 延伸部分
*
* @param {any} e
*/
function extenCheck(e) {
const self = this;
if (!e.target.checked) return;

// 目前的選取元素
const nowSelIndex = checkboxes.findIndex(ele => ele === self);

if (!e.shiftKey) return;

const nearMinCheckboxIndex = checkboxes.findIndex(
(ele, index) =>
index !== nowSelIndex && index < nowSelIndex && ele.checked === true
);
const nearMaxCheckboxIndex = checkboxes.findIndex(
(ele, index) =>
index !== nowSelIndex && index > nowSelIndex && ele.checked === true
);

if (nearMaxCheckboxIndex !== -1) {
setCheckbox(nowSelIndex, nearMaxCheckboxIndex);
}

if (nearMinCheckboxIndex !== -1) {
setCheckbox(nearMinCheckboxIndex, nowSelIndex);
}
}

/**
* 設定 checkboxes 為選取
*
* @param {any} initIndex 索引初始
* @param {any} aryLength 迴圈長度
*/
function setCheckbox(initIndex, aryLength) {
for (let index = initIndex; index < aryLength; index++) {
checkboxes[index].checked = true;
}
}

exten sample

參考資料

歡迎關注我的其它發布渠道