DOM学习笔记
馨er BOSS

DOM coderwhy老师笔记

BOM:浏览器对象模型(Broswer Object Model);DOM:文档对象模型(Document Object Model)

document对象是DOM的顶级对象,严格意义上来说,window是包含document的,通过DOM操作元素

1 BOM和DOM的关系

1
2
3
4
5
6
7
8
9
10
window.onload = function(){
console.log(window.document)
console.log(document)
// window对象是BOM的顶级老大
// document对象是DOM的顶级老大
// 实际上完整的写法是: window.document, 只不过window. 可以省略.
// var divObj = window.document.getElementById("div1")
var divObj = document.getElementById("div1")
console.log("========>" + divObj)
}

2 document对象

1
2
3
4
5
6
7
8
console.log(document.documentElement) // 对应的是html元素
console.log(document.doctype) // 文档声明:<!DOCTYPE html>
// title
document.title = "Coderwhy"

// body/head
console.log(document.body) // 对应body元素
console.log(document.head) // 对应head元素

3 导航

获取所有节点的导航:包括空行(会被解析成文本)、注释,文本

获取所有元素的导航:只包括元素,如<div><p>(常用)

3.1 节点之间的导航

  • 父节点:parentNode
  • 前兄弟节点:previousSibling
  • 后兄弟节点:nextSibling
  • 子节点:childNodes
  • 第一个子节点:firstChild
  • 第二个子节点:lastChild
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>
</head>
<body>

<!-- 我是注释, 哈哈哈 -->
我是文本, 呵呵呵

<div class="box">哈哈哈哈哈</div>
<ul>
<li>1</li>
<li>2</li>
<li>3</li>
<li>4</li>
<li>5</li>
</ul>
<script>

// var htmlEl = document.documentElement
// var bodyEl = document.body
// var headEl = document.head
// var doctype = document.doctype
// console.log(htmlEl, bodyEl, headEl, doctype)

// 1.获取节点的导航
var bodyEl = document.body
// 1.1.获取body所有的子节点
// console.log(bodyEl.childNodes)
// 1.2.获取body的第一个子节点
var bodyElFirstChild = bodyEl.firstChild
// 1.3.获取body中的注释
var bodyElCommentChild = bodyElFirstChild.nextSibling
console.log(bodyElCommentChild)
// 1.4.获取body的父节点
var bodyParent = bodyEl.parentNode
console.log(bodyParent)
</script>

</body>
</html>

3.2 元素之间的导航

  • 父元素:parentElement
  • 前兄弟节点:previousElementSibling
  • 后兄弟节点:nextElementSibling
  • 子节点:children
  • 第一个子节点:firstElementChild
  • 第二个节点:lastElementChild
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>
</head>
<body>

<!-- 我是注释, 哈哈哈 -->
我是文本, 呵呵呵

<div class="box">哈哈哈哈哈</div>
<ul>
<li>1</li>
<li>2</li>
<li>3</li>
<li>4</li>
<li>5</li>
</ul>

<script>
var bodyEl = document.body
// 根据body元素去获取子元素(element)
var childElements = bodyEl.children
console.log(childElements)

// 获取box元素
var boxEl1 = bodyEl.firstElementChild
var boxEl2 = bodyEl.children[0]
console.log(boxEl1, boxEl2, boxEl1 === boxEl2)

// 获取ul元素
var ulEl = boxEl1.nextElementSibling
console.log(ulEl)

// 获取li元素
var liEls = ulEl.children
console.log(liEls)

</script>

</body>
</html>

3.3 表格元素的导航

table元素支持以下这些属性:

  • table.rows——<tr>元素的集合
  • table.caption/tHead/tFoot——引用元素<caption>threadtfoot
  • table.tBodies——<tbody>元素的集合

<thead><tfoot><tbody>元素提供了rows属性:

  • tbody.rows——表格内部<tr>元素的集合

<tr>

  • tr.cells——在给定<tr>中的<td><th>单元格的集合
  • tr.sectionRowIndex——给定的 <tr> 在封闭的 <thead>/<tbody>/<tfoot> 中的位置(索引)
  • tr.rowIndex —— 在整个表格中 <tr> 的编号(包括表格的所有行)

<td><th>

  • td.cellIndex——在封闭的<tr>中单元格的编号
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
<!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>

<!-- 高级元素: table/form -->
<table>
<thead>
<tr>
<th>姓名</th>
<th>年龄</th>
<th>身高</th>
</tr>
</thead>
<tbody>
<tr>
<td>why</td>
<td>18</td>
<td>1.88</td>
</tr>
<tr>
<td>kobe</td>
<td>30</td>
<td>1.98</td>
</tr>
</tbody>
</table>

<script>

var tableEl = document.body.firstElementChild

// 通过table元素获取内部的后代元素
// console.log(tableEl.tHead, tableEl.tBodies, tableEl.tFoot)
// console.log(tableEl.rows)

// 拿到一行元素
// var rowEl = tableEl.rows[2]
// console.log(rowEl.cells[0])
// console.log(rowEl.sectionRowIndex)
// console.log(rowEl.rowIndex)

</script>

</body>
</html>

3.4 表单元素的导航

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>
</head>
<body>

<form action="">
<input name="account" type="text">
<input name="password" type="password">
<input name="hobbies" type="checkbox" checked>
<select name="fruits">
<option value="apple">苹果</option>
<option value="orange">橘子</option>
</select>
</form>

<script>

// 1.获取form
// var formEl = document.body.firstElementChild
var formEl = document.forms[0]

// 2.获取form中的子元素
var inputEl = formEl.elements.account
setTimeout(function() {
console.log(inputEl.value)
}, 5000)

</script>

</body>
</html>

4 获取元素的方法

image-20221029014115367

目前最常用的是querySelector和querySelectAll

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
<!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>
</style>
</head>
<body>
<div class="box">
<h2>我是标题</h2>
<div class="container">
<p>
我是段落, <span class="keyword">coderwhy</span> 哈哈哈哈
</p>
<p>
我也是段落, <span class="keyword">kobe</span> 呵呵呵呵额
</p>

<div class="article">
<h3 id="title">我是小标题</h3>
<p>
我是文章的内容, 嘿嘿嘿嘿嘿
</p>
</div>
</div>
</div>

<script>

// 一. 通过导航获取
// // 1.拿到body
// var bodyEl = document.body

// // 2.拿到box
// var boxEl = bodyEl.firstElementChild

// // 3.拿container
// var containerEl = boxEl.children[1]

// // 4.拿p
// var pEl = containerEl.children[0]

// // 5.拿到keyword
// var spanEl = pEl.children[0]
// spanEl.style.color = "red"

// 二. getElementBy*
// 1.通过className获取元素
// var keywordEls = document.getElementsByClassName("keyword")
// // 修改第一个
// // keywordEls[0].style.color = "red"
// // 修改所有的
// for (var i = 0; i < keywordEls.length; i++) {
// keywordEls[i].style.color = "red"
// }

// 2. 通过id获取元素
// var titleEl = document.getElementById("title")
// titleEl.style.color = "orange"


// 三.querySelector: 通过选择器查询
var keywordEl = document.querySelector(".keyword")
// keywordEls是对象, 可迭代的
// 可迭代对象: String/数组/节点的列表
var keywordEls = document.querySelectorAll(".keyword")
for (var el of keywordEls) {
el.style.color = "red"
}
console.log(keywordEls)

var titleEl = document.querySelector("#title")
titleEl.style.color = "orange"

</script>

</body>
</html>

5 节点的属性

5.1 nodeType

获取节点类型

常见的节点类型如下:

  • Node.ELEMENT_NODE——1——元素节点,例如<p><div>
  • Node.TEXT_NODE——3——Element或者Attr中实际的文字
  • Node.COMMENT_NODE——8——一个Comment节点,例如注释
  • Node.DOCUMENT_NODE——9——一个Document节点
  • Node.DOCUMENT_TYPE_NODE——10——描述文档类型的DocumentType节点,例如<!DOCTYPE html>

5.2 nodeName、tagName

nodeName:获取node节点的名字(针对节点)

tagName:获取元素的标签名词(针对元素)

5.3 data、innerHTML、textContent

  • data:针对非元素节点获取数据,同nodeValue
  • innerHTML:获取标签内部的所有HTML
  • textContent:只获取标签内部的数据
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
<!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 class="box">
<h2>我是标题</h2>
<p>我是内容, 哈哈哈哈</p>
</div>

<script>
// 1.获取三个节点
var bodyChildNodes = document.body.childNodes
var commentNode = bodyChildNodes[1]
var textNode = bodyChildNodes[2]
var divNode = bodyChildNodes[3]

// 2.节点属性
// 2.1.nodeType 节点的类型
for (var node of bodyChildNodes) {
if (node.nodeType === 8) {
} else if (node.nodeType === 3) {
} else if (node.nodeType === 1) {
}
}
console.log(commentNode.nodeType, textNode.nodeType, divNode.nodeType) // 8 3 1
console.log(Node.COMMENT_NODE)

// 2.2.nodeName 节点的名称
// tagName: 针对元素(element)
console.log(commentNode.nodeName, textNode.nodeName, divNode.nodeName) // #comment #text #DIV
console.log(commentNode.tagName, divNode.tagName) // undefined 'DIV'

// 2.3. data(nodeValue)/innerHTML/textContent
// data针对非元素的节点获取数据
// innerHTML: 对应的html元素也会获取
// textContent: 只会获取文本内容
// console.log(commentNode.data, textNode.data, divNode.data)
// console.log(divNode.innerHTML)
// console.log(divNode.textContent)

// 设置文本, 作用是一样
// 设置文本中包含元素内容, 那么innerHTML浏览器会解析, textContent会当成文本的一部分
// divNode.innerHTML = "<h2>呵呵呵呵</h2>"
// divNode.textContent = "<h2>嘿嘿嘿嘿</h2>"

// 2.4.outerHTML
console.log(divNode.outerHTML) // 相对于innerHTML来说加上了元素本身

</script>

</body>
</html>

5.4 hidden

DOM设置display: none

6 元素的属性和特性

attribute:元素中的属性称为attribute

property:对象中的属性称为property

6.1 attribute的分类

  • 标准的attribute:某些attribute属性是标准的,如id、class、hres、type、value等
  • 非标准的attribute:某些attribute是自定义的,如abc、age、height等

6.2 attribute的操作

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>
</head>
<body>

<div id="abc" class="box" title="box"
age="18" height="1.88">
我是box
</div>

<input type="checkbox" checked="checked">

<script>

var boxEl = document.querySelector(".box")

// 1.所有的attribute都支持的操作
console.log(boxEl.hasAttribute("AGE"), boxEl.hasAttribute("abc"), boxEl.hasAttribute("id"))
console.log(boxEl.getAttribute("AGE"), boxEl.getAttribute("abc"), boxEl.getAttribute("id"))

boxEl.setAttribute("id", "cba")
boxEl.removeAttribute("id")

var boxAttributes = boxEl.attributes
// 获取所有属性
for (var attr of boxAttributes) {
console.log(attr.name, attr.value)
}

// 2.通过getAttribute()一定是字符串类型
var inputEl = document.querySelector("input")
console.log(inputEl.getAttribute("checked")) // 取不到,为空

</script>

</body>
</html>
  • 名字大小写不敏感
  • 值为字符串类型

6.3 property

对于标准的attribute,会在DOM对象上创建与其对应的property属性,非标准的attribute,只能通过getAttribute来获取

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
<!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>

<!-- 元素中的属性称之为attribute -->
<!-- 标准的attribute在对应的对象模型中都有对应的property -->
<div id="abc" class="box" title="标题"
age="18" height="1.88">
我是box
</div>

<input type="checkbox" checked>

账号: <input class="account" type="text">
<button class="btn">设置input的值</button>

<script>

// 对象中的属性称之为property
// var obj = {
// // property
// name: "why"
// }

// 1.通过property获取attribute的值
// 获取box元素
var boxEl = document.querySelector(".box")
console.log(boxEl.id, boxEl.title, boxEl.age, boxEl.height)

// input元素
var inputEl = document.querySelector("input")
// if (inputEl.getAttribute("checked")) {
// console.log("checkbox处于选中状态")
// }
if (inputEl.checked) {
console.log("checkbox处于选中状态")
}
console.log(typeof inputEl.checked)

// 2.attribute和property是相互影响的
boxEl.id = "aaaaa"
console.log(boxEl.getAttribute("id"))

boxEl.setAttribute("title", "哈哈哈")
console.log(boxEl.title)

// 3.比较特殊的情况, input设置值(了解)
var accountInputEl = document.querySelector(".account")
var btnEl = document.querySelector(".btn")
btnEl.onclick = function() {
accountInputEl.setAttribute("value", "kobe")
// 优先级更高
accountInputEl.value = "coderwhy"
}

</script>

</body>
</html>

7 className和style

7.1 className和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
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>
.active {
color: red;
font-size: 24px;
background-color: green;
}
</style>
</head>
<body>

<div class="box">
我是box
</div>

<script>

// 1.获取boxEl
var boxEl = document.querySelector(".box")

// 2.监听点击
var counter = 1
boxEl.onclick = function() {
// 1.直接修改style
// boxEl.style.color = "red"
// boxEl.style.fontSize = "24px"
// boxEl.style.backgroundColor = "green"

// 2.动态的添加某一个class
boxEl.className = "active"

// 3.动态的修改boxEl的宽度
boxEl.style.width = 100 * counter + "px"
counter++
}

</script>

</body>
</html>

7.2 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
42
43
44
45
46
47
48
49
50
51
52
53
54
55
<!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>
.active {
color: #fff;
background-color: #f80;
font-size: 25px;
}
</style>
</head>
<body>

<div class="box">
我是box
</div>
<button class="btn">切换</button>

<script>

var boxEl = document.querySelector(".box")

// 1.方法一: className
// boxEl.className = "abc"

// var obj = {}
// obj.name = "abc"
// obj.class = "hahahaha"
// obj.var = 123
// console.log(obj)

// 2.方法二: classList操作class
boxEl.classList.add("abc")
boxEl.classList.add("active")
boxEl.classList.remove("abc")

// 需求: box在active之间切换
var btnEl = document.querySelector(".btn")
btnEl.onclick = function() {
// if (boxEl.classList.contains("active")) {
// boxEl.classList.remove("active")
// } else {
// boxEl.classList.add("active")
// }
boxEl.classList.toggle("active")
}

</script>

</body>
</html>

7.3 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
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>
</head>
<body>

<div class="box" style="background-color: aqua; color: white;">
我是box
</div>

<script>
var boxEl = document.querySelector(".box")

// 1.在property中使用的驼峰格式
// console.log(boxEl.style.backgroundColor)

// 2.如果将一个属性的值, 设置为空的字符串, 那么是使用默认值
// boxEl.style.display = ""
// boxEl.style.fontSize = ""

// 3.设置多个样式
// boxEl.style.fontSize = "30px"
// boxEl.style.color = "red"
boxEl.style.cssText = "font-size: 30px; color: red;"

// 4.读取
// console.log(boxEl.style.backgroundColor) // 为空
console.log(getComputedStyle(boxEl).fontSize)

</script>

</body>
</html>

7.4 data-*自定义属性使用方法

使用data-*自定义属性的方法,可以使用dataset属性获取

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
<!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 id="abc" class="box"
data-age="18" data-height="1.88"></div>

<script>
var boxEl = document.querySelector(".box")
// 小程序开发中使用
console.log(boxEl.dataset.age)
console.log(boxEl.dataset.height)
</script>

</body>
</html>

8 元素操作

8.1 创建元素

1
2
3
4
var boxEl = document.querySelector(".box")
var h2El = document.createElement("h2")
h2El.classList.add("title")
boxEl.append(h2El)

8.2 插入元素

1
2
3
4
5
node.append(...nodes or strings) // 在node末尾插入节点或字符串(可以添加多个)
node.prepend(...nodes or strings) // 在node开头插入节点或字符串(可以添加多个)
node.before(...nodes or strings) // 在node前面插入节点或字符串(可以添加多个)
node.after(...nodes or strings) // 在node后面插入节点或字符串(可以添加多个)
node.replaceWith(...nodes or strings) // 将node替换为给定的节点或字符串(可以添加多个)

8.3 移除元素

1
h2El.remove()

8.4 克隆元素

1
2
3
4
5
6
7
// 参数加true为深度克隆(克隆子元素)
var cloneBoxEl = boxEl.cloneNode(true)

// 方法一
document.body.append(cloneBoxEl)
// 方法二
boxEl.after(cloneBoxEl)

8.5 旧的元素操作方法(了解即可)

1
2
3
4
5
6
7
8
// 在parentElem的父元素最后位置添加一个子元素
parentElem.appendChild(node)
// 在parentElem的nextSibling前面插入一个子元素
parentElem.appendChild(node, nextSibling)
// 在parentElem中,新元素替换之前的oldChild元素
parentElem.replaceChild(node, oldChild)
// 在parentElem中,移除某一个元素
parentElem.removeChild(node)

8.6 元素的大小、滚动

  • clientWidth:contentWith+padding(不包含滚动条)
  • clientHeight:contentHeight+padding
  • clientTop:border-top的宽度
  • clientLeft:border-left的宽度
  • offsetWidth:元素完整的宽度
  • offsetHeight:元素完整的高度
  • scrollHeight:整个可滚动的区域高度
  • scrollTop:滚动部分的高度

image-20221030231254936

8.7 window的大小、滚动

innerWidth、innerHeight:获取window窗口的宽度和高度(包含滚动条)

outerWidth、outerHeight:获取window窗口的整个宽度和高度(包括测试工具,工具栏)(即整个浏览器的)

documentElement.clientHeight、documentElement.clientWidth:获取html的宽度和高度(不包含滚动条)

scrollX:X轴滚动的位置(别名pageXOffset)

scrollY:Y轴滚动的位置(别名pageYOffset)

scrollBy(x, y):将页面滚动于当前位置的(x, y)位置

scrollBy(0, 100),调用此事件会一直向下移动100,执行此事件一直有效果(相对位置)

scrollTo(pageX, pageY):将页面滚动至绝对坐标

scrollTo(0, 100),调用此事件会到达绝对位置(0, 100),执行此事件只有一次效果(绝对位置)

应用:可以利用上述函数实现回到顶部按钮(点击回到顶部,且只有位于不在顶部的位置时才会显示回到更多按钮)

9 事件

9.1 事件监听

事件监听方式一:在script中直接监听(很少使用)

事件监听方式二:DOM属性,通过元素的on来监听事件

事件监听方式三:通过EventTarget中的addEventListener来监听

1
2
3
4
5
6
7
8
9
<div id="box" onclick="alert('box点击')">我是box</div>
<script>
box.onclick = function() {
alert("box点击2")
}
box.addEventListener("click", function() {
console.log("box点击3")
})
</script>

9.2 常见的事件列表

1、鼠标事件

click——当鼠标点击一个元素时(触摸屏设备会在点击时生成)

mouseover/mouseout——当鼠标指针移入/离开一个元素时

mousedown/mouseup——当在元素上按下/释放鼠标按钮时

mousemove——当鼠标移动时

2、键盘事件

keydown/keyup——当按下或松开一个按键时

3、表单元素事件

submit——当访问者提交了一个<form>

focus——当访问者聚焦于一个元素时,例如聚焦于一个<input>

4、Document事件

DOMContentLoaded——当HTML的加载和处理均完成,DOM被完全构建完成时

5、CSS事件

transitioned——当一个CSS动画完成时

9.3 事件冒泡和事件捕获

我们在浏览器上对着一个元素点击时,点击的不仅仅是这个元素本身,包括它的父元素,故有事件流的存在

默认情况下事件是从最内层的span向外依次传递的顺序,即事件冒泡。通常我们使用事件冒泡

从外层到内层,这种称之为事件捕获

产生两种不同处理流的原因:

早期浏览器开发时,IE和Netscape公司都发现了这个问题,但是他们采用了完全相反的事件流来对事件进行了传递:IE采用了事件冒泡的方式,Netscape采用了事件捕获的方式

过程:

  • 捕获阶段:事件向下走近元素
  • 目标阶段:事件到达目标元素
  • 冒泡阶段:事件从元素上开始冒泡

如果同时有事件冒泡和事件捕获的监听,那么会优先监听到事件捕获

9.4 事件对象

当一个事件发生时,就会有和这个事件相关的很多信息,比如事件的类型是什么,点击的是哪一个元素,点击的位置是哪里等等相关信息,这些信息会被封装到一个Event对象中,这个对象由浏览器创建,称之为event对象。该对象给我们提供了想要的一些属性,以及可以通过该对象进行某些操作

1
2
3
4
5
6
7
// 会传过一个默认参数,即事件对象
spanEl.onclick = function(event) {
console.log("事件对象:", event)
}
spanEl.addEventListener("click", function(event) {
console.log("事件对象:", event)
})

1、event常见的属性

type:事件的类型

target:当前事件发生的元素

currentTarget:当前处理事件的元素

eventPhase:事件所处的阶段

offsetX、offsetY:事件发生在元素内的位置

clientX、clientY:事件发生在客户端内的位置

pageX、pageY:事件发生在客户端相对于document的位置

screenX、screenY:事件发生相对于屏幕的位置

2、event常见的方法

preventDefault:取消事件的默认行为

stopPropagation:阻止事件的进一步传递(冒泡或者捕获都可以阻止)

3、事件处理中的this

1
2
3
4
// 在函数中,我们也可以通过this来获取当前的发生元素
boxEl.addEventListener("click", function(event) {
console.log(this === event.target) // true
})

9.5 EventTarget使用

所有的节点、元素都继承自EventTarget,事实上window也继承自EventTarget。EventTarget是一个DOM接口,主要用于添加、删除、派发Event事件

常见的方法:

  • addEventListener:注册某个事件类型以及事件处理函数
  • removeEventListener:移除某个事件类型以及事件处理函数
  • dispatchEvent:派发某个事件类型到EventTarget上
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
const scrollHandler = () => {
console.log("window发生了滚动~")
}
const clickHandler = () => {
console.log("window发生了点击~")
}

window.addEventListener("scroll", scrollHandler)
window.addEventListener("click", clickHandler)

const removeBtn = document.querySelector("#removeEvent")
removeBtn.onclick = function () {
console.log("-------")
window.removeEventListener("click", scrollHandler)
window.removeEventListener("scroll", clickHandler())
}

const dispatchBtn = document.querySelector("dispatch")
dispatchBtn.onclick = function () {
window.dispatchEvent(new Event("coderway"))
}

window.addEventListener("coderwhy", () => {
console.log("监听了coderwhy事件")
})

默认事件监听https://developer.mozilla.org/zh-CN/docs/Web/Events

9.6 事件委托模式

事件冒泡在某种情况下可以帮助我们实现强大的事件处理模式 – 事件委托模式(也是一种设计模式)

1

事件委托的标记

9.7 常见的事件

1、常见的鼠标事件

image-20221101030433834

mouseover和mouseenter的区别:

mouseenter和mouseleave

  • 不支持冒泡

  • 进入子元素依然属于在该元素内,没有任何反应

mouseover和mouseout

  • 支持冒泡

  • 进入元素的子元素时

    • 先调用父元素的mouseout
    • 再调用子元素的mouseover
    • 因为支持冒泡,所以会将mouseover传递到父元素中;

2、常见的键盘事件

image-20221101030453500

down事件先发生,press发生在文本被输入,up发生在文本输入完成

可以通过key和code来区分按下的键

  • code:“按键代码”(”KeyA”,”ArrowLeft” 等),特定于键盘上按键的物理位置。
  • key:字符(”A”,”a” 等),对于非字符(non-character)的按键,通常具有与 code 相同的值。)

3、常见的表单事件

image-20221101030506931

4、文档加载事件

  • DOMContentLoaded:浏览器已完全加载 HTML,并构建了 DOM 树,但像 <img> 和样式表之类的外部资源可能尚未加载完成。

  • load:浏览器不仅加载完成了 HTML,还加载完成了所有外部资源:图片,样式等

1
2


10 定时器

10.1 setTimeout

允许我们将函数推迟到一段时间间隔之后再执行

clearTimeout:取消setTimeout的定时器

1
2
3
4
5
6
7
8
// 参数一:想要执行的函数或代码字符串
// 参数二:执行前的延时,以ms为单位,默认为0
// 参数三:要传入被执行函数的参数列表
var timerID = setTimeout(function(name, age) {
console.log("定时器", name, age)
}, 2000, "why", 18)

clearTimeout(timerID)

10.2 setInterval

允许我们重复执行一个函数,从一段时间间隔之后开始运行,之后以该时间间隔连续重复运行该函数

clearInterval:取消setInterval的定时器

1
2
3
4
5
6
// 参数含义同上
var timerID = setInterval(function() {
console.log("定时器", name, age)
}, 2000, "why", 18)

clearInterval(timerID)

11 应用

11.1 捕捉回车键

1
document.getElementById()
1
2
3
4
5
6
7
8
9
10
11
12
13
document.getElementById("username").onkeydown = function(y){
// y代表的就是一个keydown事件对象.
// console.log("keydown.....")
// 在这里捕捉键值,当用户敲回车键了,则登录
// 新知识点:所有的“键盘事件对象”,有keyCode属性,这个keyCode属性可以获取键值.
// keyCode是键盘事件对象的属性.
// 记住:键盘上回车键的键值永远都是13.ECS键的键值永远都是27.
if(y.keyCode == 13) {
console.log("登录,正在进行身份认证,请稍后...");
}else if(y.keyCode == 27){
console.log("系统安全退出了!")
}
}

11.2 操作div和span

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
<script type="text/javascript">
window.onload = function(){
document.getElementById("btn").onclick = function(){
// 设置div中的内容
var divElt = document.getElementById("div1");
// 通过元素的innerHTML属性来设置内部的内容
// innerHTML 是属性,不是一个方法.
// innerHTML属性会将后面的字符串当做一段HTML代码解释并执行!
divElt.innerHTML = "<font color='red'>用户名不能为空!</font>";

// innerText也可以设置元素当中的内容.和innerHTML有什么区别呢?
// innerText后面的字符串即使是一个HTML代码,也不会当做HTML执行,只是看做普通文本.
//divElt.innerText = "<font color='red'>用户名不能为空!</font>";
}

var spanbtn = document.getElementById("spanbtn");
spanbtn.onclick = function(){
var span1 = document.getElementById("span1");
//span1.innerHTML = "<a href='http://www.baidu.com'>百度</a>";
span1.innerText = "<a href='http://www.baidu.com'>百度</a>";
}

}
</script>

<input type="button" id="btn" value="设置div中的内容"/>
<input type="button" id="spanbtn" value="设置span中的内容"/>

<!-- div独占一行! -->
<div id="div1"></div>

<!-- span的大小会随着span中的内容多少变化而变化。 -->
<span id="span1"></span>

11.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
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
<script type="text/javascript">
// 页面加载完毕之后
window.onload = function(){
/* // 给id = "firstChk"元素绑定click
var firstChkElt = document.getElementById("firstChk");
firstChkElt.onclick = function(){
// 获取到所有的复选框对象
var aihaos = document.getElementsByName("aihao");

if(firstChkElt.checked){ //get复选框的选中状态
// 遍历数组
for(var i = 0; i < aihaos.length; i++){
var aihaoChk = aihaos[i];
aihaoChk.checked = true; //set复选框的选中状态
}
}else{
// 遍历数组
for(var i = 0; i < aihaos.length; i++){
var aihaoChk = aihaos[i];
aihaoChk.checked = false;//set复选框的选中状态
}
}
} */

// 改良代码
var firstChkElt = document.getElementById("firstChk");
var aihaos = document.getElementsByName("aihao");
firstChkElt.onclick = function(){
for(var i = 0; i < aihaos.length; i++){
aihaos[i].checked = firstChkElt.checked;
}
}

// 给每一个name="aihao"复选框绑定鼠标单击事件
for(var i = 0; i < aihaos.length; i++){
aihaos[i].onclick = function(){
// 在这里控制第一个复选框的选中状态
// 第一个复选框选中还是不选中取决于什么?
// 所有的aihao复选框的总数量,如果和总选中的数量相同的时候,第一个复选框选中,反之取消选中.
var count = aihaos.length; //总数量
var checkedCount = 0; //默认选中的数量是0
for(var i = 0; i < aihaos.length; i++){
if(aihaos[i].checked){
checkedCount++;
}
} // 循环结束之后,所有的被选中的复选框数量就统计完了.
// 第一个复选框是选中呢,还是取消选中呢?
firstChkElt.checked = (count == checkedCount);
}
}
}
</script>

<input type="checkbox" id="firstChk"/>
<br>
<input type="checkbox" name="aihao" value="smoke"/>抽烟
<br>
<input type="checkbox" name="aihao" value="drink"/>喝酒
<br>
<input type="checkbox" name="aihao" value="firehair"/>烫头
<br>

11.4 获取一个文本框的value

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<script type="text/javascript">
window.onload = function(){
document.getElementById("btn").onclick = function(){
// 获取文本框对象
var usernameElt = document.getElementById("username");
// 获取value
var username = usernameElt.value; // 文本框的value属性用来获取用户填写的信息.
alert(username)
}
}
</script>

用户名:<input type="text" id="username" />
<br>
<input type="button" id="btn" value="获取用户名"/>

11.5 获取下拉列表选中项的value

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
数据库表存储省份和市区的数据
t_province
code(pk) name
----------------------
001 山东省
002 山西省

t_city
code(pk) name pcode(fk)
------------------------------------------
1 济南 001
2 烟台 001

只要前端浏览器能够获取到山东省的code,假设code=001
那么后台java程序执行sql语句的时候这样执行:
select * from t_city where pcode = ?;
ps.setString(1, "001");
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
<!-- 这里的this代表当前的下拉列表对象。-->
<select id="province" onchange="alert(this.value)">
<option value ="">--请选择省份--</option>
<option value ="001">河北省</option>
<option value ="002">河南省</option>
<option value ="003">山东省</option>
<option value ="004">山西省</option>
</select>

<script type="text/javascript">
window.onload = function(){
document.getElementById("province2").onchange = function(){
//这里的this代表的就是当前发生change事件的这个节点对象.
//console.log(this.value)
console.log(document.getElementById("province2").value)
}
}
</script>

<select id="province2" >
<option value ="">--请选择省份--</option>
<option value ="001">河北省</option>
<option value ="002">河南省</option>
<option value ="003">山东省</option>
<option value ="004">山西省</option>
</select>

11.6 显示网页时钟

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
<script type="text/javascript">
/* window.onload = function(){
document.getElementById("displayTimeBtn").onclick = function(){
// 获取系统当前时间,把时间显示到div中
var nowTime = new Date();
// 显示到div当中
document.getElementById("timediv").innerHTML = nowTime.toLocaleString();
}
} */

/* function display(){
console.log("显示时间")
} */

// 设置每个1S执行一次display()函数
//window.setInterval("display()", 1000)

window.onload = function(){
document.getElementById("displayTimeBtn").onclick = function(){
// 每隔1S调用一次displayTime()函数(设置周期性调用。)
// 返回值是一个可以取消周期性调用的value.
v = window.setInterval("displayTime()", 1000)
}

document.getElementById("stopTimeBtn").onclick = function(){
// 停止周期性的调用.
window.clearInterval(v)
}
}

function displayTime(){
var nowTime = new Date();
document.getElementById("timediv").innerHTML = nowTime.toLocaleString();
}

</script>

<input type="button" value="显示系统当前时间" id="displayTimeBtn"/>
<input type="button" value="时间停止" id="stopTimeBtn"/>

<div id="timediv"></div>

11.7 拼接html的方式,设置table的tbody

(非常具有代表性的案例,必须敲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
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
<script type="text/javascript">
/* 从java过来一个json格式的字符串 */
var fromJava = "{\"total\" : 2, \"students\" : [{\"name\":\"李四\",\"age\":19},{\"name\":\"王五\",\"age\":18}]}";

window.onload = function(){
document.getElementById("displaybtn").onclick = function(){
// 解析上面的json格式的字符串,将解析出来的数据放到tbody当中.
// 转化json对象
window.eval("var json = " + fromJava) //json对象有了.
// 设置总记录条数
document.getElementById("totalSpan").innerHTML = json.total;
// 拼接HTML
var studentArray = json.students;
var html = "";
for(var i = 0; i < studentArray.length; i++){
var s = studentArray[i]
html += "<tr>";
html += "<td>"+(i+1)+"</td>";
html += "<td>"+s.name+"</td>";
html += "<td>"+s.age+"</td>";
html += "</tr>";
}
// 将以上拼接的HTML设置到tbody当中
document.getElementById("stutbody").innerHTML = html;
}
}

</script>

<input type="button" value="查看学生信息列表" id="displaybtn" />
<hr >

<table border="1px" width="40%">
<tr>
<th>序号</th>
<th>学生姓名</th>
<th>学生年龄</th>
</tr>
<tbody id="stutbody">
<!-- <tr>
<td>1</td>
<td>张三</td>
<td>20</td>
</tr>
<tr>
<td>2 Vue-CLI开发Vue项目</td>
<td>李四</td>
<td>22</td>
</tr> -->
</tbody>
</table>
总记录条数:<span id="totalSpan">0</span>条
<!-- 总记录条数:2条 -->

11.8 表单验证

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
<script type="text/javascript">
/*
(1)用户名不能为空
(2)用户名必须在6-14位之间
(3)用户名只能有数字和字母组成,不能含有其它符号(正则表达式)
(4)密码和确认密码一致
(5)统一失去焦点验证
(6)错误提示信息统一在span标签中提示,并且要求字体12号,红色。
(7)文本框再次获得焦点后,清空错误提示信息
(8)最终表单中所有项均合法方可提交
*/
window.onload = function(){

var nameErrorSpan = document.getElementById("nameError");

// 给id="username"的节点绑定blur事件
var usernameElt = document.getElementById("username");
usernameElt.onblur = function(){
// 获取用户名
var username = usernameElt.value;
// 去除掉前后空白
username = username.trim();
// 用户名不能为空,不能为空串
//if(username.length == 0){}
if(username == ""){
nameErrorSpan.innerHTML = "用户名不能为空";
}else{
// 用户名不是空,继续判断长度是否合法
if(username.length < 6 || username.length > 14){
nameErrorSpan.innerHTML = "用户名长度必须在[6-14]之间";
}else{
// 用户名不为空,并且长度也合法,接下来继续判断用户名中是否有特殊符号
var regExp = /^[a-zA-Z0-9]+$/
var ok = regExp.test(username)
if(ok){
// 合法
nameErrorSpan.innerHTML = "";
}else{
// 不合法
nameErrorSpan.innerHTML = "用户名只能由数字和字母组成";
}
}
}
}

// 获得焦点:清空span的错误信息.
usernameElt.onfocus = function(){
nameErrorSpan.innerHTML = "";
}

var pwdErrorSpan = document.getElementById("pwdError");
// 确认密码失去焦点就验证.
document.getElementById("confirmpwd").onblur = function(){
//获取密码
var userpwd = document.getElementById("userpwd").value;
//获取确认密码
var confirmpwd = document.getElementById("confirmpwd").value;
//进行比对
if(userpwd != confirmpwd){
pwdErrorSpan.innerHTML = "密码和确认密码不一致";
}else{
pwdErrorSpan.innerHTML = "";
}
}


document.getElementById("confirmpwd").onfocus = function(){
pwdErrorSpan.innerHTML = "";
}

document.getElementById("regbtn").onclick = function(){

// 验证用户名,怎么验证用户名?让用户名文本框失去焦点
// 重点:使用JS代码怎么触发事件??????
usernameElt.focus(); //触发文本框的获取焦点事件
usernameElt.blur();//触发文本框的失去焦点事件

// 验证密码,怎么验证密码?让确认密码失去焦点
document.getElementById("confirmpwd").focus();
document.getElementById("confirmpwd").blur();

// 当所有的span都是空的表示表单合法
if(nameErrorSpan.innerHTML == "" && pwdErrorSpan.innerHTML == ""){
//提交
var formObj = document.getElementById("userForm");
// 通过调用submit()方法来完成表单的提交
formObj.submit();
}
}
}

</script>

<form id="userForm" action="http://localhost:8080/oa/save">
用户名<input type="text" name="username" id="username"/><span id="nameError"></span>
<br>
密码<input type="password" name="userpwd" id="userpwd"/>
<br>
<!-- 确认密码是不需要提交给服务器的,这个name不要写! -->
确认密码<input type="password" id="confirmpwd"/> <span id="pwdError"></span>
<br>
<!-- 表单所有项目都合法才能提交 -->
<!-- <input type="submit" value="注册" /> -->
<!-- button不能提交表单,但是JS代码可以提交表单 -->
<input type="button" value="注册" id="regbtn"/>
</form>

11.9 获取元素的三种方式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
<div id="div1"></div>
<div id="div2"></div>

<input type="checkbox" name="aihao" value="1"/>
<input type="checkbox" name="aihao" value="2"/>
<input type="checkbox" name="aihao" value="3"/>
<input type="checkbox" name="aihao" value="4"/>

<script type="text/javascript">
// 这是JS中非常经典的获取元素的三种方式.
// 根据id获取一个元素
var div1 = document.getElementById("div1");
console.log(div1)

//根据name属性获取多个元素
var aihaos = document.getElementsByName("aihao");
console.log(aihaos)

//根据标签的名字获取
var divs = document.getElementsByTagName("div");
console.log(divs)
</script>
  • 本文标题:DOM学习笔记
  • 本文作者:馨er
  • 创建时间:2022-10-29 21:57:13
  • 本文链接:https://sjxbbd.vercel.app/2022/10/29/66d3b31c4153/
  • 版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!