Web APIs

思考:

1
2
3
4
5
6
7
8
9
10
11
变量声明有三个 var   let  和 const 
我们应该用那个呢?
首先var 先排除,老派写法,问题很多,可以淘汰掉…
let or const ?
建议: const 优先,尽量使用const,原因是:
const 语义化更好
很多变量我们声明的时候就知道他不会被更改了,那为什么不用 const呢?
实际开发中也是,比如react框架,基本const
如果你还在纠结,那么我建议:
有了变量先给const,如果发现它后面是要被修改的,再改为let

1、作用与分类

作用: 就是使用 JS 去操作 html 和浏览器

分类:DOM (文档对象模型)、BOM(浏览器对象模型)

2、什么是DOM

1
2
3
4
5
DOM(Document Object Model——文档对象模型)是用来呈现以及与任意 HTML 或 XML文档交互的API
白话文:DOM是浏览器提供的一套专门用来 操作网页内容 的功能
DOM作用
开发网页内容特效和实现用户交互

3、DOM树

1
2
3
4
5
6
DOM树是什么
将 HTML 文档以树状结构直观的表现出来,我们称之为文档树或 DOM 树
描述网页内容关系的名词
作用:文档树直观的体现了标签与标签之间的关系


4、DOM对象(重要)

1
2
3
4
5
6
7
8
9
10
11
DOM对象:浏览器根据html标签生成的 JS对象
所有的标签属性都可以在这个对象上面找到
修改这个对象的属性会自动映射到标签身上
DOM的核心思想
把网页内容当做对象来处理
document 对象
是 DOM 里提供的一个对象
所以它提供的属性和方法都是用来访问和操作网页内容的
例:document.write()
网页所有内容都在document里面

1
2
3
4
5
6
7
8
<body>
<div>123</div>
<script>
const div = document.querySelector('div')
// 打印对象
console.dir(div) // dom 对象
</script>
</body>

5、获取DOM对象

•根据CSS选择器来获取DOM元素

•其他获取DOM元素方法

5.1 根据CSS选择器来获取DOM元素

选择匹配的第一个元素

语法:

1
document.querySelector('css 选择器');

参数:

包含一个或多个有效的CSS选择器 字符串

返回值:

CSS选择器匹配的第一个元素,一个 HTMLElement对象。

如果没有匹配到,则返回null。

选择匹配的多个元素

这里使用querySelectorAll方法

1
document.querySelectorAll('css 选择器');

参数:

包含一个或多个有效的CSS选择器 字符串

返回值:

CSS选择器匹配的NodeList 对象集合

例如:

1
document.querySelectorAll('ul li');
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
44
45
46
47
48
49
50
51
52
53
54
<!DOCTYPE html>
<html lang="en">

<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
.box {
width: 200px;
height: 200px;
}
</style>
</head>

<body>
<div class="box">123</div>
<div class="box">abc</div>
<p id="nav">导航栏</p>
<ul class="nav">
<li>测试1</li>
<li>测试2</li>
<li>测试3</li>
</ul>
<script>
// 1. 获取匹配的第一个元素
// const box = document.querySelector('div')
// const box = document.querySelector('.box')
// console.log(box)
// const nav = document.querySelector('#nav')
// console.log(nav)
// nav.style.color = 'red'
// 1. 我要获取第一个小 ulli
// const li = document.querySelector('ul li:first-child')
// console.log(li)
// 2. 选择所有的小li
// const lis = document.querySelectorAll('ul li')
// console.log(lis)

// 1.获取元素
const lis = document.querySelectorAll('.nav li')
// console.log(lis)
for (let i = 0; i < lis.length; i++) {
console.log(lis[i]) // 每一个小li对象
}

const p = document.querySelectorAll('#nav')
// console.log(p)
// p[0].style.color = 'red'
</script>
</body>

</html>

querySelectorAll方法得到的是一个伪数组

伪数组:有长度有索引号,但是没有pop(),push()等数组方法。

5.2 其他获取DOM元素的方法

1
2
3
4
5
6
// 根据id获取一个元素
doucument.getElementById('nav');
// 根据标签获取一类元素,例如获取页面所有div元素
document.getElementsByTagName('div');
// 根据 类名获取元素,例如:获取页面中所有类名为w的元素
document.getElementsByClassName('w');

6、操作元素内容

1
2
3
对象.innerText 属性
对象.innerHTML 属性

1
2
3
4
元素innerText 属性
将文本内容添加/更新到任意标签位置
显示纯文本,不解析标签

1
2
3
4
元素.innerHTML 属性
将文本内容添加/更新到任意标签位置
会解析标签,多标签建议使用模板字符

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
<body>
<div class="box">我是文字的内容</div>
<script>
// const obj = {
// name: '张三'
// }
// console.log(obj.name)
// obj.name = '李四'
// 1. 获取元素
const box = document.querySelector('.box')
// 2. 修改文字内容 对象.innerText 属性
// console.log(box.innerText) // 获取文字内容
// // box.innerText = '我是一个盒子' // 修改文字内容
// box.innerText = '<strong>我是一个盒子</strong>' // 不解析标签

// 3. innerHTML 解析标签
console.log(box.innerHTML)
// box.innerHTML = '我要更换'
box.innerHTML = '<strong>我要更换</strong>'
</script>
</body>

如果还在纠结到底用谁,你可以选择innerHTML

7、操作元素属性

7.1 操作元素常用属性

还可以通过 JS 设置/修改标签元素属性,比如通过 src更换 图片

最常见的属性比如: href、title、src 等

1
2
3
4
5
6
7
8
9
10
11
<body>
<img src="./images/1.webp" alt="">
<script>
// 1. 获取图片元素
const img = document.querySelector('img')
// 2. 修改图片对象的属性 对象.属性 = 值
img.src = './images/2.webp'
img.title = 'hello'
</script>
</body>

页面刷新,图片随机变换

7.2 操作元素样式属性

1.通过 style 属性操作CSS

语法:对象.style.样式属性 = 值

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
<!DOCTYPE html>
<html lang="en">

<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
.box {
width: 200px;
height: 200px;
background-color: blue;
}
</style>
</head>

<body>
<div class="box"></div>
<script>
// 1. 获取元素
const box = document.querySelector('.box')
//2. 修改样式属性 对象.style.样式属性 = '值' 别忘了跟单位
box.style.width = '300px'
// 多组单词的采取 小驼峰命名法
box.style.backgroundColor = 'red'
box.style.border = '2px solid blue'
box.style.borderTop = '2px solid red'
</script>
</body>

</html>

7.3 操作类名className操作CSS

如果修改的样式比较多,直接通过style属性修改比较繁琐,我们可以通过借助于css类名的形式。

语法:

1
2
// active 是一个css类名
元素.className = 'active'

注意:

1
2
3
由于class是关键字, 所以使用className去代替
className是使用新值换旧值, 如果需要添加一个类,需要保留之前的类名

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
<!DOCTYPE html>
<html lang="en">

<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
div {
width: 200px;
height: 200px;
background-color: yellow;
}

.nav {
color: red;
}

.box {
width: 300px;
height: 300px;
background-color: skyblue;
margin: 100px auto;
padding: 10px;
border: 1px solid #000;
}
</style>
</head>

<body>
<div class="nav">123</div>
<script>
// 1. 获取元素
const div = document.querySelector('div')
// 2.添加类名 class 是个关键字 我们用 className
div.className = 'nav box'
</script>
</body>

</html>

注意:直接使用 className 赋值会覆盖以前的类名

7.4 通过classList操作类控制css

为了解决className 容易覆盖以前的类名,我们可以通过classList方式追加和删除类名

语法:

1
2
3
4
5
6
// 追加一个类
元素.classList.add('类名');
// 删除一个类
元素.classList.remove('类名')
// 切换一个类
元素.classList.toggle('类名')
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
<!DOCTYPE html>
<html lang="en">

<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
.box {
width: 200px;
height: 200px;
color: #333;
}

.active {
color: red;
background-color: blue;
}
</style>
</head>

<body>
<div class="box ">文字</div>
<script>
// 通过classList添加
// 1. 获取元素
const box = document.querySelector('.box')
// 2. 修改样式
// 2.1 追加类 add() 类名不加点,并且是字符串
// box.classList.add('active')
// 2.2 删除类 remove() 类名不加点,并且是字符串
// box.classList.remove('box')
// 2.3 切换类 toggle() 有还是没有啊, 有就删掉,没有就加上
box.classList.toggle('active')
</script>
</body>

</html>

随机轮播图案例:

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
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
<!DOCTYPE html>
<html lang="en">

<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>轮播图点击切换</title>
<style>
* {
box-sizing: border-box;
}

.slider {
width: 560px;
height: 400px;
overflow: hidden;
}

.slider-wrapper {
width: 100%;
height: 320px;
}

.slider-wrapper img {
width: 100%;
height: 100%;
display: block;
}

.slider-footer {
height: 80px;
background-color: rgb(100, 67, 68);
padding: 12px 12px 0 12px;
position: relative;
}

.slider-footer .toggle {
position: absolute;
right: 0;
top: 12px;
display: flex;
}

.slider-footer .toggle button {
margin-right: 12px;
width: 28px;
height: 28px;
appearance: none;
border: none;
background: rgba(255, 255, 255, 0.1);
color: #fff;
border-radius: 4px;
cursor: pointer;
}

.slider-footer .toggle button:hover {
background: rgba(255, 255, 255, 0.2);
}

.slider-footer p {
margin: 0;
color: #fff;
font-size: 18px;
margin-bottom: 10px;
}

.slider-indicator {
margin: 0;
padding: 0;
list-style: none;
display: flex;
align-items: center;
}

.slider-indicator li {
width: 8px;
height: 8px;
margin: 4px;
border-radius: 50%;
background: #fff;
opacity: 0.4;
cursor: pointer;
}

.slider-indicator li.active {
width: 12px;
height: 12px;
opacity: 1;
}
</style>
</head>

<body>
<div class="slider">
<div class="slider-wrapper">
<img src="./images/slider01.jpg" alt="" />
</div>
<div class="slider-footer">
<p>对人类来说会不会太超前了?</p>
<ul class="slider-indicator">
<li></li>
<li></li>
<li></li>
<li></li>
<li></li>
<li></li>
<li></li>
<li></li>
</ul>
<div class="toggle">
<button class="prev">&lt;</button>
<button class="next">&gt;</button>
</div>
</div>
</div>
<script>
// const arr = [1, 3]
// arr[0]
// 1. 初始数据
const sliderData = [
{ url: './images/slider01.jpg', title: '对人类来说会不会太超前了?', color: 'rgb(100, 67, 68)' },
{ url: './images/slider02.jpg', title: '开启剑与雪的黑暗传说!', color: 'rgb(43, 35, 26)' },
{ url: './images/slider03.jpg', title: '真正的jo厨出现了!', color: 'rgb(36, 31, 33)' },
{ url: './images/slider04.jpg', title: '李玉刚:让世界通过B站看到东方大国文化', color: 'rgb(139, 98, 66)' },
{ url: './images/slider05.jpg', title: '快来分享你的寒假日常吧~', color: 'rgb(67, 90, 92)' },
{ url: './images/slider06.jpg', title: '哔哩哔哩小年YEAH', color: 'rgb(166, 131, 143)' },
{ url: './images/slider07.jpg', title: '一站式解决你的电脑配置问题!!!', color: 'rgb(53, 29, 25)' },
{ url: './images/slider08.jpg', title: '谁不想和小猫咪贴贴呢!', color: 'rgb(99, 72, 114)' },
]




// 1. 需要一个随机数
const random = parseInt(Math.random() * sliderData.length)
// console.log(sliderData[random])
// 2. 把对应的数据渲染到标签里面
// 2.1 获取图片
const img = document.querySelector('.slider-wrapper img')
// 2.2. 修改图片路径 = 对象.url
img.src = sliderData[random].url
// 3. 把p里面的文字内容更换
// 3.1 获取p
const p = document.querySelector('.slider-footer p')
// 3.2修改p
p.innerHTML = sliderData[random].title
// 4. 修改背景颜色
const footer = document.querySelector('.slider-footer')
footer.style.backgroundColor = sliderData[random].color
// 5. 小圆点
const li = document.querySelector(`.slider-indicator li:nth-child(${random + 1})`)
// 让当前这个小li 添加 active这个类
li.classList.add('active')
</script>
</body>

</html>

7.5 操作表单元素 属性

表单很多情况,也需要修改属性

正常的有属性有取值的 跟其他的标签属性没有任何区别

1
2
3
获取: DOM对象.属性名
设置: DOM对象.属性名 = 新值

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
<!DOCTYPE html>
<html lang="en">

<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>

<body>
<!-- <input type="text" value="电脑"> -->
<input type="checkbox" name="" id="">
<button>点击</button>
<script>
// 1 获取元素
// const uname = document.querySelector('input')
// 2. 获取值 获取表单里面的值 用的 表单.value
// console.log(uname.value) // 电脑
// console.log(uname.innerHTML) innertHTML 得不到表单的内容
// 3. 设置表单的值
// uname.value = '我要买电脑'
// console.log(uname.type)
// uname.type = 'password'

// 1. 获取
const ipt = document.querySelector('input')
// console.log(ipt.checked) // false 只接受布尔值
ipt.checked = true
// ipt.checked = 'true' // 会选中,不提倡 有隐式转换

// 1.获取
const button = document.querySelector('button')
// console.log(button.disabled) // 默认false 不禁用
button.disabled = true // 禁用按钮

</script>
</body>

</html>

7.6 自定义属性

1
2
3
4
5
6
标准属性: 标签天生自带的属性 比如class id title等, 可以直接使用点语法操作比如: disabled、checked、selected
自定义属性:
在html5中推出来了专门的data-自定义属性
在标签上一律以data-开头
在DOM对象上一律以dataset对象方式获取

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
<!DOCTYPE html>
<html lang="en">

<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>

<body>
<div data-id="1" data-spm="不知道">1</div>
<div data-id="2">2</div>
<div data-id="3">3</div>
<div data-id="4">4</div>
<div data-id="5">5</div>
<script>
const one = document.querySelector('div')
console.log(one.dataset.id) // 1
console.log(one.dataset.spm) // 不知道
</script>
</body>

</html>

8、定时器-间歇函数

1
2
3
4
5
网页中经常会需要一种功能:每隔一段时间需要自动执行一段代码,不需要我们手动去触发
例如:网页中的倒计时
要实现这种需求,需要定时器函数
定时器函数有两种,今天我先讲间歇函数

开启定时器

1
setInterval(函数,间隔时间) 

作用:每隔一段时间调用这个函数

间隔时间单位是毫秒

注意:

1.函数名字不需要加括号

2.定时器返回的是一个id数字

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
<script>
// setInterval(函数, 间隔时间)
// setInterval(function () {
// console.log('一秒执行一次')
// }, 1000)

function fn() {
console.log('一秒执行一次')
}
// setInterval(函数名, 间隔时间) 函数名不要加小括号
let n = setInterval(fn, 1000)
// setInterval('fn()', 1000)
console.log(n)
// 关闭定时器
clearInterval(n)

// let m = setInterval(function () {
// console.log(11)
// }, 2000)
// console.log(m)

// const num = 10
// num = 10
// console.log(num)
let i = 1
setInterval(function () {
i++
document.write(`${i} `)
}, 200)
</script>

关闭定时器

1
2
let 变量名 = setInterval(函数,间隔时间)
clearInterval(变量名);

一般都是满足了一定条件以后再停止

以下是:用户注册倒计时案例

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
<!DOCTYPE html>
<html lang="en">

<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>

<body>
<textarea name="" id="" cols="30" rows="10">
用户注册协议
欢迎注册成为京东用户!在您注册过程中,您需要完成我们的注册流程并通过点击同意的形式在线签署以下协议,请您务必仔细阅读、充分理解协议中的条款内容后再点击同意(尤其是以粗体或下划线标识的条款,因为这些条款可能会明确您应履行的义务或对您的权利有所限制)。
【请您注意】如果您不同意以下协议全部或任何条款约定,请您停止注册。您停止注册后将仅可以浏览我们的商品信息但无法享受我们的产品或服务。如您按照注册流程提示填写信息,阅读并点击同意上述协议且完成全部注册流程后,即表示您已充分阅读、理解并接受协议的全部内容,并表明您同意我们可以依据协议内容来处理您的个人信息,并同意我们将您的订单信息共享给为完成此订单所必须的第三方合作方(详情查看
</textarea>
<br>
<button class="btn" disabled>我已经阅读用户协议(5)</button>
<script>
// 1. 获取元素
const btn = document.querySelector('.btn')
// console.log(btn.innerHTML) butto按钮特殊用innerHTML
// 2. 倒计时
let i = 5
// 2.1 开启定时器
let n = setInterval(function () {
i--
btn.innerHTML = `我已经阅读用户协议(${i})`
if (i === 0) {
clearInterval(n) // 关闭定时器
// 定时器停了,我就可以开按钮
btn.disabled = false
btn.innerHTML = '同意'
}
}, 1000)

</script>
</body>

</html>

轮播图定时器案例

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
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
<!DOCTYPE html>
<html lang="en">

<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>轮播图点击切换</title>
<style>
* {
box-sizing: border-box;
}

.slider {
width: 560px;
height: 400px;
overflow: hidden;
}

.slider-wrapper {
width: 100%;
height: 320px;
}

.slider-wrapper img {
width: 100%;
height: 100%;
display: block;
}

.slider-footer {
height: 80px;
background-color: rgb(100, 67, 68);
padding: 12px 12px 0 12px;
position: relative;
}

.slider-footer .toggle {
position: absolute;
right: 0;
top: 12px;
display: flex;
}

.slider-footer .toggle button {
margin-right: 12px;
width: 28px;
height: 28px;
appearance: none;
border: none;
background: rgba(255, 255, 255, 0.1);
color: #fff;
border-radius: 4px;
cursor: pointer;
}

.slider-footer .toggle button:hover {
background: rgba(255, 255, 255, 0.2);
}

.slider-footer p {
margin: 0;
color: #fff;
font-size: 18px;
margin-bottom: 10px;
}

.slider-indicator {
margin: 0;
padding: 0;
list-style: none;
display: flex;
align-items: center;
}

.slider-indicator li {
width: 8px;
height: 8px;
margin: 4px;
border-radius: 50%;
background: #fff;
opacity: 0.4;
cursor: pointer;
}

.slider-indicator li.active {
width: 12px;
height: 12px;
opacity: 1;
}
</style>
</head>

<body>
<div class="slider">
<div class="slider-wrapper">
<img src="./images/slider01.jpg" alt="" />
</div>
<div class="slider-footer">
<p>对人类来说会不会太超前了?</p>
<ul class="slider-indicator">
<li class="active"></li>
<li></li>
<li></li>
<li></li>
<li></li>
<li></li>
<li></li>
<li></li>
</ul>
<div class="toggle">
<button class="prev">&lt;</button>
<button class="next">&gt;</button>
</div>
</div>
</div>
<script>
// 1. 初始数据
const sliderData = [
{ url: './images/slider01.jpg', title: '对人类来说会不会太超前了?', color: 'rgb(100, 67, 68)' },
{ url: './images/slider02.jpg', title: '开启剑与雪的黑暗传说!', color: 'rgb(43, 35, 26)' },
{ url: './images/slider03.jpg', title: '真正的jo厨出现了!', color: 'rgb(36, 31, 33)' },
{ url: './images/slider04.jpg', title: '李玉刚:让世界通过B站看到东方大国文化', color: 'rgb(139, 98, 66)' },
{ url: './images/slider05.jpg', title: '快来分享你的寒假日常吧~', color: 'rgb(67, 90, 92)' },
{ url: './images/slider06.jpg', title: '哔哩哔哩小年YEAH', color: 'rgb(166, 131, 143)' },
{ url: './images/slider07.jpg', title: '一站式解决你的电脑配置问题!!!', color: 'rgb(53, 29, 25)' },
{ url: './images/slider08.jpg', title: '谁不想和小猫咪贴贴呢!', color: 'rgb(99, 72, 114)' },
]
// 1. 获取元素
const img = document.querySelector('.slider-wrapper img')
const p = document.querySelector('.slider-footer p')
let i = 0 // 信号量 控制图片的张数
// 2. 开启定时器
// console.log(sliderData[i]) 拿到对应的对象啦
setInterval(function () {
i++
// 无缝衔接位置 一共八张图片,到了最后一张就是 8, 数组的长度就是 8
if (i >= sliderData.length) {
i = 0
}
// console.log(i)
// console.log(sliderData[i])
// 更换图片路径
img.src = sliderData[i].url
// 把字写到 p里面
p.innerHTML = sliderData[i].title
// 小圆点
// 先删除以前的active
document.querySelector('.slider-indicator .active').classList.remove('active')
// 只让当前li添加active
document.querySelector(`.slider-indicator li:nth-child(${i + 1})`).classList.add('active')
}, 1000)

</script>
</body>

</html>

9、事件监听

什么是事件?

事件是在编程时系统内发生的动作或者发生的事情

比如用户在网页上单击一个按钮

什么是事件监听?

​ 就是让程序检测是否有事件产生,一旦有事件触发,就立即调用一个函数做出响应,也称为 绑定事件或者注册事件

​ 比如鼠标经过显示下拉菜单,比如点击可以播放轮播图等等

语法:

1
元素对象.addEventListener('事件类型',要执行的函数)
1
2
3
4
5
事件监听三要素:
事件源: 那个dom元素被事件触发了,要获取dom元素
事件类型: 用什么方式触发,比如鼠标单击 click、鼠标经过 mouseover 等
事件调用的函数: 要做什么事

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<body>
<button>点击</button>
<script>
// 需求: 点击了按钮,弹出一个对话框
// 1. 事件源 按钮
// 2.事件类型 点击鼠标 click 字符串
// 3. 事件处理程序 弹出对话框
const btn = document.querySelector('button')
btn.addEventListener('click', function () {
alert('你早呀~')
})

</script>
</body>

案例:

需求:点击关闭之后,顶部关闭

分析:

①:点击的是关闭按钮

②:关闭的是父盒子

核心:利用样式的显示和隐藏完成, display:none 隐藏元素 display:block 显示元素

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
44
45
46
47
48
49
50
51
52
53
54
55
李伟兴 09:31:13
<!DOCTYPE html>
<html lang="en">

<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
.box {
position: relative;
width: 1000px;
height: 200px;
background-color: blue;
margin: 100px auto;
text-align: center;
font-size: 50px;
line-height: 200px;
font-weight: 700;
}

.box1 {
position: absolute;
right: 20px;
top: 10px;
width: 20px;
height: 20px;
background-color: skyblue;
text-align: center;
line-height: 20px;
font-size: 16px;
cursor: pointer;
}
</style>
</head>

<body>
<div class="box">
我是广告
<div class="box1">X</div>
</div>
<script>
// 1. 获取事件源
const box1 = document.querySelector('.box1')
// 关闭的是大盒子
const box = document.querySelector('.box')
// 2. 事件侦听
box1.addEventListener('click', function () {
box.style.display = 'none'
})
</script>
</body>

</html>

10、事件监听版本

1
2
3
4
5
6
7
DOM L0 
事件源.on事件 = function() { }
DOM L2
事件源.addEventListener(事件, 事件处理函数)
区别:
on方式会被覆盖,addEventListener方式可绑定多次,拥有事件更多特性,推荐使用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<body>
<button>点击</button>
<script>
const btn = document.querySelector('button')
// btn.onclick = function () {
// alert(11)
// }
// btn.onclick = function () {
// alert(22)
// }

// let num = 10
// num = 20
btn.addEventListener('click', function () {
alert(11)
})
btn.addEventListener('click', function () {
alert(22)
})
</script>
</body>

11、事件类型

鼠标事件

1
2
3
4
鼠标触发
click 鼠标点击
mouseenter 鼠标经过mouseleave 鼠标离开

焦点事件

1
2
3
4
表单获得光标
focus 获得焦点
blur 失去焦点

键盘事件

1
2
3
4
键盘触发
Keydown 键盘按下触发
Keyup 键盘抬起触发

文本事件

1
input    用户输入事件

轮播图完整案例:

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
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
<!DOCTYPE html>
<html lang="en">

<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>轮播图点击切换</title>
<style>
* {
box-sizing: border-box;
}

.slider {
width: 560px;
height: 400px;
overflow: hidden;
}

.slider-wrapper {
width: 100%;
height: 320px;
}

.slider-wrapper img {
width: 100%;
height: 100%;
display: block;
}

.slider-footer {
height: 80px;
background-color: rgb(100, 67, 68);
padding: 12px 12px 0 12px;
position: relative;
}

.slider-footer .toggle {
position: absolute;
right: 0;
top: 12px;
display: flex;
}

.slider-footer .toggle button {
margin-right: 12px;
width: 28px;
height: 28px;
appearance: none;
border: none;
background: rgba(255, 255, 255, 0.1);
color: #fff;
border-radius: 4px;
cursor: pointer;
}

.slider-footer .toggle button:hover {
background: rgba(255, 255, 255, 0.2);
}

.slider-footer p {
margin: 0;
color: #fff;
font-size: 18px;
margin-bottom: 10px;
}

.slider-indicator {
margin: 0;
padding: 0;
list-style: none;
display: flex;
align-items: center;
}

.slider-indicator li {
width: 8px;
height: 8px;
margin: 4px;
border-radius: 50%;
background: #fff;
opacity: 0.4;
cursor: pointer;
}

.slider-indicator li.active {
width: 12px;
height: 12px;
opacity: 1;
}
</style>
</head>

<body>
<div class="slider">
<div class="slider-wrapper">
<img src="./images/slider01.jpg" alt="" />
</div>
<div class="slider-footer">
<p>对人类来说会不会太超前了?</p>
<ul class="slider-indicator">
<li class="active"></li>
<li></li>
<li></li>
<li></li>
<li></li>
<li></li>
<li></li>
<li></li>
</ul>
<div class="toggle">
<button class="prev">&lt;</button>
<button class="next">&gt;</button>
</div>
</div>
</div>
<script>
// 1. 初始数据
const data = [
{ url: './images/slider01.jpg', title: '对人类来说会不会太超前了?', color: 'rgb(100, 67, 68)' },
{ url: './images/slider02.jpg', title: '开启剑与雪的黑暗传说!', color: 'rgb(43, 35, 26)' },
{ url: './images/slider03.jpg', title: '真正的jo厨出现了!', color: 'rgb(36, 31, 33)' },
{ url: './images/slider04.jpg', title: '李玉刚:让世界通过B站看到东方大国文化', color: 'rgb(139, 98, 66)' },
{ url: './images/slider05.jpg', title: '快来分享你的寒假日常吧~', color: 'rgb(67, 90, 92)' },
{ url: './images/slider06.jpg', title: '哔哩哔哩小年YEAH', color: 'rgb(166, 131, 143)' },
{ url: './images/slider07.jpg', title: '一站式解决你的电脑配置问题!!!', color: 'rgb(53, 29, 25)' },
{ url: './images/slider08.jpg', title: '谁不想和小猫咪贴贴呢!', color: 'rgb(99, 72, 114)' },
]
// 获取元素
const img = document.querySelector('.slider-wrapper img')
const p = document.querySelector('.slider-footer p')
const footer = document.querySelector('.slider-footer')
// 1. 右按钮业务
// 1.1 获取右侧按钮
const next = document.querySelector('.next')
let i = 0 // 信号量 控制播放图片张数
// 1.2 注册点击事件

next.addEventListener('click', function () {
// console.log(11)
i++
// 1.6判断条件 如果大于8 就复原为 0
// if (i >= 8) {
// i = 0
// }
i = i >= data.length ? 0 : i
// 1.3 得到对应的对象
// console.log(data[i])
// 调用函数
toggle()
})

// 2. 左侧按钮业务
// 2.1 获取左侧按钮
const prev = document.querySelector('.prev')
// 1.2 注册点击事件
prev.addEventListener('click', function () {
i--
// 判断条件 如果小于0 则爬到最后一张图片索引号是 7
// if (i < 0) {
// i = 7
// }
i = i < 0 ? data.length - 1 : i
// 1.3 得到对应的对象
// console.log(data[i])
// 调用函数
toggle()
})

// 声明一个渲染的函数作为复用
function toggle() {
// 1.4 渲染对应的数据
img.src = data[i].url
p.innerHTML = data[i].title
footer.style.backgroundColor = data[i].color
// 1.5 更换小圆点 先移除原来的类名, 当前li再添加 这个 类名
document.querySelector('.slider-indicator .active').classList.remove('active')
document.querySelector(`.slider-indicator li:nth-child(${i + 1})`).classList.add('active')
}



// 3. 自动播放模块
let timerId = setInterval(function () {
// 利用js自动调用点击事件 click() 一定加小括号调用函数
next.click()
}, 1000)


// 4. 鼠标经过大盒子,停止定时器
const slider = document.querySelector('.slider')
// 注册事件
slider.addEventListener('mouseenter', function () {
// 停止定时器
clearInterval(timerId)
})

// 5. 鼠标离开大盒子,开启定时器
// 注册事件
slider.addEventListener('mouseleave', function () {
// 停止定时器
if (timerId) clearInterval(timerId)
// 开启定时器
timerId = setInterval(function () {
// 利用js自动调用点击事件 click() 一定加小括号调用函数
next.click()
}, 1000)
})
</script>
</body>

</html>

焦点事件演示:

1
2
3
4
5
6
7
8
9
10
11
12
<body>
<input type="text">
<script>
const input = document.querySelector('input')
input.addEventListener('focus', function () {
console.log('有焦点触发')
})
input.addEventListener('blur', function () {
console.log('失去焦点触发')
})
</script>
</body>

小米搜索框案例

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
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
<!DOCTYPE html>
<html lang="en">

<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}

ul {

list-style: none;
}

.mi {
position: relative;
width: 223px;
margin: 100px auto;
}

.mi input {
width: 223px;
height: 48px;
padding: 0 10px;
font-size: 14px;
line-height: 48px;
border: 1px solid #e0e0e0;
outline: none;
}

.mi .search {
border: 1px solid #ff6700;
}

.result-list {
display: none;
position: absolute;
left: 0;
top: 48px;
width: 223px;
border: 1px solid #ff6700;
border-top: 0;
background: #fff;
}

.result-list a {
display: block;
padding: 6px 15px;
font-size: 12px;
color: #424242;
text-decoration: none;
}

.result-list a:hover {
background-color: #eee;
}
</style>

</head>

<body>
<div class="mi">
<input type="search" placeholder="小米笔记本">
<ul class="result-list">
<li><a href="#">全部商品</a></li>
<li><a href="#">小米11</a></li>
<li><a href="#">小米10S</a></li>
<li><a href="#">小米笔记本</a></li>
<li><a href="#">小米手机</a></li>
<li><a href="#">黑鲨4</a></li>
<li><a href="#">空调</a></li>
</ul>
</div>
<script>
// 1. 获取元素
const input = document.querySelector('[type=search]')
const ul = document.querySelector('.result-list')
// console.log(input)
// 2. 监听事件 获得焦点
input.addEventListener('focus', function () {
// ul显示
ul.style.display = 'block'
// 添加一个带有颜色边框的类名
input.classList.add('search')
})
// 3. 监听事件 失去焦点
input.addEventListener('blur', function () {
ul.style.display = 'none'
input.classList.remove('search')
})
</script>
</body>

</html>

键盘事件演示

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<body>
<input type="text">
<script>
const input = document.querySelector('input')
// 1. 键盘事件
// input.addEventListener('keydown', function () {
// console.log('键盘按下了')
// })
// input.addEventListener('keyup', function () {
// console.log('键盘谈起了')
// })
// 2. 用户输入文本事件 input
input.addEventListener('input', function () {
console.log(input.value)
})
</script>
</body>

评论字数统计案例

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
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
<!DOCTYPE html>
<html lang="en">

<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>评论回车发布</title>
<style>
.wrapper {
min-width: 400px;
max-width: 800px;
display: flex;
justify-content: flex-end;
}

.avatar {
width: 48px;
height: 48px;
border-radius: 50%;
overflow: hidden;
background: url(./images/avatar.jpg) no-repeat center / cover;
margin-right: 20px;
}

.wrapper textarea {
outline: none;
border-color: transparent;
resize: none;
background: #f5f5f5;
border-radius: 4px;
flex: 1;
padding: 10px;
transition: all 0.5s;
height: 30px;
}

.wrapper textarea:focus {
border-color: #e4e4e4;
background: #fff;
height: 50px;
}

.wrapper button {
background: #00aeec;
color: #fff;
border: none;
border-radius: 4px;
margin-left: 10px;
width: 70px;
cursor: pointer;
}

.wrapper .total {
margin-right: 80px;
color: #999;
margin-top: 5px;
opacity: 0;
transition: all 0.5s;
}

.list {
min-width: 400px;
max-width: 800px;
display: flex;
}

.list .item {
width: 100%;
display: flex;
}

.list .item .info {
flex: 1;
border-bottom: 1px dashed #e4e4e4;
padding-bottom: 10px;
}

.list .item p {
margin: 0;
}

.list .item .name {
color: #FB7299;
font-size: 14px;
font-weight: bold;
}

.list .item .text {
color: #333;
padding: 10px 0;
}

.list .item .time {
color: #999;
font-size: 12px;
}
</style>
</head>

<body>
<div class="wrapper">
<i class="avatar"></i>
<textarea id="tx" placeholder="发一条友善的评论" rows="2" maxlength="200"></textarea>
<button>发布</button>
</div>
<div class="wrapper">
<span class="total">0/200字</span>
</div>
<div class="list">
<div class="item" style="display: none;">
<i class="avatar"></i>
<div class="info">
<p class="name">清风徐来</p>
<p class="text">大家都辛苦啦,感谢各位大大的努力,能圆满完成真是太好了[笑哭][支持]</p>
<p class="time">2022-10-10 20:29:21</p>
</div>
</div>
</div>
<script>
const tx = document.querySelector('#tx')
const total = document.querySelector('.total')
// 1. 当我们文本域获得了焦点,就让 total 显示出来
tx.addEventListener('focus', function () {
total.style.opacity = 1
})
// 2. 当我们文本域失去了焦点,就让 total 隐藏出来
tx.addEventListener('blur', function () {
total.style.opacity = 0
})
// 3. 检测用户输入
tx.addEventListener('input', function () {
// console.log(tx.value.length) 得到输入的长度
total.innerHTML = `${tx.value.length}/200字`
})

// const str = 'andy'
// console.log(str.length)
</script>
</body>

</html>

12、事件对象

12.1 获取事件对象

1
2
3
4
5
6
7
事件对象是什么
也是个对象,这个对象里有事件触发时的相关信息
例如:鼠标点击事件中,事件对象就存了鼠标点在哪个位置等信息
使用场景
可以判断用户按下哪个键,比如按下回车键可以发布新闻
可以判断鼠标点击了哪个元素,从而做相应的操作

语法:

在事件绑定的回调函数的第一个参数就是事件对象

一般命名为event、ev、e

1
2
3
元素.addEventListener('click',function(e){

})

12.2 事件对象常用属性

1
2
3
4
5
6
7
8
9
10
11
部分常用属性
type
获取当前的事件类型
clientX/clientY
获取光标相对于浏览器可见窗口左上角的位置
offsetX/offsetY
获取光标相对于当前DOM元素左上角的位置
key
用户按下的键盘键的值
现在不提倡使用keyCode

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
<!DOCTYPE html>
<html lang="en">

<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>

<body>
<!-- <button>点击</button> -->
<input type="text">
<script>
// const btn = document.querySelector('button')
// btn.addEventListener('click', function (e) {
// console.log(e)
// })

const input = document.querySelector('input')
input.addEventListener('keyup', function (e) {
// console.log(11)
// console.log(e.key)
if (e.key === 'Enter') {
console.log('我按下了回车键')
}
})
</script>
</body>

</html>

评论回车发布案例

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
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
<!DOCTYPE html>
<html lang="en">

<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>评论回车发布</title>
<style>
.wrapper {
min-width: 400px;
max-width: 800px;
display: flex;
justify-content: flex-end;
}

.avatar {
width: 48px;
height: 48px;
border-radius: 50%;
overflow: hidden;
background: url(./images/avatar.jpg) no-repeat center / cover;
margin-right: 20px;
}

.wrapper textarea {
outline: none;
border-color: transparent;
resize: none;
background: #f5f5f5;
border-radius: 4px;
flex: 1;
padding: 10px;
transition: all 0.5s;
height: 30px;
}

.wrapper textarea:focus {
border-color: #e4e4e4;
background: #fff;
height: 50px;
}

.wrapper button {
background: #00aeec;
color: #fff;
border: none;
border-radius: 4px;
margin-left: 10px;
width: 70px;
cursor: pointer;
}

.wrapper .total {
margin-right: 80px;
color: #999;
margin-top: 5px;
opacity: 0;
transition: all 0.5s;
}

.list {
min-width: 400px;
max-width: 800px;
display: flex;
}

.list .item {
width: 100%;
display: flex;
}

.list .item .info {
flex: 1;
border-bottom: 1px dashed #e4e4e4;
padding-bottom: 10px;
}

.list .item p {
margin: 0;
}

.list .item .name {
color: #FB7299;
font-size: 14px;
font-weight: bold;
}

.list .item .text {
color: #333;
padding: 10px 0;
}

.list .item .time {
color: #999;
font-size: 12px;
}
</style>
</head>

<body>
<div class="wrapper">
<i class="avatar"></i>
<textarea id="tx" placeholder="发一条友善的评论" rows="2" maxlength="200"></textarea>
<button>发布</button>
</div>
<div class="wrapper">
<span class="total">0/200字</span>
</div>
<div class="list">
<div class="item" style="display: none;">
<i class="avatar"></i>
<div class="info">
<p class="name">清风徐来</p>
<p class="text">大家都辛苦啦,感谢各位大大的努力,能圆满完成真是太好了[笑哭][支持]</p>
<p class="time">2022-10-10 20:29:21</p>
</div>
</div>
</div>
<script>
const tx = document.querySelector('#tx')
const total = document.querySelector('.total')
const item = document.querySelector('.item')
const text = document.querySelector('.text')
// 1. 当我们文本域获得了焦点,就让 total 显示出来
tx.addEventListener('focus', function () {
total.style.opacity = 1
})
// 2. 当我们文本域失去了焦点,就让 total 隐藏出来
tx.addEventListener('blur', function () {
total.style.opacity = 0
})
// 3. 检测用户输入
tx.addEventListener('input', function () {
// console.log(tx.value.length) 得到输入的长度
total.innerHTML = `${tx.value.length}/200字`
})

// 4. 按下回车发布评论
tx.addEventListener('keyup', function (e) {
// 只有按下的是回车键,才会触发
// console.log(e.key)
if (e.key === 'Enter') {
// 如果用户输入的不为空就显示和打印
if (tx.value.trim()) {
// console.log(11)
item.style.display = 'block'
// console.log(tx.value) // 用户输入的内容
text.innerHTML = tx.value

}
// 等我们按下回车,结束,清空文本域
tx.value = ''
// 按下回车之后,就要把 字符统计 复原
total.innerHTML = '0/200字'
}

})
</script>
</body>

</html>

13、环境对象

1
2
3
4
5
6
环境对象:指的是函数内部特殊的变量 this ,它代表着当前函数运行时所处的环境
作用:弄清楚this的指向,可以让我们代码更简洁
函数的调用方式不同,this 指代的对象也不同
【谁调用, this 就是谁】 是判断 this 指向的粗略规则
直接调用函数,其实相当于是 window.函数,所以 this 指代 window

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<body>
<button>点击</button>
<script>
// 每个函数里面都有this 环境对象 普通函数里面this指向的是window
// function fn() {
// console.log(this)
// }
// window.fn()
const btn = document.querySelector('button')
btn.addEventListener('click', function () {
// console.log(this) // btn 对象
// btn.style.color = 'red'
this.style.color = 'red'
})
</script>
</body>

</html>

回调函数说明

1
2
3
4
如果将函数 A 做为参数传递给函数 B 时,我们称函数 A 为回调函数
简单理解: 当一个函数当做参数来传递给另外一个函数的时候,这个函数就是回调函数
常见的使用场景:

全选复选框案例

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
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
<!DOCTYPE html>

<html>

<head lang="en">
<meta charset="UTF-8">
<title></title>
<style>
* {
margin: 0;
padding: 0;
}

table {
border-collapse: collapse;
border-spacing: 0;
border: 1px solid #c0c0c0;
width: 500px;
margin: 100px auto;
text-align: center;
}

th {
background-color: #09c;
font: bold 16px "微软雅黑";
color: #fff;
height: 24px;
}

td {
border: 1px solid #d0d0d0;
color: #404060;
padding: 10px;
}

.allCheck {
width: 80px;
}
</style>
</head>

<body>
<table>
<tr>
<th class="allCheck">
<input type="checkbox" name="" id="checkAll"> <span class="all">全选</span>
</th>
<th>商品</th>
<th>商家</th>
<th>价格</th>
</tr>
<tr>
<td>
<input type="checkbox" name="check" class="ck">
</td>
<td>小米手机</td>
<td>小米</td>
<td>¥1999</td>
</tr>
<tr>
<td>
<input type="checkbox" name="check" class="ck">
</td>
<td>小米净水器</td>
<td>小米</td>
<td>¥4999</td>
</tr>
<tr>
<td>
<input type="checkbox" name="check" class="ck">
</td>
<td>小米电视</td>
<td>小米</td>
<td>¥5999</td>
</tr>
</table>
<script>
// 1. 获取大复选框
const checkAll = document.querySelector('#checkAll')
// 2. 获取所有的小复选框
const cks = document.querySelectorAll('.ck')
// 3. 点击大复选框 注册事件
checkAll.addEventListener('click', function () {
// 得到当前大复选框的选中状态
// console.log(checkAll.checked) // 得到 是 true 或者是 false
// 4. 遍历所有的小复选框 让小复选框的checked = 大复选框的 checked
for (let i = 0; i < cks.length; i++) {
cks[i].checked = this.checked
}
})
// 5. 小复选框控制大复选框

for (let i = 0; i < cks.length; i++) {
// 5.1 给所有的小复选框添加点击事件
cks[i].addEventListener('click', function () {
// 判断选中的小复选框个数 是不是等于 总的小复选框个数
// 一定要写到点击里面,因为每次要获得最新的个数
// console.log(document.querySelectorAll('.ck:checked').length)
// console.log(document.querySelectorAll('.ck:checked').length === cks.length)
checkAll.checked = document.querySelectorAll('.ck:checked').length === cks.length
})
}
</script>
</body>

</html>

14、事件流

事件流指的是事件完整执行过程中的流动路径

说明:假设页面里有个div,当触发事件时,会经历两个阶段,分别是捕获阶段、冒泡阶段

简单来说:捕获阶段是 从父到子 冒泡阶段是从子到父

实际工作都是使用事件冒泡为主

14.1 事件捕获

概念:从DOM的根元素开始去执行对应的事件 (从外到里)

代码:

1
Dom.addEventListener(事件类型,事件处理函数,是否使用捕获机制)

说明:

1
2
3
4
addEventListener第三个参数传入 true 代表是捕获阶段触发(很少使用)
若传入false代表冒泡阶段触发,默认就是false
若是用 L0 事件监听,则只有冒泡阶段,没有捕获

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
44
45
<!DOCTYPE html>
<html lang="en">

<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
.father {
width: 500px;
height: 500px;
background-color: blue;
}

.son {
width: 200px;
height: 200px;
background-color: purple;
}
</style>
</head>

<body>
<div class="father">
<div class="son"></div>
</div>
<script>
const fa = document.querySelector('.father')
const son = document.querySelector('.son')

document.addEventListener('click', function () {
alert('我是爷爷')
}, true)
fa.addEventListener('click', function () {
alert('我是爸爸')
}, true)
son.addEventListener('click', function () {
alert('我是儿子')
}, true)

</script>
</body>

</html>

14.2 事件冒泡

当一个元素的事件被触发时,同样的事件将会在该元素的所有祖先元素中依次被触发。这一过程被称为事件冒泡

简单理解:当一个元素触发事件后,会依次向上调用所有父级元素的 同名事件

事件冒泡是默认存在的

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
44
45
46
<!DOCTYPE html>
<html lang="en">

<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
.father {
width: 500px;
height: 500px;
background-color: blue;
}

.son {
width: 200px;
height: 200px;
background-color: purple;
}
</style>
</head>

<body>
<div class="father">
<div class="son"></div>
</div>
<script>
const fa = document.querySelector('.father')
const son = document.querySelector('.son')
document.addEventListener('click', function () {
alert('我是爷爷')
})
fa.addEventListener('click', function () {
alert('我是爸爸')
})
son.addEventListener('click', function (e) {
alert('我是儿子')
// 阻止流动传播 事件对象.stopPropagation()
e.stopPropagation()
})

</script>
</body>

</html>

14.3 阻止冒泡

因为默认就有冒泡模式的存在,所以容易导致事件影响到父级元素

若想把事件就限制在当前元素内,就需要阻止事件冒泡

阻止事件冒泡需要拿到事件对象

事件对象.stopPropagation()

14.4 阻止默认行为

我们某些情况下需要阻止默认行为的发生,比如 阻止 链接的跳转,表单域跳转

语法:

1
e.preventDefault()

14.5 解绑事件

on事件方式,直接使用null覆盖偶就可以实现事件的解绑

addEventListener方式,必须使用:

removeEventListener(事件类型, 事件处理函数, [获取捕获或者冒泡阶段])

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<body>
<button>点击</button>
<script>
const btn = document.querySelector('button')
// btn.onclick = function () {
// alert('点击了')
// // L0 事件移除解绑
// btn.onclick = null
// }
function fn() {
alert('点击了')
}
btn.addEventListener('click', fn)
// L2 事件移除解绑
btn.removeEventListener('click', fn)
</script>
</body>

注意:匿名函数无法被解绑

14.6 鼠标经过事件的区别

1
2
3
4
鼠标经过事件:
mouseover 和 mouseout 会有冒泡效果
mouseenter 和 mouseleave 没有冒泡效果 (推荐)

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
44
45
46
<!DOCTYPE html>
<html lang="en">

<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
.dad {
width: 400px;
height: 400px;
background-color: blue;
}

.baby {
width: 200px;
height: 200px;
background-color: purple;
}
</style>
</head>

<body>
<div class="dad">
<div class="baby"></div>
</div>
<script>
const dad = document.querySelector('.dad')
const baby = document.querySelector('.baby')
baby.addEventListener('mouseover', function () {
console.log('鼠标经过')
})
baby.addEventListener('mouseout', function () {
console.log('鼠标离开')
})
dad.addEventListener("mouseover",function(){
console.log('dad');
})
dad.addEventListener("mouseout",function(){
console.log("dad hello");
})
</script>
</body>

</html>

这里指定的mouseout,mouseover会有冒泡效果。

14.7 两种注册事件的区别

1
2
3
4
5
6
7
8
9
10
11
传统on注册(L0)
同一个对象,后面注册的事件会覆盖前面注册(同一个事件)
直接使用null覆盖偶就可以实现事件的解绑
都是冒泡阶段执行的
事件监听注册(L2)
语法: addEventListener(事件类型, 事件处理函数, 是否使用捕获)
后面注册的事件不会覆盖前面注册的事件(同一个事件)
可以通过第三个参数去确定是在冒泡或者捕获阶段执行
必须使用removeEventListener(事件类型, 事件处理函数, 获取捕获或者冒泡阶段)
匿名函数无法被解绑

15、事件委托

1.如果同时给多个元素注册事件,我们怎么做的?

​ for循环注册事件

2.有没有一种技巧 注册一次事件就能完成以上效果呢?

事件委托是利用事件流的特征解决一些开发需求的知识技巧

优点:减少注册次数,可以提高程序性能

原理:事件委托其实是利用事件冒泡的特点。

1
2
给父元素注册事件,当我们触发子元素的时候,会冒泡到父元素身上,从而触发父元素的事件

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
<!DOCTYPE html>
<html lang="en">

<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>

<body>
<ul>
<li>第1个孩子</li>
<li>第2个孩子</li>
<li>第3个孩子</li>
<li>第4个孩子</li>
<li>第5个孩子</li>
<p>我不需要变色</p>
</ul>
<script>
// 点击每个小li 当前li 文字变为红色
// 按照事件委托的方式 委托给父级,事件写到父级身上
// 1. 获得父元素
const ul = document.querySelector('ul')
ul.addEventListener('click', function (e) {
// alert(11)
// this.style.color = 'red'
// console.dir(e.target) // 就是我们点击的那个对象
// e.target.style.color = 'red'
// 我的需求,我们只要点击li才会有效果
if (e.target.tagName === 'LI') {
e.target.style.color = 'red'
}
})
</script>
</body>

</html>

16、其他事件

16.1 页面加载事件

加载外部资源(如图片、外联CSS和JavaScript等)加载完毕时触发的事件

应用场景:

(1)有些时候需要等页面资源全部处理完了做一些事情

(2)老代码喜欢把 script 写在 head 中,这时候直接找 dom 元素找不到

事件名称:load

监听页面所有资源加载完毕:

window添加load事件

1
2
3
window.addEventListener('load',function(){

})

注意:不光可以监听整个页面资源加载完毕,也可以针对某个资源绑定load事件

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
<!DOCTYPE html>
<html lang="en">

<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<script>
// 等待页面所有资源加载完毕,就回去执行回调函数
// window.addEventListener('load', function () {
// const btn = document.querySelector('button')
// btn.addEventListener('click', function () {
// alert(11)
// })
// })

// img.addEventListener('load', function () {
// // 等待图片加载完毕,再去执行里面的代码
// })

document.addEventListener('DOMContentLoaded', function () {
const btn = document.querySelector('button')
btn.addEventListener('click', function () {
alert(11)
})
})
</script>
</head>

<body>
<button>点击</button>

</body>

</html>

当初始的 HTML 文档被完全加载和解析完成之后,DOMContentLoaded 事件被触发,而无需等待样式表、图像等完全加载

事件名:DOMContentLoaded

监听页面DOM加载完毕:

给 document 添加 DOMContentLoaded 事件

16.2 页面滚动事件

滚动条在滚动的时候持续触发的事件

很多网页需要检测用户把页面滚动到某个区域后做一些处理, 比如固定导航栏,比如返回顶部

事件名:scroll

监听整个页面滚动

1
2
3
window.addEventListener('scroll',function(){

})

监听某个元素的内部滚动直接给某个元素加即可

使用场景:

我们想要页面滚动一段距离,比如100px,就让某些元素

显示隐藏,那我们怎么知道,页面滚动了100像素呢?

就可以使用scroll 来检测滚动的距离~~~

16.2.1 页面滚动事件-获取位置

1
2
3
4
5
scrollLeft和scrollTop (属性)
获取被卷去的大小
获取元素内容往左、往上滚出去看不到的距离
这两个值是可读写的
尽量在scroll事件里面获取被卷去的距离

开发中,我们经常检测页面滚动的距离,比如页面滚动100像素,就可以显示一个元素,或者固定一个元素

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
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
<!DOCTYPE html>
<html lang="en">

<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
body {
padding-top: 100px;
height: 3000px;
}

div {
display: none;
margin: 100px;
overflow: scroll;
width: 200px;
height: 200px;
border: 1px solid #000;
}
</style>
</head>

<body>
<div>
我里面有很多很多的文字
我里面有很多很多的文字
我里面有很多很多的文字
我里面有很多很多的文字
我里面有很多很多的文字
我里面有很多很多的文字
我里面有很多很多的文字
我里面有很多很多的文字
我里面有很多很多的文字
我里面有很多很多的文字
我里面有很多很多的文字
我里面有很多很多的文字
我里面有很多很多的文字
我里面有很多很多的文字

</div>
<script>
const div = document.querySelector('div')
// 页面滚动事件
window.addEventListener('scroll', function () {
// console.log('我滚了')
// 我想知道页面到底滚动了多少像素, 被卷去了多少 scrollTop
// 获取html元素写法
// document.documentElement
// console.log(document.documentElement.scrollTop)
const n = document.documentElement.scrollTop
if (n >= 100) {
div.style.display = 'block'
} else {
div.style.display = 'none'
}
})
// const div = document.querySelector('div')
// div.addEventListener('scroll', function () {
// // console.log(111)
// // scrollTop 被卷去的头部
// console.log(div.scrollTop)
// })
</script>
</body>

</html>

scrollTop细节

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
<!DOCTYPE html>
<html lang="en">

<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
body {
height: 3000px;
}
</style>
</head>

<body>
<script>
document.documentElement.scrollTop = 800
window.addEventListener('scroll', function () {
// 必须写到里面
const n = document.documentElement.scrollTop
// 得到是什么数据 数字型 不带单位
console.log(n)
})
</script>
</body>

</html>

16.3 页面尺寸事件

会在窗口尺寸改变的时候触发事件:

resize

1
2
3
window.addEventListener('resize',function(){

})

获取元素的可见部分宽高(不包含边框,margin,滚动条等)

clientWidth和clientHeight

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
<!DOCTYPE html>
<html lang="en">

<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
div {
display: inline-block;
/* width: 200px; */
height: 200px;
background-color: blue;
padding: 10px;
border: 20px solid red;
}
</style>
</head>

<body>
<div>123123123123123123123123123123123123123</div>
<script>
const div = document.querySelector('div')
console.log(div.clientWidth)
// resize 浏览器窗口大小发生变化的时候触发的事件
window.addEventListener('resize', function () {
console.log(1)
})
</script>
</body>

</html>

17、日期对象

作用:可以得到当前系统时间

在代码中发现了 new 关键字时,一般将这个操作称为实例化

创建一个时间对象并获取时间。

获取当前时间

1
const date  = new Date()

获取指定时间

1
2
const date = new Date('2022-2-2')
console.log(date);

1
2
3
4
5
6
7
8
9
<script>
// 获得日期对象
const date = new Date()
// 使用里面的方法
console.log(date.getFullYear())
console.log(date.getMonth() + 1) // 月份要 + 1
console.log(date.getDate())
console.log(date.getDay()) // 星期几
</script>

显示格式化的日期时间案例

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
<!DOCTYPE html>
<html lang="en">

<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
div {
width: 300px;
height: 40px;
border: 1px solid blue;
text-align: center;
line-height: 40px;
}
</style>
</head>

<body>
<div></div>
<script>
const div = document.querySelector('div')
function getMyDate() {
const date = new Date()
let h = date.getHours()
let m = date.getMinutes()
let s = date.getSeconds()
h = h < 10 ? '0' + h : h
m = m < 10 ? '0' + m : m
s = s < 10 ? '0' + s : s
return `今天是: ${date.getFullYear()}${date.getMonth() + 1}${date.getDate()}${h}:${m}:${s}`
}

div.innerHTML = getMyDate()
setInterval(function () {
div.innerHTML = getMyDate()
}, 1000)
</script>
</body>

</html>

18、节点操作

DOM节点:DOM树里每一个内容都称之为节点

1
2
3
4
5
6
节点类型
元素节点:所有的标签 比如 body、 div,html 是根节点
属性节点:所有的属性,比如href等
文本节点:所有的文本


18.1 查找节点

节点关系

1
2
3
父节点
子节点
兄弟节点

父节点查找

​ 通过parentNode属性进行查找,返回最近一级的父节点,找不到返回的是null

1
子元素.parentNode
1
2
3
4
5
6
7
8
9
10
11
12
13
<body>
<div class="yeye">
<div class="dad">
<div class="baby">x </div>
</div>
</div>
<script>
const baby = document.querySelector('.baby')
console.log(baby) // 返回dom对象
console.log(baby.parentNode) // 返回dom对象
console.log(baby.parentNode.parentNode) // 返回dom对象
</script>
</body>

子节点查找

childNodes: 获得所有子节点、包括文本节点(空格、换行)、注释节点等

children 属性 (重点): 仅获取所有元素节点,返回的是一个伪数组

1
父元素.children

兄弟关系查找:

1
2
3
4
5
    1. 下一个兄弟节点 
nextElementSibling 属性
2. 上一个兄弟节点
previousElementSibling 属性

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<body>
<ul>
<li>1</li>
<li>2</li>
<li>3</li>
<li>4</li>
<li>5</li>
</ul>
<script>
// const ul = document.querySelector('ul') // ul
// console.log(ul.children) // 得到伪数组 选择的是 亲儿子
const li2 = document.querySelector('ul li:nth-child(2)')
console.log(li2.previousElementSibling) // 上一个兄弟
console.log(li2.nextElementSibling) // 下一个兄弟
</script>
</body>

18.2 增加节点

l即创造出一个新的网页元素,再添加到网页内,一般先创建节点,然后插入节点

创建元素节点方法:

1
2
// 创建一个新的元素节点
document.createElement('标签名')

要想在界面看到,还得插入到某个父元素中

插入到父元素的最后一个子元素:

1
2
3
4
5
// 插入到这个父元素的最后
父元素.appendChild(要插入的元素)

// 插入到某个子元素的前面
父元素.insertBefore(要插入的元素,在哪个元素前面)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
<body>
<ul>
<li>我是老大</li>
</ul>
<script>
// // 1. 创建节点
// const div = document.createElement('div')
// // console.log(div)
// 2. 追加节点 作为最后一个子元素
// document.body.appendChild(div)
const ul = document.querySelector('ul')
const li = document.createElement('li')
li.innerHTML = '我是li'
// ul.appendChild(li)
// ul.children
// 3. 追加节点
// insertBefore(插入的元素, 放到哪个元素的前面)
ul.insertBefore(li, ul.children[0])
</script>
</body>


克隆节点

特殊情况下,我们新增节点,按照如下操作:

1
2
3
复制一个原有的节点

把复制的节点放入到指定的元素内部
1
2
// 克隆一个已有的元素节点
元素.cloneNode(布尔值);
1
2
3
4
5
cloneNode会克隆出一个跟原标签一样的元素,括号内传入布尔值
若为true,则代表克隆时会包含后代节点一起克隆
若为false,则代表克隆时不包含后代节点
默认为false

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<body>
<ul>
<li>1</li>
<li>2</li>
<li>3</li>
</ul>
<script>
const ul = document.querySelector('ul')
// 1 克隆节点 元素.cloneNode(true)
// const li1 = ul.children[0].cloneNode(true)
// console.log(li1)
// 2. 追加
ul.appendChild(ul.children[0].cloneNode(true))
</script>
</body>

18.3 删除节点

若一个节点在页面中已不需要时,可以删除它

在 JavaScript 原生DOM操作中,要删除元素必须通过父元素删除

语法:

1
父元素.removeChild(要删除的元素)

如不存在父子关系则删除不成功

删除节点和隐藏节点(display:none) 有区别的: 隐藏节点还是存在的,但是删除,则从html中删除节点

1
2
3
4
5
6
7
8
9
10
11
12
<body>
<div class="box">123</div>
<ul>
<li>没用了</li>
</ul>
<script>
const ul = document.querySelector('ul')
// 删除节点 父元素.removeChlid(子元素)
ul.removeChild(ul.children[0])
</script>
</body>

19、Window对象

19.1 BOM

BOM(Browser Object Model ) 是浏览器对象模型

1
2
3
4
5
6
7
8
window对象是一个全局对象,也可以说是JavaScript中的顶级对象
像document、alert()、console.log()这些都是window的属性,基本BOM的属性和方法都是window的。
所有通过var定义在全局作用域中的变量、函数都会变成window对象的属性和方法
window对象下的属性和方法调用的时候可以省略window

location: 浏览器地址


1
2
3
4
5
6
7
8
9
10
11
12
13
14
<body>
<script>
// document.querySelector()
// window.document.querySelector()
console.log(document === window.document)
function fn() {
console.log(11)
}
window.fn()
var num = 10
console.log(window.num)

</script>
</body>

19.2 定时器-延时函数

JavaScript 内置的一个用来让代码延迟执行的函数,叫 setTimeout

语法:

1
setTimeout(回调函数,等待的毫秒数)

setTimeout 仅仅只执行一次,所以可以理解为就是把一段代码延迟执行, 平时省略window

清除延时函数

1
2
let timer = setTimeout(回调函数,等待的毫秒数)
clearTimeout(timer)
1
2
3
4
两种定时器对比:执行的次数
延时函数: 执行一次
间歇函数:每隔一段时间就执行一次,除非手动清除

1
2
3
4
5
6
7
8
<body>
<script>
setTimeout(function () {
console.log('时间到了')
}, 2000)
</script>
</body>

19.3 JS执行机制

JavaScript 语言的一大特点就是单线程,也就是说,同一个时间只能做一件事。

这是因为 Javascript 这门脚本语言诞生的使命所致——JavaScript 是为处理页面中用户的交互,以及操作 DOM 而诞生的。比如我们对某个 DOM 元素进行添加和删除操作,不能同时进行。 应该先进行添加,之后再删除。

单线程就意味着,所有任务需要排队,前一个任务结束,才会执行后一个任务。这样所导致的问题是: 如果 JS 执行的时间过长,这样就会造成页面的渲染不连贯,导致页面渲染加载阻塞的感觉。

为了解决这个问题,利用多核 CPU 的计算能力,HTML5 提出 Web Worker 标准,允许 JavaScript 脚本创建多个线程。于是,JS 中出现了同步和异步。

同步

前一个任务结束后再执行后一个任务,程序的执行顺序与任务的排列顺序是一致的、同步的。比如做饭的同步做法:我们要烧水煮饭,等水开了(10分钟之后),再去切菜,炒菜。

异步

你在做一件事情时,因为这件事情会花费很长时间,在做这件事的同时,你还可以去处理其他事情。比如做饭的异步做法,我们在烧水的同时,利用这10分钟,去切菜,炒菜。

同步任务

同步任务都在主线程上执行,形成一个执行栈。

异步任务

JS 的异步是通过回调函数实现的。

一般而言,异步任务有以下三种类型:

1、普通事件,如 click、resize 等

2、资源加载,如 load、error 等

3、定时器,包括 setInterval、setTimeout 等

异步任务相关添加到任务队列中(任务队列也称为消息队列)。

  1. 先执行执行栈中的同步任务。

  2. 异步任务放入任务队列中。

  3. 一旦执行栈中的所有同步任务执行完毕,系统就会按次序读取任务队列中的异步任务,于是被读取的异步任务结束等待状态,进入执行栈,开始执行。

由于主线程不断的重复获得任务、执行任务、再获取任务、再执行,所以这种机制被称为事件循环( event loop)。

1
2
3
4
5
6
7
8
9
10
11
<body>
<script>
console.log(1)
console.log(2)
setTimeout(function () {
console.log(3)
}, 0)
console.log(4);
</script>
</body>

19.4 location对象

1
2
3
4
5
6
7
location 的数据类型是对象,它拆分并保存了 URL 地址的各个组成部分
常用属性和方法:
href 属性获取完整的 URL 地址,对其赋值时用于地址的跳转
search 属性获取地址中携带的参数,符号 ?后面部分
hash 属性获取地址中的啥希值,符号 # 后面部分
reload 方法用来刷新当前页面,传入参数 true 时表示强制刷新

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
<body>
<form action="">
<input type="text" name="username">
<input type="password" name="pwd">
<button>提交</button>
</form>

<a href="#/my">我的</a>
<a href="#/friend">关注</a>
<a href="#/download">下载</a>
<button class="reload">刷新</button>

<script>
// console.log(window.location)
// console.log(location)
// console.log(location.href)
// 1. href 经常用href 利用js的方法去跳转页面
// location.href = 'http://www.baidu.com'
const reload = document.querySelector('.reload')
reload.addEventListener('click', function () {
// f5 刷新页面
// location.reload()
// 强制刷新 ctrl+f5
location.reload(true)
})
</script>
</body>

5秒钟之后跳转到首页

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
<!DOCTYPE html>
<html lang="en">

<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
span {
color: red;
}
</style>
</head>

<body>
<a href="http://www.baidu.cn">支付成功<span>5</span>秒钟之后跳转到首页</a>
<script>
// 1. 获取元素
const a = document.querySelector('a')
// 2.开启定时器
// 3. 声明倒计时变量
let num = 5
let timerId = setInterval(function () {
num--
a.innerHTML = `支付成功<span>${num}</span>秒钟之后跳转到首页`
// 如果num === 0 则停止定时器,并且完成跳转功能
if (num === 0) {
clearInterval(timerId)
// 4. 跳转 location.href
location.href = 'http://www.baidu.cn'
}
}, 1000)
</script>
</body>

</html>

19.5 navigator对象

navigator的数据类型是对象,该对象下记录了浏览器自身的相关信息

常用属性和方法:

通过 userAgent 检测浏览器的版本及平台

1
2
3
4
5
6
7
8
9
10
11
       // 检测 userAgent(浏览器信息)
        !(function () {
            const userAgent = navigator.userAgent
            // 验证是否为Android或iPhone
            const android = userAgent.match(/(Android);?[\s\/]+([\d.]+)?/)
            const iphone = userAgent.match(/(iPhone\sOS)\s([\d_]+)/)
           // 如果是Android或iPhone,则跳转至移动站点
            if (android || iphone) {
                location.href = 'http://m.itcast.cn'
            }      })()

19.6 histroy对象

history 的数据类型是对象,主要管理历史记录, 该对象与浏览器地址栏的操作相对应,如前进、后退、历史记录等

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<body>
<button>后退</button>
<button>前进</button>
<script>
const back = document.querySelector('button:first-child')
const forward = back.nextElementSibling
back.addEventListener('click', function () {
// 后退一步
// history.back()
history.go(-1)
})
forward.addEventListener('click', function () {
// 前进一步
// history.forward()
history.go(1)
})
</script>
</body>

20、本地存储

20.1 本地存储介绍

以前我们页面写的数据一刷新页面就没有了,是不是?

随着互联网的快速发展,基于网页的应用越来越普遍,同时也变的越来越复杂,为了满足各种各样的需求,会经常性在本地存储大量的数据,HTML5规范提出了相关解决方案。

1、数据存储在用户浏览器中

2、设置、读取方便、甚至页面刷新不丢失数据

3、容量较大,sessionStorage和localStorage约 5M 左右

20.2 本地存储分类- localStorage

1
2
3
4
5
作用: 可以将数据永久存储在本地(用户的电脑), 除非手动删除,否则关闭页面也会存在
特性:
可以多窗口(页面)共享(同一浏览器可以共享)
以键值对的形式存储使用

语法:

1
2
3
4
5
6
7
存储数据:
localStorage.setItem(key, value)
获取数据:
localStorage.getItem(key)
删除数据
localStorage.removeItem(key)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<body>
<script>
// 1. 要存储一个名字 'uname',
// localStorage.setItem('键','值')
localStorage.setItem('uname', '张三')
// 2. 获取方式 都加引号
console.log(localStorage.getItem('uname'))
// 3. 删除本地存储 只删除名字
// localStorage.removeItem('uname')
// 4. 改 如果原来有这个键,则是改,如果么有这个键是增
localStorage.setItem('uname', '张老师')

// 我要存一个年龄
// 2. 本地存储只能存储字符串数据类型
localStorage.setItem('age', 18)
console.log(localStorage.getItem('age'))
</script>
</body>

20.3 本地存储分类- sessionStorage

1
2
3
4
5
6
特性:
生命周期为关闭浏览器窗口
在同一个窗口(页面)下数据可以共享,另外一个页面无法获取到,默认返回的null
以键值对的形式存储使用
用法跟localStorage 基本相同

20.4 存储复杂数据类型

本地只能存储字符串,无法存储复杂数据类型.

解决:需要将复杂数据类型转换成JSON字符串,在存储到本地

语法:JSON.stringify(复杂数据类型)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
<body>
<script>
const obj = {
uname: '张三',
age: 18,
gender: '女'
}
// // 存储 复杂数据类型 无法直接使用
// localStorage.setItem('obj', obj) [object object]
// // 取
// console.log(localStorage.getItem('obj'))

// 1.复杂数据类型存储必须转换为 JSON字符串存储
localStorage.setItem('obj', JSON.stringify(obj))
// JSON 对象 属性和值有引号,而是引号统一是双引号
// {"uname":"张三","age":18,"gender":"女"}
// 取
// console.log(typeof localStorage.getItem('obj'))
// 2. 把JSON字符串转换为 对象
const str = localStorage.getItem('obj') // {"uname":"张三","age":18,"gender":"女"}
console.log(JSON.parse(str))
</script>

问题****:因为本地存储里面取出来的是字符串,不是对象,无法直接使用

解决:把取出来的字符串转换为对象

**语法:JSON.parse(JSON字符串)

20.5 数组中的map方法

map 迭代数组

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<body>
<script>

const arr = ['red', 'blue', 'green']
// map 方法也是遍历 处理数据 可以返回一个数组
const newArr = arr.map(function (item, i) {
// console.log(item) // 数组元素 'red'
// console.log(i) // 下标
return item + 'hello'
})
console.log(newArr)


const arr1 = [10, 20, 30]
const newArr1 = arr1.map(function (item) {
return item + 10
})
console.log(newArr1)

</script>
</body>


map 可以处理数据,并且返回新的数组

20.6 数组中join方法

join() 方法用于把数组中的所有元素转换一个字符串

1
2
3
4
5
6
7
8
<body>
<script>
const arr = ['red', 'blue', 'green']
// 把数组元素转换为字符串
console.log(arr.join('*'))
</script>
</body>

参数:数组元素是通过参数里面指定的分隔符进行分隔的

21、正则表达式

正则表达式(Regular Expression)是用于匹配字符串中字符组合的模式。在 JavaScript中,正则表达式也是对象

通常用来查找、替换那些符合正则表达式的文本,许多语言都支持正则表达式。

正则表达式在 JavaScript中的使用场景:

例如验证表单:用户名表单只能输入英文字母、数字或者下划线, 昵称输入框中可以输入中文(匹配)

过滤掉页面内容中的一些敏感词(替换),或从字符串中获取我们想要的特定部分(提取)等

21.1 语法

1
const 变量名 = /表达式/

/开始,以/结束

例如:

1
const reg =/前端/

判断是否有符合规则的字符串

test() 方法 用来查看正则表达式与指定的字符串是否匹配

例如:

1
2
3
4
5
// 要检测的字符串
const str ='前端开发,前端工程师'
// 定义正则表达式
const reg =/前端/
console.log(reg.test(str)); // true

下面再来介绍另外一个方法:exec()方法。在一个指定字符串中执行一个搜索匹配

例如:

1
2
3
4
5
// 要检测的字符串
const str ='前端开发,前端工程师'
// 定义正则表达式
const reg =/前端/
console.log(reg.exec(str)); // 返回的是数组

如果匹配成功,exec()方法返回的是一个数组,否则返回null.

21.2 元字符

普通字符

大多数的字符仅能够描述它们本身,这些字符称作普通字符,例如所有的字母和数字。
也就是说普通字符只能够匹配字符串中与它们相同的字符。

元字符**(**特殊字符)

1
2
3
4
是一些具有特殊含义的字符,可以极大提高了灵活性和强大的匹配功能。
比如,规定用户只能输入英文26个英文字母,普通字符的话 abcdefghijklm…..
但是换成元字符写法: [a-z]

参考文档:

MDN:https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Guide/Regular_Expressions

正则测试工具: http://tool.oschina.net/regex

1
2
3
4
5
为了方便记忆和学习,我们对众多的元字符进行了分类:
边界符(表示位置,开头和结尾,必须用什么开头,用什么结尾)
量词 (表示重复次数)
字符类 (比如 \d 表示 0~9)

21.2.1 边界符

正则表达式中的边界符(位置符)用来提示字符所处的位置,主要有两个字符

如果 ^ 和 $ 在一起,表示必须是精确匹配

1
2
3
4
5
6
7
8
9
10
11
12
13
// 元字符

// console.log(/哈/.test('哈')) // true
// console.log(/哈/.test('哈哈')) // true
// console.log(/哈/.test('二哈')) // true
// console.log('------------------')
// // 1. 边界符
// console.log(/^哈/.test('哈')) // true
// console.log(/^哈/.test('哈哈')) // true
// console.log(/^哈/.test('二哈')) // flase
// console.log(/^哈$/.test('哈')) // true 只有这种情况为true 否则全是false
// console.log(/^哈$/.test('哈哈')) // false
// console.log(/^哈$/.test('二哈')) // false

21.2.2 量词

量词用来 设定某个模式出现的次数

注意: 逗号左右两侧千万不要出现空格

21.2.3 字符类

(1)[ ] 匹配字符集合

后面的字符串只要包含 abc 中任意一个字符,都返回 true 。

(1)[ ] 里面加上 - 连字符

使用连字符 - 表示一个范围

1
2
3
4
5
比如:
[a-z] 表示 a 到 z 26个英文字母都可以
[a-zA-Z] 表示大小写都可以
[0-9] 表示 0~9 的数字都可以

1
console.log(/^[a-z]$/.test('c')); // true

(1)[ ] 里面加上 ^ 取反符号

1
2
3
4
比如:
[^a-z] 匹配除了小写字母以外的字符
注意要写到中括号里面

用户名验证案例

需求:用户名要求用户英文字母,数字,下划线或者短横线组成,并且用户名长度为 6~16位

1
2
3
4
5
6
分析:
(1):首先准备好这种正则表达式模式 /^[-a-zA-Z0-9_]{6,16}$/
(2):当表单失去焦点就开始验证.
(3):如果符合正则规范, 则让后面的span标签添加 right 类.
(4):如果不符合正则规范, 则让后面的span标签添加 wrong 类.

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
44
45
46
47
48
49
50
51
52
<!DOCTYPE html>
<html lang="en">

<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
span {
display: inline-block;
width: 250px;
height: 30px;
vertical-align: middle;
line-height: 30px;
padding-left: 15px;
}

.error {
color: red;
background: url(./images/error1.png) no-repeat left center;
}

.right {
color: green;
background: url(./images/right.png) no-repeat left center;
}
</style>
</head>

<body>
<input type="text">
<span></span>
<script>
// 1. 准备正则
const reg = /^[-a-zA-Z0-9_]{6,16}$/
const input = document.querySelector('input')
const span = input.nextElementSibling
input.addEventListener('blur', function () {
// console.log(reg.test(this.value))
if (reg.test(this.value)) {
span.innerHTML = '输入正确'
span.className = 'right'
} else {
span.innerHTML = '请输入6~16位的英文数字下划线'
span.className = 'error'
}
})
</script>
</body>

</html>

预定义:指的是 某些常见模式的简写方式。

以数字开头,并且是4位数字

21.2.4 修饰符

修饰符约束正则执行的某些细节行为,如是否区分大小写、是否支持多行匹配等

语法:

1
/表达式/修饰符
1
2
3
i 是单词 ignore 的缩写,正则匹配时字母不区分大小写
g 是单词 global 的缩写,匹配所有满足正则表达式的结果

1
2
console.log(/a/i.test('a')) // true
console.log(/a/i.test('A')) // true
1
2
3
console.log(/^java$/.test('java')) // true
console.log(/^java$/i.test('JAVA')) // true
console.log(/^java$/i.test('Java')) // true

替换 replace 替换

语法

1
字符串.replace(/正则表达式/,'替换的文本')
1
2
3
const str = 'java是一门编程语言, 学完JAVA工资很高'
const re = str.replace(/java|JAVA/g, 'c#')
console.log(re)

上面的正则表达式中,我们是添加了java的大小写。

这里也可以使用i这个修饰符来进行表示

1
2
const re = str.replace(/java/ig, 'c#')
console.log(re)

敏感词过滤

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<body>
<textarea name="" id="" cols="30" rows="10"></textarea>
<button>发布</button>
<div></div>
<script>
const tx = document.querySelector('textarea')
const btn = document.querySelector('button')
const div = document.querySelector('div')
btn.addEventListener('click', function () {
// console.log(tx.value)
div.innerHTML = tx.value.replace(/激情|基情/g, '**')
tx.value = ''
})
</script>
</body>