01-DOM介绍和元素节点操作
码路教育 7/19/2022
# 1. DOM介绍
# 1.1 认识DOM
JavaScript是一门编程语言,它的运行环境有两个,一个是浏览器,一个是node,前面我们学的JS必于ECMAScript中的语法,浏览器的JS有三部分组成:
- ECMAScript
- DOM
- BOM
ECMAScript并不能和和网页进行交互,操作浏览器(网页),就需要给我们提供一个API,我们去调用API,实现操作,学习DOM和BOM就是学习浏览器给我们提供的API,所以说,DOM和BOM操作,也叫WebAPI。
深入理解DOM
- 浏览器将我们编写在HTML中的每一个元素(Element)都抽象成了一个个对象
- 所有这些对象都可以通过JavaScript来对其进行访问,那么我们就可以通过JavaScript来操作页面
- 将这个抽象过程称之为 文档对象模型(Document Object Model)
DOM:Docuemnt Object Model 文档对象模型
- 文档:html文档 之前的html文件就可以称一个文档
- Object: 对象 一切都是对象 所有的元素都是对象
- Model:模型 树模型 所有的元素,要形成一个树模型
整个文档被抽象到 document 对象中
- 如document.documentElement对应的是html元素
- 如document.body对应的是body元素
- 如document.head对应的是head元素
<script>
// 下面的一行代码可以让整个页面变成红色
document.body.style.background = "gold";
</script>
1
2
3
4
2
3
4
DOM树
- 在html结构中,最终会形成一个树结构
# 1.2 重新认识window
前面我们学习了一个window的全局对象,window上事实上就包含了这些内容
- JavaScript语法部分的Object、Array、Date等
- DOM
- BOM
# 2. document对象
Document节点表示的整个载入的网页,它的实例是全局的document对象
- 对DOM的所有操作都是从 document 对象开始的
- 它是DOM的 入口点,可以从document开始去访问任何节点元素
对于最顶层的html、head、body元素,我们可以直接在document对象中获取到
- document.documentElement 得到 html元素
- document.body 得到 body元素
- document.head 得到 head元素
- document.doctype 得到 文档声明元素
<body>
<script>
// document是内置对象 也是GO中的
// 一个节点就是一个对象
// 对象是属性和方法的无序集合 api
// 学习DOM操作就学习一个对象中的属性或方法 api
// console.dir(document);
// 获取head元素节点
// console.log(document.head);
// 获取title元素节点中的文本节点
// console.log(document.title);
// 获取body元素节点
// console.log(document.body);
document.write("<h1>Hello DOM</h1>")
</script>
</body>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
# 3. 节点与节点关系
节点分类:
- 元素节点
- 文本节点
- 属性节点
- 注释节点
- ...
注意点:
- 在DOM树上,只需要关注元素节点和文本节点,在DOM上,没有属性节点。
- 我们只需要关注三种节点:1)元素节点 2)文本节点 3)属性节点
<body>
<!-- 我是一个注释 -->
<div id="box" title="haha">我是一个DIV</div>
<a href="http://www.baidu.com">百度一下</a>
<script>
let oDiv = document.getElementById("box");
// nodeType判断一个节点的类型
// 如果是元素节点 它的nodeType是1
console.log(oDiv.nodeType); // 1
// 要获取属性节点,必须先得到元素节点
// 通过打点的形式,就可以获取属性节点
// console.log(oDiv.title); // haha
// getAttribute 根据属性名获取属性值的
console.log(oDiv.getAttribute("title")); // haha
// getAttributeNode 获取属性节点
let attr = oDiv.getAttributeNode("title"); // 2
console.log(attr.nodeType);
let text = oDiv.firstChild;
console.log(text); // 我是一个DIV
console.log(text.nodeType); // 3
</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
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
节点之间的关系一:
- 获取到一个节点(Node)后,可以根据这个节点去获取其他的节点,我们称之为节点之间的关系
- 父节点:parentNode
- 前兄弟节点:previousSibling
- 后兄弟节点:nextSibling
- 子节点:childNodes
- 第一个子节点:firstChild
- 最后一个子节点:lastChild
<body>
<!-- 我是一个注释 -->
我是一个文本
<div class="box">
我是一个孤独的DIV
</div>
<ul>
<li>One</li>
<li>Two</li>
<li>Three</li>
</ul>
<!-- <script>
// 获取body的元素节点
let bodyEle = document.body;
console.log(bodyEle.firstChild); // #text 换行节点
console.log(bodyEle.firstChild.nextSibling);
console.log(bodyEle.parentNode);
</script> -->
<script>
// 通过节点关系去获取某些节点,非常麻烦,因为需要考虑换行节点和注释节点
let bodyEle = document.body;
console.log(bodyEle.firstChild.nextSibling.nextSibling.nextSibling);
</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
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
节点之间的关系二:
- 获取到一个节点(Node)后,可以根据这个节点去获取其他的节点,我们称之为节点之间的关系
- 父节点:parentElement
- 前兄弟节点:previousElementSibling
- 后兄弟节点:nextElementSibling
- 子节点:children
- 第一个子节点:firstElementChild
- 最后一个子节点:lastElementChild
<body>
<!-- 我是一个注释 -->
我是一个文本
<div class="box">
我是一个孤独的DIV
</div>
<ul>
<li>One</li>
<li>Two</li>
<li>Three</li>
</ul>
<script>
let bodyEle = document.body;
console.log(bodyEle.firstElementChild);
console.log(bodyEle.firstElementChild.nextElementSibling);
// 得到一个伪数组
console.log(bodyEle.firstElementChild.nextElementSibling.children);
</script>
</body>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
节点关系总结(加粗的是需要记的):
- parentNode 获取父元素节点 没有兼容性问题
- parentElement 获取父元素节点 没有兼容性问题
- firstChild 获取第一个子节点 会考虑换行节点和注释节点 不要用
- firstElementChild 获取第一个子元素节点 可以使用
- lastChild 获取最后一个子节点 会考虑换行节点和注释节点 不要用
- lastElementChild 获取最后一个子元素节点 可以使用
- nextSibling 获取下一个兄弟节点 会考虑换行节点和注释节点 不要用
- nextElementSibling 获取下一个兄弟元素节点 可以使用
- previousSibling 获取上一个兄弟节点 会考虑换行节点和注释节点 不要用
- previousElementSibling 获取上一个兄弟元素节点 可以使用
# 4. 获取元素节点的方法
通过节点关系可以得到某个元素,但是,在实际开发中,我们希望可以任意的获取到某一个元素应该如何操作呢?
DOM为我们提供了获取元素的方法:
最常用的几个方法如下:
- document.getElementById
- document.getElementsByTagName
- document.querySelectorAll
- document.querySelector
<body>
<div id="box" name="a" class="item">我是一个DIV1</div>
<div id="box" name="a" class="item">我是一个DIV2</div>
<p class="father">
<span class="son">son</span>
</p>
<ul class="wrap">
<li>1</li>
<li>2</li>
<li>3</li>
</ul>
<script>
// 1) ------ document.getElementById 获取1个
// let oDiv = document.getElementById("box")
// console.dir(oDiv);
// 2) ------ document.getElementsByTagName 得到的是伪数组
// 伪数组 本质是对象 Array.from()
// 是文档中所有的div 要获取某个div,需要通过索引
// let oDivs = document.getElementsByTagName("div")
// console.log(oDivs)
// console.log(oDivs[0])
// 3) ------ document.getElementsByName 得到的是伪数组
// 是文档中所有的div 要获取某个div,需要通过索引
// let oDivs = document.getElementsByName("a")
// console.log(oDivs)
// console.log(oDivs[0])
// 4) ------ document.getElementsByClassName 得到的是伪数组
// let oDivs = document.getElementsByClassName("item")
// console.log(oDivs)
// console.log(oDivs[0])
// 5) ------ document.querySelectorAll 得到的是伪数组
// querySelector是选择器的意思 通过选择器来获取元素
// querySelectorAll 获取多个
// let lis = document.querySelectorAll("li")
// console.log(Array.isArray(lis)); // 得到的也是一个伪数组
// console.log(lis[0]);
// 6) ------ document.querySelector 得到的是伪数组
// querySelector 获取第1个
// let li = document.querySelector("li")
// console.log(li);
</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
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
# 5. 节点的属性
# 5.1 节点的属性之nodeType
不同的节点类型有可能有不同的属性,但它们有共有属性:
- nodeType 属性提供了一种获取节点类型的方法
- 其他类型可以查看MDN文档: https://developer.mozilla.org/zh-CN/docs/Web/API/Node/nodeType
常见的节点类型有如下:
<body>
<!-- 我是一个注释 -->
我是文本
<div class="father">
<h2>我是一个H2</h2>
<p>我是内容</p>
</div>
<script>
let bodyChildNodes = document.body.childNodes;
let commentNode = bodyChildNodes[1];
let textNode = bodyChildNodes[2];
let divNode = bodyChildNodes[3];
console.log(commentNode.nodeType);
console.log(textNode.nodeType);
console.log(divNode.nodeType);
for(let node of bodyChildNodes){
if(node.nodeType === 8){
console.log(node + "是注释节点");
}else if(node.nodeType === 3){
console.log(node + "是文本节点");
}else if(node.nodeType === 1){
console.log(node + "是元素节点");
}
}
</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
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
# 5.2 节点的属性之nodeName
- nodeName:获取node节点的名字;
- tagName:获取元素的标签名词;
tagName 和 nodeName 之间有什么不同呢?
- tagName 属性仅适用于 Element 节点;
- nodeName 是为任意 Node 定义的
- nodeName 对于元素,它的意义与 tagName 相同,所以使用哪一个都是可以的;
- nodeName 对于其他节点类型(text,comment 等),它拥有一个对应节点类型的字符串;
<body>
<!-- 我是一个注释 -->
我是文本
<div class="father">
<h2>我是一个H2</h2>
<p>我是内容</p>
</div>
<script>
let bodyChildNodes = document.body.childNodes;
let commentNode = bodyChildNodes[1];
let textNode = bodyChildNodes[2];
let divNode = bodyChildNodes[3];
// 得到节点名
console.log(commentNode.nodeName); // #comment
console.log(textNode.nodeName); // #text
console.log(divNode.nodeName); // #DIV
console.log("-------------");
console.log(commentNode.tagName); // undefined
console.log(textNode.tagName); // undefined
console.log(divNode.tagName); // #DIV
</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
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
# 5.3 节点的属性之innerHTML和textContent
innerHTML 属性
- 将元素中的 HTML 获取为字符串形式
- 设置元素中的内容;
<body>
<!-- 我是一个注释 -->
我是文本
<div class="father">
<h2>我是一个H2</h2>
<p>我是内容</p>
</div>
<script>
let bodyChildNodes = document.body.childNodes;
let commentNode = bodyChildNodes[1];
let textNode = bodyChildNodes[2];
let divNode = bodyChildNodes[3];
// 获取div标签中的内容
console.log(divNode.innerHTML);
// 设置div标签中的内容
divNode.innerHTML = "<strong>我是一个Strong标签</strong>"
</script>
</body>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
outerHTML 属性
- 包含了元素的完整 HTML
- innerHTML 加上元素本身一样;
<body>
<!-- 我是一个注释 -->
我是文本
<div class="father">
<h2>我是一个H2</h2>
<p>我是内容</p>
</div>
<script>
let bodyChildNodes = document.body.childNodes;
let divNode = bodyChildNodes[3];
// 获取div标签中的内容 outerHTML相比innerHTML来说,带上自己本身
console.log(divNode.outerHTML);
// 对于设置来说,使用innerHTML多一点
divNode.outerHTML = "<strong>我是一个Strong标签</strong>"
</script>
</body>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
textContent 属性
- 仅仅获取元素中的文本内容;
<body>
<!-- 我是一个注释 -->
我是文本
<div class="father">
<h2>我是一个H2</h2>
<p>我是内容</p>
</div>
<script>
let bodyChildNodes = document.body.childNodes;
let divNode = bodyChildNodes[3];
// 只能获取文本节点
console.log(divNode.textContent);
// 设置也是针对文本节点,如果写了标签,也不会解析
divNode.textContent = "<strong>我是一个Strong标签</strong>";
</script>
</body>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
innerHTML和textContent的区别:
- 使用 innerHTML,我们将其“作为 HTML”插入,带有所有 HTML 标签。
- 使用 textContent,我们将其“作为文本”插入,所有符号(symbol)均按字面意义处理。
# 5.4 节点的属性之nodeValue
- 用于获取非元素节点的文本内容
<body>
<!-- 我是一个注释 -->
我是文本
<div class="father">
<h2>我是一个H2</h2>
<p>我是内容</p>
</div>
<script>
let bodyChildNodes = document.body.childNodes;
let commentNode = bodyChildNodes[1];
let textNode = bodyChildNodes[2];
let divNode = bodyChildNodes[3];
// 获取注释节点中的内容
console.log(commentNode.nodeValue);
// 获取文本节点中的内容
console.log(textNode.nodeValue);
// 元素节点的nodeValue是null
console.log(divNode.nodeValue); // null
</script>
</body>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
# 5.5 节点的属性之hidden
- 用于设置元素隐藏
<body>
<button id="btn">Toggle</button>
<div class="box" style="color: red;">
Hello DOM~
</div>
<script>
let btn = document.getElementById("btn");
let div = document.getElementsByTagName("div")[0];
// console.log("start...");
// 给btn绑定点击事件 btn叫事件源 click叫点击事件(事件类型)
// function(){} 事件处理程序,也叫监听器
// 事件绑定是异步任务,是宏任务
btn.onclick = function(){
// console.log("click...");
// 隐藏div
// div.style.display = "none"; // 隐藏方式一
// div.hidden = true; // 隐藏方式二
// if(div.hidden === false){
// div.hidden = true;
// }else{
// div.hidden = false;
// }
// 经典
div.hidden = !div.hidden
}
// console.log("end...");
</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
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
# 6. 创建和挂载节点
我们想要插入一个元素,通常会按照如下步骤:
- 步骤一:创建一个元素;
- 步骤二:插入元素到DOM的某一个位置;
创建节点:
- createElement 创建元素节点
- createTextNode 创建文本节点
- createComment 创建注释节点
- createAttribute 创建属性节点
挂载节点:
- 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 替换为给定的节点或字符串。
<body>
<div class="father">
<div class="son">
SON
</div>
</div>
<script>
let father = document.querySelector(".father");
// 使用innerHTML也可以添加元素,不推荐
// father.innerHTML = "<h2>我是一个H2标签</h2>";
let h2Ele = document.createElement("h2");
h2Ele.textContent = "我是H2标签";
console.log(h2Ele);
// 需要把h2挂载到DOM树上
father.append(h2Ele);
// father.prepend(h2Ele)
// father.before(h2Ele)
// father.after(h2Ele)
// father.replaceWith(h2Ele)
let spanEle = document.createElement("span");
spanEle.textContent = "我是一个孤独的span";
h2Ele.append(spanEle)
</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
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
# 7. 删除,替换,克隆节点
删除节点:
- removeChild 只有父元素才有资格删除一个子元素
- remove 移除元素我们可以调用元素本身的remove方法:
<body>
<div class="father">
<div class="son">
son
</div>
</div>
<script>
let father = document.querySelector(".father");
let son = document.querySelector(".son");
// son.remove, 自己移除自己
// son.remove();
// 父也有能力去移除它里面的元素
father.removeChild(son)
</script>
</body>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
替换节点:
- replaceChild
<body>
<div class="father">
<div class="son">
son
</div>
</div>
<script>
let father = document.querySelector(".father");
let son = document.querySelector(".son");
let pEle = document.createElement("p");
pEle.textContent = "GrandSon";
father.replaceChild(pEle,son)
</script>
</body>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
如果我们想要复制一个现有的元素,可以通过cloneNode方法克隆节点:
- 可以传入一个Boolean类型的值,来决定是否是深度克隆;
- 深度克隆会克隆对应元素的子元素,否则不会;
<body>
<div class="father">
<div class="son">
son
</div>
</div>
<script>
let father = document.querySelector(".father");
let son = document.querySelector(".son");
// 默认是浅copy 只copy一个节点,内部的其它节点不会copy
// let newFather = father.cloneNode();
// console.log(newFather);
// 如果传递一个true表示深copy 如果不传或传一个false表示浅copy
let newFather = father.cloneNode(true);
console.log(newFather);
</script>
</body>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19