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

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

# 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

节点之间的关系一:

  • 获取到一个节点(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

节点之间的关系二:

  • 获取到一个节点(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

节点关系总结(加粗的是需要记的):

  • 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

# 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

# 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

# 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

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

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

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

# 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

# 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

# 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

替换节点:

  • 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

如果我们想要复制一个现有的元素,可以通过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
Last Updated: 12/25/2022, 10:02:14 PM