DOM相关

DOM👀

🌈 什么是DOM

DOM(Document Object Model,文档对象模型)是JavaScript操作HTML文档的接口,使文档操作变得非常优雅、简便。

🔔nodeType属性值

节点的nodeType属性可以显示这个节点具体的类型。

nodeType值 节点类型
1 元素节点
3 文本节点
8 注释节点
9 document节点
10 DTD节点(文档类型声明)

⭕DOM节点操作

📍访问元素节点的常用方法

方法 功能 兼容性
document.getElementById() 通过id获得元素 IE6
document.getElementsByTagName() 通过标签名获得元素数组 IE6
document.getElementsByClassName() 通过类名获得元素数组 IE9
document.querySelect() 通过选择器获得元素 IE8部分兼容、IE9完全兼容
document.querySelectAll() 通过选择器获得元素数组 IE8部分兼容、IE9完全兼容

拓展:动态获取元素

1.getElementsByClassName()方法和getElementsByClassName()方法可以动态获取元素,可以理解为,页面上增加或者删除元素时,获取的元素个数可以改变;
2.而querySelectorAll()方法做不到此效果。

📍节点的关系

关系 考虑所有节点 只考虑元素节点
子节点 childNode children
父节点 parentNode parentNode
第一个子节点 firstChild firstElementChild
最后一个子节点 lastChild lastElementChild
前一个兄弟节点 previousSibling previousElementSibling
后一个兄弟节点 nextSibling nextElementSibling

注意:文本节点也属于节点

在标准的W3C规范中,空白文本节点也应该算作节点,但是在IE8及以前的浏览器中会有一定的兼容问题,它们不把空文本节点当做节点

💡书写IE6也能兼容的“寻找所有元素子节点”函数

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 name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<div id="box">
<p>我是一段落1</p>
<p>我是一段落2</p>
我是文本文本文本
<p>我是一段落3</p>
<p>我是一段落4</p>
<p>我是一段落5</p>
</div>
<script>
// 书写IE6也能兼容的“寻找所有元素子节点”函数

// 获取父节点
var oBox = document.getElementById('box')
// 获取所有子节点
var arr_p = oBox.childNodes
console.log(arr_p) // 打印的内容含有文本节点

// 封装函数 通过 nodeType 属性来筛选出元素节点
function getElementChild(obj) {
// 获取所有的子节点
var arrChild = obj.childNodes
// 定义结果数组
var result = []
for (var i = 0 ; i < arrChild.length ; i ++) {
// 元素节点的nodeType值为1
arrChild[i].nodeType === 1 ? result.push(arrChild[i]) : ''
}
return result
}
// 打印子节点元素
console.log(getElementChild(oBox))
</script>
</body>
</html>

💡封装一个的函数,这个的函数可以返回元素的前一个元素兄弟节点(兼容到IE6),类似previousE1ementsib1ing的功能

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
//封装一个的函数,这个的数可以返回元素的前一个元素兄弟节点(兼容到IE6),类似previousE1ementsib1ing的功能
function getPreviousElementSibling(bro) {
var o = bro;
// 循环判断前一个兄弟节点是否存在
while (o.previousSibling != null) {
// nodeType 判断节点属性
if (o.previousSibling.nodeType === 1) return o.previousSibling;
// 让它成为前一个节点
o = o.previousSibling
}
// 找不到返回null
return null
}
// 利用前文封装的函数 getElementChild()
console.log(getPreviousElementSibling(getElementChild(oBox)[3]))

💡**封装第三个函数,这个函数可以返回元素的所有元素兄弟节点 **

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
// 封装第三个函数,这个函数可以返回元素的所有元素兄弟节点
function getElementSibling(bro){
var o = bro
// 前面的兄弟节点数组
var previous = []
// 后面的兄弟节点数组
var next = []
// 循环 找到前面的元素节点
while(o.previousSibling != null) {
if (o.previousSibling.nodeType === 1) {
// 考虑到顺序,使用unshift
previous.unshift(o.previousSibling)
}
o = o.previousSibling
}
// 将o重置
o = bro
// 循环 找到后面的元素节点
while(o.nextSibling != null) {
if (o.nextSibling.nodeType === 1) {
// 考虑到顺序,使用unshift
next.push(o.nextSibling)
}
o = o.nextSibling
}
// 组合数组
return previous.concat(next)
}
console.log(getElementSibling(getElementChild(oBox)[2]))

📍改变元素节点中的内容

  1. innerHTML属性:能以HTML语法设置节点中的内容
  2. innerText属性:只能以纯文本的形式设置节点中的内容

📍改变元素节点的CSS样式

1
2
3
4
//改变元素节点的CSS样式需要使用这样的语句
oBox.style.backgroundColor ='red'
oBox.style.backgroundImage ='url(images/1.jpg)'
oBox.style.fontSize ='32px'

📍改变元素节点的HTML属性

1
2
标准W3C属性,如src、href等等,只需要直接打点进行更改即可
oImg.src='images/2.jpg'
1
2
3
4
不符合W3C标准的属性,要使用setAttribute()和getAttribute()来设置、读取
oBox.setAttribute('data-n', 10)
var n=oBox.getAttribute('data-n')
alert(n)

⭕节点的创建

**document.creatElement()**方法用于创还能一个指定tagname的HTML元素

var oDiv = document.creatElement(‘div’)

“孤儿节点”

新创建出的节点是“孤儿节点”,这意味着它并没有被挂载到DOM树上,我们无法看见它

必须继续使用**appendChild() 或 insertBefore()**方法将孤儿节点插入到DOM树上

📍appendChild()

任何已经在DOM树上的节点,都可以调用appendChild()方法,它可以将孤儿节点挂载到它的内部,成为它的最后一个子节点

1
父节点.appendChild(孤儿节点)

📍insertBefore()

任何已经在DOM树上的节点,都可以调用insertBefore0)方法,它可以将孤儿节点挂载到它的内部,成为它的“标杆子节点”之前的节点

1
父节点.insertBefor(孤儿节点,标杆节点)

💡请动态创建出一个20行12列的表格

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 name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
table {
border-collapse: collapse;
}
table td {
width: 50px;
height: 20px;
border: 1px solid #000;
}
</style>
</head>
<body>
<table id="table" border></table>
<script>
// 请动态创建出一个20行12列的表格
// 获取表格
var oTable = document.getElementById('table')
for (var i = 0 ; i < 20 ; i++) {
// 创建行
var oTr = document.createElement('tr')
// 上树
oTable.appendChild(oTr)
for(var j = 0 ; j < 12 ; j++) {
// 创建列
var oTd = document.createElement('td')
// 上树
oTr.appendChild(oTd)
}
}
</script>
</body>
</html>

💡制作九九乘法表(以上代码改动版)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// 制作九九乘法表 --- 改动部分
var oTable = document.getElementById('table');
for (var i = 1; i <= 9; i++) {
// 创建行
var oTr = document.createElement('tr');
// 上树
oTable.appendChild(oTr);
for (var j = 1; j <= i; j++) {
// 创建列
var oTd = document.createElement('td');
// 在td中添加内容
oTd.innerText = i + '乘' + j + '等于' + i * j;
// 上树
oTr.appendChild(oTd);
}
}

📍移动节点

如果将已经挂载到DOM树上的节点成为appendChild()或者insertBefore()的参数,这个节点将会被移动

1
2
新父节点.appendChild(已经有父亲的节点)
新父节点.insertBefore(已经有父亲的节点,标杆节点)

🔔这意味着一个节点不能同时位于DOM树的两个位置

📍删除节点

removeChild() 方法从DOM中删除一个节点

1
父节点.removeChild(要删除的子节点)

🔔节点不能主动删除自己,必须有父节点删除它

📍克隆节点

cloneNode() 方法可以克隆节点,克隆出的节点是“孤儿节点”

1
2
var 孤儿节点 = 老节点.cloneNode()
var 孤儿节点 = 老节点.cloneNode(true)

🔔参数是一个布尔值,表示是否采用深度克隆:如果为true,则该节点的所有后代节点也都会被克隆;如果为false,则只克隆该节点本身

⭕事件监听

📍常见的鼠标事件监听

事件名 事件描述
onclick 当鼠标单击某个对象
ondblclick 当鼠标双击某个对象
onmousedown 当鼠标按键在某个对象上被按下
onmouseup 当鼠标按键在某个对象上被松开
onmousemove 当鼠标按键在某个对象上被移动
onmouseenter 当鼠标进入某个对象(相似事件onmouseover)
onmouseleave 当鼠标离开某个对象(相似事件onmouseout)

📍常见的键盘事件监听

事件名 事件描述
onkeypress 当某个键盘的键被按下(系统按钮如箭头和功能键无法得到识别)
onkeydown 当某个键盘的键被按下(系统按钮可以识别,并且会优先于onkeypress发生)
onkeyup 当某个键盘的按键被松开

📍常见的表单事件监听

事件名 事件描述
onchange 当用户修改完了域的内容
oninput 当用户正在修改域的内容
onfocus 当某元素获得焦点(比如tab键或鼠标点击)
onblur 当某元素失去焦点
onsubmit 当表单被提交
onreset 当表单被重置

📍常见的页面事件监听

事件名 事件描述
onload 当页面或图像被完全加载
onunload 当用户退出页面

🔔更多关于窗口或者页面的事件,后续在BOM章节介绍

⭕事件传播

事件的传播:先从外到内,然后再从内到外

从外到内:捕获阶段

从内到外:冒泡阶段

❗onxxx这样的写法只能监听到冒泡阶段

🔔addEventListener() 方法

DOM0级事件监听:只能监听冒泡阶段

​ oBox.onclick = function() { }

DOM2级事件监听:

​ oBox.addEventListener(‘click’ , function () {

} , true ) // ‘click’ 为事件名(注意不加on),true为监听捕获阶段,flase为监听冒泡阶段

❗注意事项

1.最内部元素不再区分捕获和冒泡阶段,会先执行写在前面的监听,然后执行后写的监听

2.如果给元素设置相同的两个或多个同名事件,则DOM0级写法后面写的会覆盖先写的;而DOM2级会按顺序执行

💡小练习:实现计算器功能

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
<!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>
<input type="text" id="num1" />
<select id="operate">
<option value="+">+</option>
<option value="-">-</option>
<option value="*">*</option>
<option value="/">/</option>
</select>
<input type="text" id="num2" />
<input type="button" value=" = " id="btn" />
<input type="text" id="result" />
<script>
// 获取元素
var num1 = document.getElementById('num1');
var num2 = document.getElementById('num2');
var result = document.getElementById('result');
var operate = document.getElementById('operate');
var btn = document.getElementById('btn');
// 监听等号按钮的click事件
btn.addEventListener('click', function () {
// 根据 option.value 判断计算方式
switch (operate.value) {
case '+':
result.value = Number(num1.value) + Number(num2.value);
break;
case '-':
result.value = num1.value - num2.value;
break;
case '*':
result.value = num1.value * num2.value;
break;
case '/':
result.value = num1.value / num2.value;
break;
}
});
</script>
</body>
</html>

⭕事件对象

事件处理函数提供了一个形式参数,它是一个对象封装了本次事件的细节

这个参数通常个用单词event或字母e来表示

oBox.onmouse = function (e) {

​ // 对象e就是这次事件的“事件对象”

};

📍鼠标位置

属性 属性描述
clientX 鼠标指针相对于浏览器的水平坐标
clientY 鼠标指针相对于浏览器的垂直坐标
pageX 鼠标指针相对于整张网页的水平坐标
pageY 鼠标指针相对于整张网页的垂直坐标
offsetX 鼠标指针相对于事件源元素的水平坐标
offsetY 鼠标指针相对于事件源元素的垂直坐标
鼠标位置

❗注意:offsetX / offsetY 是鼠标相对事件源元素的坐标,如果监听的对象内部有元素,当鼠标移动到内部的对象时,offsetX / offsetY 的值会重新根据内部元素来计算了(此时事件源元素发生了改变,所以使用时需要避免有内部元素存在)

📍e.charCode 和 e.keyCode 属性

e.charCode属性通常用于onkeypress事件中,表示用户输入的字符的“字符码”

e.keyCode属性通常用于onkeydown 和 onkeyup事件中,表示用户按下的按键的“键码”

🔔charCode字符码

字符 字符码
数字0~数字9 48~57
大写字母A~Z 65~90
小写字母a~z 97~122

🔔keyCode键码

按键 键码
数字0~数字9 48~57(同charCode键码完全相同)
字母不分大小a~z 6590(同charCode键码的大写字母AZ,而keyCode不分大小写,一律为65~90)
四个方向键 ← ↑ → ↓ 37、38、39、40
回车键 13
空格键 32

💡小案例:控制盒子移动

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
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
<style>
* {
margin: 0;
padding: 0;
}
#box1 {
position: absolute;
top: 35%;
left: 50%;
margin-top: -50px;
margin-left: -50px;
width: 100px;
height: 100px;
background-color: orange;
transition: all .2s linear 0s;
}
#box2 {
position: fixed;
bottom: 50px;
left: 50%;
margin-left: -120px;
width: 240px;
height: 60px;
background-color: rgba(0, 0, 0, 0.5);
border-radius: 20px;
transition: all .2s linear 0s;
}
#text {
text-align: center;
line-height: 60px;
font-size: 30px;
color: white;
letter-spacing: 5px;
}
</style>
</head>
<body>
<div id="box1"></div>
<div id="box2"><p id="text">方块向上移动</p></div>
<script>
// 获取盒子元素
var oBox1 = document.getElementById('box1');
var oBox2 = document.getElementById('box2');
var oText = document.getElementById('text')
// 全局变量 top值和left值
var t = 35;
var l = 50;
// 添加事件监听
window.addEventListener('keydown', function (e) {
switch (e.keyCode) {
case 37:
l -= 5
oText.innerText = '方块向左移动'
break;
case 38:
t -= 5
oText.innerText = '方块向上移动'
break;
case 39:
l += 5
oText.innerText = '方块向右移动'
break;
case 40:
t += 5
oText.innerText = '方块向下移动'
break;
}
oBox1.style.top = t + '%'
oBox1.style.left = l + '%'
});
</script>
</body>
</html>

📍e.preventDefault()方法

e.preventDefault() 方法来阻止事件产生的“默认动作”

一些特殊的业务需求,需要阻止事件的“默认动作”

💡小案例1:制作文本框,只能让用户在其中输入小写字母和数字,其他字符输入没有效果

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
<!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>
<!-- 制作文本框,只能让用户在其中输入小写字母和数字,其他字符输入没有效果 -->
<p>
只能输入小写字母和数字:
<input type="text" id="field">
</p>
<script>
// 获取元素
var ofield = document.getElementById('field')
// 事件监听
ofield.addEventListener('keypress', function (e) {
console.log(e.charCode)
if (!(e.charCode >= 48 && e.charCode <= 57 || e.charCode >= 97 && e.charCode <= 122)) {
// 阻止文本框的默认动作————阻止输入非数字和非小写字母的显示
e.preventDefault()
}
})
</script>
</body>
</html>

💡小案例2:制作鼠标滚轮事件:当鼠标在盒子中向下滚动时,数字加1;反之,数字减1

鼠标滚轮事件是 onmousewheel ,它的事件对象e提供deltaY属性表示鼠标滚动方向,向下滚动时返回正值,向上滚动时返回负值

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 name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
#box {
width: 200px;
height: 200px;
background-color: orange;
}
body {
height: 2000px;
}
</style>
</head>
<body>
<!-- 制作鼠标滚轮事件:当鼠标在盒子中向下滚动时,数字加1;反之,数字减1 -->
<div id="box"></div>
<p id="num">0</p>
<script>
// 获取元素
var oBox = document.getElementById('box')
var oNum = document.getElementById('num')
// 显示的数字
var num = 0
// 事件监听
oBox.addEventListener('mousewheel',function(e){
e.deltaY > 0 ? num++ : num--
oNum.innerText = num
// 当有滚动条的时候,滚轮向下滚动时,页面上升,此时盒子会超出显示区域;需要阻止滚轮事件的默认行为
e.preventDefault()
})
</script>
</body>
</html>

📍e.stopPropagation()方法

e.stopPropagation()方法用来阻止事件继续传播(捕获阶段和冒泡阶段都可以阻止)

在一些场合,非常有必要切断事件继续传播,否则会造成页面特效显示出bug

💡小案例

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
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
<style>
* {
margin: 0;
padding: 0;
}
#mask {
position: absolute;
display: none;
top: 50%;
left: 50%;
margin-left: -300px;
margin-top: -150px;
width: 600px;
height: 300px;
background-color: rgba(0, 0, 0, 0.5);
}
</style>
</head>
<body>
<!-- 制作一个弹出层:点击按钮显示弹出层,点击网页任何地方,弹出层关闭 -->
<input type="button" value="显示弹出层" id="btn" />
<div id="mask"></div>
<script>
// 获取元素
var oMask = document.getElementById('mask');
var oBtn = document.getElementById('btn');
// 事件监听
oBtn.addEventListener('click', function (e) {
oMask.style.display = 'block';
// 阻止事件传播
e.stopPropagation();
});
document.addEventListener('click', function (e) {
oMask.style.display = 'none';
});
oMask.addEventListener('click', function (e) {
// 阻止事件传播
e.stopPropagation();
});
</script>
</body>
</html>

⭕事件委托

❗批量添加事件监听的性能问题

1.每一个事件监听注册都会消耗一定的系统内存,而批量添加事件会导致监听数量太多,内存消耗会非常大

2.实际上,每一个

  • 的事件处理函数都是不同的函数,这些函数本身也会占用内存。

  • ❗动态绑定事件的问题

    1.新增元素必须分别添加事件监听,不能自动获得事件监听

    2.大量事件监听、大量事件处理函数都会产生大量消耗内存

    📍事件委托:利用事件冒泡机制,将后代元素事件委托给祖先元素

    🔔e.target 和 e.currentTarget 属性

    属性 属性描述
    target 触发此事件的最早元素,即 “事件源元素”
    currentTarget 事件处理程序附加到的元素(作用和事件处理函数中的this功能类似)

    💡小案例:给列表项批量添加事件监听,并且点击按钮新增一个列表项,这个列表项也要绑定事件(点击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
    <!DOCTYPE html>
    <html lang="en">
    <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Document</title>
    <style>
    li {
    cursor: pointer;
    }
    </style>
    </head>
    <body>
    <!-- 小案例:给列表项批量添加事件监听,并且点击按钮新增一个列表项,这个列表项也要绑定事件(点击li文字变红)-->
    <button id="btn">点击新增列表项</button>
    <ul id="list">
    <li>列表项</li>
    <li>列表项</li>
    <li>列表项</li>
    </ul>
    <script>
    // 获取元素
    var oBtn = document.getElementById('btn');
    var oList = document.getElementById('list');
    // 事件监听
    oList.addEventListener('click', function (e) {
    e.target.style.color = 'red';
    });
    oBtn.addEventListener('click', function () {
    // 创建孤儿节点
    var oLi = document.createElement('li');
    // 添加内容
    oLi.innerText = '我是新创建的li';
    // 上树
    oList.appendChild(oLi);
    });
    </script>
    </body>
    </html>

    🔔事件委托的使用场景

    1.当有大量类似元素需要批量添加事件监听时,使用事件委托可以减少内存开销。

    2.当有动态元素节点上树时,使用事件委托可以让新上树的元素具有事件监听

    ❗使用事件委托时需要注意的事项

    1.onmouseenter 和 onmouseover 都表示 ”鼠标移入“,但是 onmouseenter 不冒泡(使用事件委托不生效),而 onmouseover 冒泡,所以不能委托不冒泡的事件给祖先元素

    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
    47
    48
    49
    50
    51
    52
    <!DOCTYPE html>
    <html lang="en">
    <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Document</title>
    <style>
    li {
    cursor: pointer;
    }
    </style>
    </head>
    <body>
    <!-- 页面上有一个记录待办事项的列表,在输入框中输入内容之后,点击添加按钮,-->
    <!-- 可以将输入的内容添加到列表里。如果事情完成了,点击此项可以从列表里移除 -->
    <input type="text" id="input" />
    <button id="btn">添加</button>
    <ul id="list"></ul>
    <script>
    // 获取元素
    var oInput = document.getElementById('input');
    var oBtn = document.getElementById('btn');
    var oList = document.getElementById('list');
    // 事件监听
    oBtn.addEventListener('click', function () {
    if (!oInput.value) return alert('内容不能为空');
    // 创建li
    var oLi = document.createElement('li');
    // 设置内容为input的value
    oLi.innerText = oInput.value;
    // 上树
    oList.appendChild(oLi);
    // 清空输入框的内容
    oInput.value = '';
    // 光标聚焦到输入框
    oInput.focus();
    });
    // 点击li删除li
    oList.addEventListener('click', function (e) {
    // 事件委托锁定到li,并删除
    oList.removeChild(e.target);
    });
    // 新增---在输入框按下回车键添加
    oInput.addEventListener('keypress', function (e) {
    if (e.charCode == 13) {
    oBtn.click();
    }
    });
    </script>
    </body>
    </html>

    ⭕定时器和延时器

    📍 定时器

    setInterval() 函数可以重复调用一个函数,在每次调用之间具有固定的时间间隔

    1
    2
    3
    4
    5
    6
    7
    8
    9
    // 具体语法
    setInterval( function () { // 第一个参数是函数
    //这个函数将自动被以固定间隔时间调用
    }, 1000); // 第二个参数是间隔时间,单位ms,1000ms=1s

    // setInterval() 函数可以接收第3、4......个参数,它们将按顺序传入函数
    setInterval( function (a, b) {
    // 形式参数a的值是76,形式参数b的值是24
    }, 2000 , 76, 24)

    💡具名函数也可以传入setInterval()

    1
    2
    3
    4
    5
    var a = 0
    function fun () {
    console.log(a++)
    }
    setInterval( fun, 2000) // 具名函数只写函数名即可,不能带括号!!!

    📍清除定时器

    clearInterval()函数可以清除一个定时器

    1
    2
    3
    4
    5
    6
    7
    8
    9
    // 具体语法
    // 设置定时器,并且用timer变量接收这个定时器
    var timer = setInterval( function () {
    }, 2000)

    // 点击按钮时,清除定时器
    oBtn.addEventListener('click', function() {
    clearInterval(timer)
    })

    💡小案例:计时器,从0开始,点击开始按钮每秒加1,点击暂停按钮停止加1

    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 name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Document</title>
    </head>
    <body>
    <!-- 计时器,从0开始,点击开始按钮每秒加1,点击暂停按钮停止加1 -->
    <p id="num">0</p>
    <button id="start">开始</button>
    <button id="stop">暂停</button>
    <script>
    // 获取元素
    var oNum = document.getElementById('num');
    var oStart = document.getElementById('start');
    var oStop = document.getElementById('stop');
    // 定义变量
    var a = 0;
    var timer;
    // 事件监听
    oStart.addEventListener('click', function () {
    // 避免快速点击开始按钮时,同时创建多个定时器;在创建定时器之前先清除定时器
    clearInterval(timer); // 当 timer = underfined 时,不会报错
    // 创建定时器
    timer = setInterval(function () {
    oNum.innerText = ++a;
    }, 1000);
    });
    // 点击暂停
    oStop.addEventListener('click', function () {
    // 清除定时器
    clearInterval(timer);
    });
    </script>
    </body>
    </html>

    📍延时器

    setTimeout() 函数可以设置一个延时器,当指定时间到了之后,会执行函数一次,不再重复执行。**

    1
    2
    3
    4
    // 具体语法
    setTimeout( function(){
    // 这个函数会在2s后执行一次
    }, 2000)

    📍清除延时器

    clearTimeout() 函数可以清除延时器,和clearInterval() 非常类似

    💡小案例:创建一个延时器,2s后显示文字,点击取消按钮则取消文字显示

    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
    <!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>
    <!-- 小案例:创建一个延时器,2s后显示文字,点击取消按钮则取消文字显示 -->
    <p id="text"></p>
    <button id="cancel">取消</button>
    <script>
    // 获取元素
    var oText = document.getElementById('text');
    var oCancel = document.getElementById('cancel');
    // 定义变量
    var timer;
    // 设置延时器
    timer = setTimeout(function () {
    oText.innerText = '我是2s后显示的文字!';
    }, 2000);
    // 事件监听
    oCancel.addEventListener('click', function () {
    console.log('取消文字显示');
    clearTimeout(timer);
    });
    </script>
    </body>
    </html>

    ⭕异步语句

    **异步:不会阻塞CPU继续执行其他语句,当异步完成时,会执行 “回调函数” ( callback) **

    setInterval() 函数 和 setTimeout() 函数都是异步语句

    ⭕使用定时器实现动画 —— 视觉暂留原理

    使用定时器实现动画较为不便:

    1. 不方便根据动画总时间计算步长
    2. 运动方向要设置正负
    3. 多种运动进行叠加较为困难(比如一个方形一边一移动一边变为圆形)
    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 name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Document</title>
    <style>
    #box {
    position: absolute;
    top: 100px;
    left: 100px;
    width: 200px;
    height: 200px;
    background-color: orange;
    }
    </style>
    </head>
    <body>
    <!-- 点击按钮盒子移动的动画,原理:视觉暂留 -->
    <button id="btn">开始移动</button>
    <div id="box"></div>
    <script>
    // 获取元素
    var oBtn = document.getElementById('btn');
    var oBox = document.getElementById('box');
    // 定义变量
    var l = 100;
    var timer;
    // 事件监听
    oBtn.addEventListener('click', function () {
    // 设置定时器
    timer = setInterval(function(){
    // 改变左边的距离
    l += 10
    if (l >= 1000) {
    clearInterval(timer)
    }
    // 设置属性
    oBox.style.left = l + 'px'
    },20)
    });
    </script>
    </body>
    </html>

    🔔JS和CSS3结合实现动画 —— 规避了定时器制作动画的缺点

    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
    <!DOCTYPE html>
    <html lang="en">
    <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Document</title>
    <style>
    #box {
    position: absolute;
    top: 100px;
    left: 100px;
    width: 200px;
    height: 200px;
    background-color: orange;
    }
    </style>
    </head>
    <body>
    <!-- JS和CSS3结合实现动画 -->
    <button id="btn">开始移动</button>
    <div id="box"></div>
    <script>
    // 获取元素
    var oBtn = document.getElementById('btn');
    var oBox = document.getElementById('box');
    // 设置全局变量
    var i = true; // 控制运动的方向
    var lock = true; // 函数节流锁
    // 添加事件监听,再次点击改变运动方向
    oBtn.addEventListener('click', function () {
    // 首先检查锁是否是关闭的
    if (!lock) return;

    // 给盒子添加过渡属性
    oBox.style.transition = 'all 2s linear 0s';
    if (i) {
    // 设置盒子的重点位置
    oBox.style.left = '1100px';
    // 改变方向
    i = !i;
    } else {
    // 设置盒子的重点位置
    oBox.style.left = '100px';
    // 改变方向
    i = !i;
    }

    // 动画结束,上锁
    lock = false;

    // 设置延时器,2s后解锁
    setTimeout(function () {
    // 开锁
    lock = true;
    }, 2000);
    });
    </script>
    </body>
    </html>

    💡函数节流

    函数节流:一个函数执行一次后,只有大于设定的执行周期后才允许执行第二次

    函数节流非常容易实现,只需要借助setTimeout()延时器

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    // 展示语法

    // 定义全局变量 --- 作为函数节流锁
    var lock = true; // 默认为开锁状态

    // 需要节流的函数
    function 需要节流的函数(){
    // 检查锁的状态,如果关闭,则不执行后面的操作
    if (!lock) return;
    // 函数核心语句

    // 关锁
    lock = false;

    // 指定毫秒数后将锁打开
    setTimeout(function () {
    lock = true;
    }, 2000);
    }

    ⭕动画效果开发

    💡无缝连续滚动特效

    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
    <!DOCTYPE html>
    <html lang="en">
    <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Document</title>
    <style>
    * {
    margin: 0;
    padding: 0;
    }
    #box {
    position: relative;
    margin: 0 auto;
    margin-top: 50px;
    height: 100px;
    width: 640px;
    border: 1px solid #000;
    overflow: hidden;
    }
    #box ul {
    position: absolute;
    left: 0;
    width: 2000px;
    }
    #box ul li {
    list-style: none;
    float: left;
    height: 100px;
    width: 150px;
    margin-right: 10px;
    background-color: orange;
    text-align: center;
    line-height: 100px;
    color: white;
    font-size: 100px;
    }
    #box ul li.first {
    background-color: orange;
    }
    #box ul li.second {
    background-color: red;
    }
    #box ul li.third {
    background-color: green;
    }
    #box ul li.fourth {
    background-color: pink;
    }
    #box ul li.fiveth {
    background-color: blue;
    }
    </style>
    </head>
    <body>
    <!-- 无缝连续滚动特效,鼠标移入暂停滚动,移出继续滚动 -->
    <!-- 原理:其实有两份li元素,当滚动完成第一份li的结尾后,ul的left重置为0,重新向左移动 -->
    <div id="box">
    <ul id="ul">
    <li class="first">1</li>
    <li class="second">2</li>
    <li class="third">3</li>
    <li class="fourth">4</li>
    <li class="fiveth">5</li>
    </ul>
    </div>
    <script>
    // 获取元素
    var list = document.getElementById('ul');
    // 复制一份li
    list.innerHTML += list.innerHTML;

    // 全局变量
    var left = 0;
    var timer;

    // 封装定时器动画
    function move() {
    // 设表先关,防止动画积累
    clearInterval(timer);
    timer = setInterval(function () {
    left -= 10;
    if (left <= -790) {
    left = 0;
    }
    list.style.left = left + 'px';
    }, 20);
    }
    // 滚动效果
    move();
    // 事件监听
    list.addEventListener('mouseenter', function () {
    clearInterval(timer);
    });
    list.addEventListener('mouseleave', function () {
    move();
    });
    </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 name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Document</title>
    <style>
    * {
    margin: 0;
    padding: 0;
    }
    #box {
    position: relative;
    margin: 0 auto;
    margin-top: 50px;
    height: 400px;
    width: 640px;
    border: 1px solid #000;
    overflow: hidden;
    }
    #box ul {
    position: absolute;
    left: 0;
    width: 5000px;
    }
    #box ul li {
    list-style: none;
    float: left;
    height: 400px;
    width: 640px;
    margin-right: 10px;
    background-color: orange;
    text-align: center;
    line-height: 400px;
    color: white;
    font-size: 100px;
    }
    #box ul li.first {
    background-color: orange;
    }
    #box ul li.second {
    background-color: red;
    }
    #box ul li.third {
    background-color: green;
    }
    #box ul li.fourth {
    background-color: pink;
    }
    #box ul li.fiveth {
    background-color: blue;
    }
    #leftBtn,#rightBtn {
    position: absolute;
    top: 50%;
    margin-top: -25px;
    border-radius: 50%;
    width: 50px;
    height: 50px;
    background-color: rgba(0, 0, 0, 0.5);
    color: white;
    line-height: 50px;
    text-align: center;
    text-decoration: none;
    font-size: 30px;
    }
    #leftBtn {
    left: 20px;
    }
    #rightBtn {
    right: 20px;
    }
    </style>
    </head>
    <body>
    <!-- 跑马灯轮播特效 -->
    <div id="box">
    <ul id="ul">
    <li class="first">1</li>
    <li class="second">2</li>
    <li class="third">3</li>
    <li class="fourth">4</li>
    <li class="fiveth">5</li>
    </ul>
    <a href="javascript:;" id="leftBtn"><</a>
    <a href="javascript:;" id="rightBtn">></a>
    </div>
    <script>
    // 获取元素
    var list = document.getElementById('ul');
    var leftBtn = document.getElementById('leftBtn');
    var rightBtn = document.getElementById('rightBtn');
    // 全局变量
    var idx = 0; // 第几张图
    var lock = true; //节流锁
    // 添加过渡
    list.style.transition = 'all .5s ease 0s';
    // 复制第一张图到最后
    var cloneLi = list.firstElementChild.cloneNode(true);
    list.appendChild(cloneLi);
    // 事件监听
    rightBtn.addEventListener('click', function () {
    // 判断是否上锁
    if (!lock) return;
    // 上锁
    lock = false;

    list.style.transition = 'all .5s ease 0s';
    idx++;
    list.style.left = idx * -650 + 'px';
    // 当到最后一张时
    if (idx == 5) {
    // 设置延时器,.5s后切到第一张
    setTimeout(function () {
    // 取消过渡的效果
    list.style.transition = 'none';
    list.style.left = 0;
    idx = 0;
    }, 500);
    }
    // 解锁
    setTimeout(function () {
    lock = true;
    }, 500);
    });

    leftBtn.addEventListener('click', function () {
    // 判断是否上锁
    if (!lock) return;
    // 上锁
    lock = false;

    idx--;
    list.style.left = idx * -650 + 'px';

    // 当到第一张后再点击左按钮
    if (idx == -1) {
    // 取消过渡的效果
    list.style.transition = 'none';
    idx = 5;
    list.style.left = idx * -650 + 'px';
    setTimeout(function () {
    list.style.transition = 'all .5s ease 0s';
    idx--;
    list.style.left = idx * -650 + 'px';
    }, 0);
    }

    // 解锁
    setTimeout(function () {
    lock = true;
    }, 500);
    });
    </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
    <!DOCTYPE html>
    <html lang="en">
    <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Document</title>
    <style>
    * {
    margin: 0;
    padding: 0;
    }
    #box {
    position: relative;
    margin: 0 auto;
    margin-top: 50px;
    height: 400px;
    width: 640px;
    border: 1px solid #000;
    /* overflow: hidden; */
    }
    #box ul {
    position: relative;
    }
    #box ul li {
    position: absolute;
    list-style: none;
    height: 400px;
    width: 640px;
    text-align: center;
    line-height: 400px;
    color: white;
    font-size: 100px;
    opacity: 0;
    transition: all 1s ease 0s;
    }
    #box ul li:first-child{
    opacity: 1;
    }
    #box ul li.first {
    background-color: orange;
    }
    #box ul li.second {
    background-color: red;
    }
    #box ul li.third {
    background-color: green;
    }
    #box ul li.fourth {
    background-color: pink;
    }
    #box ul li.fiveth {
    background-color: blue;
    }
    #leftBtn,#rightBtn {
    position: absolute;
    top: 50%;
    margin-top: -25px;
    border-radius: 50%;
    width: 50px;
    height: 50px;
    background-color: rgba(0, 0, 0, 0.5);
    color: white;
    line-height: 50px;
    text-align: center;
    text-decoration: none;
    font-size: 30px;
    }
    #leftBtn {
    left: 20px;
    }
    #rightBtn {
    right: 20px;
    }
    </style>
    </head>
    <body>
    <!-- 跑马灯轮播特效 -->
    <div id="box">
    <ul id="ul">
    <li class="first">1</li>
    <li class="second">2</li>
    <li class="third">3</li>
    <li class="fourth">4</li>
    <li class="fiveth">5</li>
    </ul>
    <a href="javascript:;" id="leftBtn"><</a>
    <a href="javascript:;" id="rightBtn">></a>
    </div>
    <script>
    // 获取元素
    var list = document.getElementById('ul');
    var leftBtn = document.getElementById('leftBtn');
    var rightBtn = document.getElementById('rightBtn');
    // 全局变量
    var idx = 0; // 第几张图
    var lock = true; //节流锁
    var arr = list.getElementsByTagName('li')
    // 事件监听
    rightBtn.addEventListener('click', function(){
    arr[idx].style.opacity = 0
    idx++
    if (idx == 5) idx = 0
    arr[idx].style.opacity = 1
    })
    leftBtn.addEventListener('click', function(){
    arr[idx].style.opacity = 0
    idx--
    if (idx == -1) idx = 4
    arr[idx].style.opacity = 1
    })

    </script>
    </body>
    </html>