跳至主要内容

[JS30] Day10: Hold Shift to Check Multiple Checkbox

keywords: checkbox, shift, select

使用 label

在原本的程式碼中,作者並沒有使用 label ,這樣在操作上會比較不方便,因為一定要點選到該 checkbox 才能勾選,因此先在 HTML 中加上 <label>

<label class="item">
<input type="checkbox" />
<p>This is an inbox layout.</p>
</label>

監聽所有的 checkbox

// STEP1: 選擇並監聽所有的 checkbox
const checkboxs = document.querySelectorAll('.inbox input[type="checkbox"]');
checkboxs.forEach((checkbox) => checkbox.addEventListener('click', checkHandler));

按住 shift 導致文字被圈選的問題

在我們把原本的 <div> 改成用 <label> 雖然可以直接點選項目(文字部分)就來勾選和取消勾選 checkbox,但卻也多了一個問題,就是當使用者按住 shift 點選文字時,會把文字圈選起來,變成沒辦法觸發 click 事件。

img

這時候有一個簡單的解決方式(但不是最好的),就是透過 CSS 讓使用者無法圈選文字,進而不會出現這個問題。我們可以使用 CSS 中的 user-select 這個屬性,設定好後就能夠正常觸發 click 事件:

.item {
display: flex;
align-items: center;
border-bottom: 1px solid #f1f1f1;
user-select: none;
-ms-user-select: none;
-moz-user-select: none;
-webkit-user-select: none;
}

處理 click 事件

建立處理 click 事件的 function

建立一個 function 來處理 click 事件

// STEP2-1 處理 checkbox 被 click 的事件
function checkHandler(e) {
// ...
}

辨認開始和結束的 checkbox

為了要讓使用者按住 Shift 就能勾選所有這中間的項目,我們要知道哪些是兩個 checkbox 的「中間」,因此我們建立一個變數叫做 lastCheck 這是使用者最後一次勾選時的那個 checkbox:

let lastCheck;
function checkHandler(e) {
// STEP2-2 紀錄上一次勾選的是哪個 checkbox
if (e.target.checked) {
lastCheck = e.target;
}
}

現在有了上一次點擊的 checkbox(lastCheck),而這一次點擊的 checkBox 可以直接抓取 e.target 就表示是當前被點擊的那個 checkbox(也可以用 this表示)。

使用迴圈修改每個 checkbox 的狀態

有了開始(lastCheck)和結束(e.target),我們再建立一個變數叫做 isBetween 用來判斷這個 checkbox 是不是在開始和結束之間的 checkbox。

我們用 forEach 來疊代每個 checkbox,如果是開始,就把 isBetween 設為 true,如果碰到結束,就把 isBetween 設為 false。如果 isBetween 為 true 就把該 checkbox 設為勾選:

checkBoxs.forEach((checkbox) => {
// STEP2-4 把 isBetween 開啟和關閉(第一次碰到開啟,第二次碰到關閉)
if (checkbox === lastCheck || checkbox === e.target) {
isBetween = !isBetween;
}

// STEP2-5 如果 isBetween 為 true,則把該 checkbox 勾選
if (isBetween) {
checkbox.checked = true;
}
console.log('isBetween', checkbox, isBetween);
});

我們希望只有在使用者按住 shift 且勾選某一個項目的時候才會去執行上面的迴圈,因此我們把上面的 forEach 放在 if 判斷式中:

if (e.target.checked && e.shiftKey) {
// STEP2-3 如果使用者按住 shift 且勾選某個項目
checkBoxs.forEach((checkbox) => {
// STEP2-4 把 isBetween 開啟和關閉(第一次碰到開啟,第二次碰到關閉)
if (checkbox === lastCheck || checkbox === e.target) {
isBetween = !isBetween;
}

// STEP2-5 如果 isBetween 為 true,則把該 checkbox 勾選
if (isBetween) {
checkbox.checked = true;
}
console.log('isBetween', checkbox, isBetween);
});
}

完整的 checkHandler

// STEP2-1 處理 checkbox 被 click 的事件
let lastCheck;
function checkHandler(e) {
let isBetween = false;
if (e.target.checked && e.shiftKey) {
// STEP2-3 如果使用者按住 shift 且勾選某個項目
checkBoxs.forEach((checkbox) => {
// STEP2-4 把 isBetween 開啟和關閉(第一次碰到開啟,第二次碰到關閉)
if (checkbox === lastCheck || checkbox === e.target) {
isBetween = !isBetween;
}

// STEP2-5 如果 isBetween 為 true,則把該 checkbox 勾選
if (isBetween) {
checkbox.checked = true;
}
console.log('isBetween', checkbox, isBetween);
});
}

// STEP2-2 紀錄上一次勾選的是哪個 checkbox
if (e.target.checked) {
lastCheck = e.target;
}
console.log('==========lastCheck=========', lastCheck);
}

完成作品

Day10: Hold Shift to Check Multiple Checkboxs @ Codepen