Skip to content

Commit a027761

Browse files
committed
complete 10 and guide
1 parent b1871f0 commit a027761

File tree

5 files changed

+523
-9
lines changed

5 files changed

+523
-9
lines changed
Lines changed: 121 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,121 @@
1+
# 10 JS 实现 Checkboxex 中 Shift 批量多选功能
2+
3+
> 作者:©[缉熙Soyaine](https://github.com/soyaine)
4+
> 简介:[JavaScript30](https://javascript30.com)[Wes Bos](https://github.com/wesbos) 推出的一个 30 天挑战。项目免费提供了 30 个视频教程、30 个挑战的起始文档和 30 个挑战解决方案源代码。目的是帮助人们用纯 JavaScript 来写东西,不借助框架和库,也不使用编译器和引用。现在你看到的是这系列指南的第 10 篇。完整指南在 [GitHub](https://github.com/soyaine/JavaScript30),喜欢请 Star 哦♪(^∇^*)
5+
6+
> 创建时间:2017-01-07
7+
最后更新:2017-01-07
8+
9+
## 实现效果
10+
11+
![shift 多选效果示例](https://cl.ly/422z2e3X350Z/Image%202017-01-07%20at%2012.03.29%20PM.png)
12+
13+
初始文档中提供了一组 `checkbox` 类型的 `input` 元素,选中某个复选框时,其 `<p>` 标签中的文字会显示删除线。最终效果是,提供按下 Shift 键后进行多选操作的功能。
14+
15+
## 过程指南
16+
17+
1. 获取所有的 `<input>` 元素,并添加事件监听
18+
```js
19+
const boxs = document.querySelectorAll('.inbox input[type="checkbox"]');
20+
boxs.forEach(box => box.addEventListener('click', handleCheck));
21+
```
22+
2. 编写 handleCheck 内部的处理逻辑(细节请看下一部分)
23+
24+
## 解决思路
25+
26+
在谈具体的代码时,先讲讲思路。首先来复现一下,当你按下 Shift 键进行多选时,发生了什么?
27+
28+
1. 选中 A 项
29+
2. 按下 Shift
30+
3. 再选中 B 项
31+
4. A-B 之间的所有项都被选中
32+
33+
关键点就在于 A、B 划出了一个范围,在这个范围之内的元素状态发生了改变。A 是上一次操作选中的对象,B 是此次操作对象,之后的内容将会用这两个单词来叙述。下面的方案就依据划定范围的方法不同来进行区分。
34+
35+
### 方法一
36+
37+
Wes Bos 在文档里提供了一种解决办法:用一个变量,来标记这个范围。
38+
39+
变量初始值为 `false`,当按下 Shift 键且同时选中了某个元素的时候,遍历所有项,遍历过程中,若遇到 A 或 B,则将标记值取反。同时,将所有标记为 `true` 的项设置为选中。
40+
41+
```js
42+
let lastChecked;
43+
44+
// 处理方法一:用变量 inBetween 对需要选中的元素进行标记
45+
function handleCheck0(e) {
46+
let inBetween = false;
47+
if(e.shiftKey && this.checked){
48+
boxs.forEach(input => {
49+
console.log(input);
50+
if(input === lastChecked || input ===this) {
51+
inBetween = !inBetween;
52+
}
53+
if(inBetween) {
54+
console.log("on");
55+
input.checked = true;
56+
}
57+
});
58+
}
59+
lastChecked = this;
60+
}
61+
```
62+
63+
> 延伸思考
64+
65+
上面会出现一个问题,初次加载页面时,按住 Shift 再点击某一项,此项之后的元素都会被选中。此外,对于取消选中,无法批量操作。所以我参照了 Stack Overflow 的一个答案: [How can I shift-select multiple checkboxes like GMail?](http://stackoverflow.com/a/659571/6820726) 改进得到第二种解决方案。
66+
67+
### 方法二
68+
69+
方法一中的 `inBetween` 仅仅表示此项是否在被选中的范围中,此处会赋给它更多的意义,用它来表示此项是选中还是未选中,而范围划定则由数组来解决。
70+
71+
首先将获取到的 `<input>` 组转化为数组,针对每次操作,获取 A 和 B,利用 `indexOf()` 来获得 A 和 B 在数组中的索引值,由此即可确定范围,并能通过 `slice()` 来直接截取 A-B 的所有 DOM 元素,并进行状态改变的操作,而变量 `onOff` 表示 A-B 范围内的状态,`true` 表示选中,`false` 表示取消选中。
72+
73+
1. 转换 Nodelist 为数组
74+
```js
75+
const boxs = document.querySelectorAll('.inbox input[type="checkbox"]');
76+
const boxArr = Array.from(boxs);
77+
```
78+
2. 针对按下了 Shift 键的情况,获取 A-B 范围
79+
```js
80+
let start = boxArr.indexOf(this);
81+
let end = boxArr.indexOf(lastChecked);
82+
```
83+
3. 截取该范围内的数组元素,并改变选中状态
84+
```js
85+
boxArr.slice(Math.min(start, end), Math.max(start, end) + 1)
86+
.forEach(input => input.checked = onOff);
87+
```
88+
4. 确定选中 or 取消选中
89+
```js
90+
onOff = lastChecked.checked ? true : false;
91+
```
92+
5. 标记 A 值
93+
```js
94+
if(!lastChecked) lastChecked = this;
95+
/* ... */
96+
lastChecked = this;
97+
```
98+
99+
注意,以上几点是按点抽出的分块代码,整合起来的解决办法如下:
100+
101+
```js
102+
const boxArr = Array.from(boxs);
103+
let lastChecked;
104+
let onOff = false;
105+
106+
// 处理方法二:利用数组索引获取需要选中的范围
107+
function handleCheck1(e) {
108+
if(!lastChecked) lastChecked = this;
109+
onOff = lastChecked.checked ? true : false;
110+
if(e.shiftKey) {
111+
let start = boxArr.indexOf(this);
112+
let end = boxArr.indexOf(lastChecked);
113+
boxArr.slice(Math.min(start, end), Math.max(start, end) + 1)
114+
.forEach(input => input.checked = onOff);
115+
console.log(start + "+" + end);
116+
}
117+
lastChecked = this;
118+
}
119+
```
120+
121+
这样一来,挑战 10 就完!成!啦!恭喜走完了 1/3 的路程\(^o^)/~
Lines changed: 138 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,138 @@
1+
<!DOCTYPE html>
2+
<html lang="en">
3+
<head>
4+
<meta charset="UTF-8">
5+
<title>Document</title>
6+
</head>
7+
<body>
8+
<style>
9+
10+
html {
11+
font-family: sans-serif;
12+
background:#ffc600;
13+
}
14+
15+
.inbox {
16+
max-width:400px;
17+
margin:50px auto;
18+
background:white;
19+
border-radius:5px;
20+
box-shadow:10px 10px 0 rgba(0,0,0,0.1);
21+
}
22+
23+
.item {
24+
display:flex;
25+
align-items:center;
26+
border-bottom: 1px solid #F1F1F1;
27+
}
28+
29+
.item:last-child {
30+
border-bottom:0;
31+
}
32+
33+
34+
input:checked + p {
35+
background:#F9F9F9;
36+
text-decoration: line-through;
37+
}
38+
39+
input[type="checkbox"] {
40+
margin:20px;
41+
}
42+
43+
p {
44+
margin:0;
45+
padding:20px;
46+
transition:background 0.2s;
47+
flex:1;
48+
font-family:'helvetica neue';
49+
font-size: 20px;
50+
font-weight: 200;
51+
border-left: 1px solid #D1E2FF;
52+
}
53+
54+
.details {
55+
text-align: center;
56+
font-size: 15px;
57+
}
58+
59+
60+
</style>
61+
<!--
62+
The following is a common layout you would see in an email client.
63+
64+
When a user clicks a checkbox, holds Shift, and then clicks another checkbox a few rows down, all the checkboxes inbetween those two checkboxes should be checked.
65+
66+
-->
67+
<div class="inbox">
68+
<div class="item">
69+
<input type="checkbox">
70+
<p>This is an inbox layout.</p>
71+
</div>
72+
<div class="item">
73+
<input type="checkbox">
74+
<p>Check one item</p>
75+
</div>
76+
<div class="item">
77+
<input type="checkbox">
78+
<p>Hold down your Shift key</p>
79+
</div>
80+
<div class="item">
81+
<input type="checkbox">
82+
<p>Check a lower item</p>
83+
</div>
84+
<div class="item">
85+
<input type="checkbox">
86+
<p>Everything inbetween should also be set to checked</p>
87+
</div>
88+
<div class="item">
89+
<input type="checkbox">
90+
<p>Try do it with out any libraries</p>
91+
</div>
92+
<div class="item">
93+
<input type="checkbox">
94+
<p>Just regular JavaScript</p>
95+
</div>
96+
<div class="item">
97+
<input type="checkbox">
98+
<p>Good Luck!</p>
99+
</div>
100+
<div class="item">
101+
<input type="checkbox">
102+
<p>Don't forget to tweet your result!</p>
103+
</div>
104+
</div>
105+
106+
<script>
107+
const checkboxes = document.querySelectorAll('.inbox input[type="checkbox"]');
108+
109+
let lastChecked;
110+
111+
function handleCheck(e) {
112+
// Check if they had the shift key down
113+
// AND check that they are checking it
114+
let inBetween = false;
115+
if (e.shiftKey && this.checked) {
116+
// go ahead and do what we please
117+
// loop over every single checkbox
118+
checkboxes.forEach(checkbox => {
119+
console.log(checkbox);
120+
if (checkbox === this || checkbox === lastChecked) {
121+
inBetween = !inBetween;
122+
console.log('STarting to check them inbetween!');
123+
}
124+
125+
if (inBetween) {
126+
checkbox.checked = true;
127+
}
128+
});
129+
}
130+
131+
lastChecked = this;
132+
}
133+
134+
checkboxes.forEach(checkbox => checkbox.addEventListener('click', handleCheck));
135+
console.log("ss1");
136+
</script>
137+
</body>
138+
</html>

0 commit comments

Comments
 (0)