【浏览器】- DOM

210 阅读7分钟

DOM

DOM 是浏览器提供给 JavaScript 操作网页的接口,全称为“文档对象模型”(Document Object Model),DOM 是 HTML 页面结构的抽象表示,整个网页的DOM结构就像是一颗树,树的根就是 document 节点(根节点),每个 DOM 节点都可以有父节点、子节点、兄弟节点。 ​

DOM 的最小组成单位是节点,一共有7个节点类型

节点名称节点类型类型常量类型数值
Element元素Node.ELEMENT_NODE1
Attribute属性NODE.ATTRIBUTE_NODE (已废弃)2
Text文本Node.TEXT_NODE3
Document文档Node.DOCUMENT_NODE9
DocumentType文档类型Node.DOCUMENT_TYPE_NODE10
Comment注释类型Node.COMMENT_NODE8
DocumentFragment文档片段Node.DOCUMENT_FRAGMENT_NODE11

Node 接口

前面说过 HTML 文档由各种类型的 DOM 节点构成,所以各种类型的 DOM API 对象都继承自这个接口

节点的信息

  • nodeType——判断节点的类型
// 返回值是一个整数,代表节点的类型
document.nodeType // 9

// 或者使用表示节点类型的常量
document.nodeType === Node.DOCUMENT_NODE;
  • nodeValue——返回节点的文本内容
// <div id="app">hello world</div>

const div = document.getElementById('app');
div.nodeValue // null
div.firstChild.nodeValue // "hello world",文本内容相当于 textNode 节点

节点的关系

  • nextSibling——后一个同级节点
  • previousSibling——前一个同级节点
  • parentNode——当前节点的父节点
  • parentElement——当前节点的父元素节点
  • firstChild——当前节点的第一个子节点
  • lastChild——当前节点的最后一个子节点
  • childNodes——返回一个类似数组的对象(NodeList集合),表示当前节点的所有子节点

操作节点

  • appendChild()——在当前节点的最后一个子节点的后面插入一个子节点
  • insertBefore()——在给定节点的前面插一个新的节点
  • removeChild()——删除当前节点的一个子节点
  • replaceChild()——替换当前节点的一个子节点为给出的新节点

NodeList集合

DOM 节点都是一个个单独的对象,为了批量操作多个节点,就产生了 NodeList 接口。NodeList 不是数组但类似数组,可以使用 forEach() 、length,但是不能使用push()pop() 这一类方法,因为它不继承自 Array 类型。 ​

NodeList 可以返回动态集合或者静态集合。动态集合:只有 Node.childNodes() 能返回动态集合,任何一个节点的变动,都会立刻更新到动态节点集合中;静态集合:任何节点的变动,都不会立刻更新到静态集合中,除非手动的去检测并更新。

Document 节点

document 节点是文档的根节点,每张网页都有自己的 document 对象

创建节点

createElement()

用于创建元素节点,并返回该节点

document.createElement('div')

createTextNode()

用于创建文本节点,可以确保浏览器会将其内容当作纯文本而不是 HTML 来解析

document.createTextNode('Hello')

createAttribute()

用于创建属性节点,返回值是被创建的属性

let h = document.createAttribute('hello')
h.value = "world"

createDocumentFragment()

用于生成一个空的文档片段,DocumentFragment 是一个存在于内存中的 DOM 片段,不属于当前文档,常常用来生成一段较复杂的 DOM 结构,然后再插入当前文档。 ​

这样做的好处在于,因为DocumentFragment不属于当前文档,对它的任何改动,都不会引发网页的重新渲染,比直接修改当前文档的 DOM 有更好的性能表现。 ​

通常,先创建一个空的文档片段,然后把要生成的一系列DOM节点插入到 documentFragment 中,再把 documentFragment 插入到文档中,这样就只需要插入一次。 ​

事件监听

addEventListener()

为元素节点添加事件监听

document.getElementById("app").addEventListener('click', function(){
    console.log("Hello World")
})

removeEventListener()

用于移除元素节点上的事件监听

createEvent()

用于实现自定义事件监听,它生成一个事件对象(Event实例),该对象可以被dispatchEvent方法使用,触发指定事件。接收的参数是事件的类型,例如UIEventsMouseEventsMutationEventsHTMLEvents

const myEvent = document.createEvent('MouseEvents')
document.addEventListener(myEvent, function(){
    console.log("Hello World")
})

dispatchEvent()

用于触发 createEvent() 自定义的事件

document.dispatchEvent(myEvent)

Element 节点

Element 对象对应网页的 HTML 元素,也就是一个个元素节点对象

操作元素类名

element.classList 属性表示一个元素节点的 class 类名组成的数组,它包含了一些方法,可以方便的操作元素的样式:

  • add():增加一个 class
  • remove():移除一个 class
  • contains():检查当前元素是否包含某个 class
  • toggle():将某个 class 移入或移出当前元素

获取元素节点

document.body

返回 html 的 body 节点

getElementById()

根据节点的 id 属性来获取到节点

getElementByClassName()

根据节点的 class 属性来获取到节点

getElementByTagName()

根据节点的 HTML 标签名称来获取到节点

getElementByName()

获取拥有 name 属性的 HTML 元素节点

querySelector()

根据元素的 CSS 选择器标识来获取匹配到的第一个元素

querySelectorAll()

根据元素的 CSS 选择器标识来获取匹配到的所有元素 ​

获取元素内容

innerHTML

该属性返回一个字符串,等于该元素节点标签内部所包含所有内容。该属性可读写,常用来设置某个节点的内容,innerHTML会将节点内出现的<>等符号转为&gt、&lt。当需要读取节点的文本内容的时候,最好使用 textContent 属性来读取。如果插入的内容之中含有 <script> 标签,虽然可以生成 script 节点,但是插入的代码不会执行。

<div id="app">
    <p>Hello World</p>
</div>
const content = document.getElementById('app').innerHTML
console.log(content)
/*
<p>Hello World</p>
*/

outerHTML

该属性用于获取整个节点的内容,返回值是字符串,例如:

<div id="app">
    <p>Hello World</p>
</div>
const content = document.getElementById('app').outerHTML
console.log(content)
/*
<div id="app">
    <p>Hello World</p>
</div>
*/

outerHTML 属性也是可读写的,对它进行赋值,等于替换掉当前元素。如果一个节点没有父节点,设置outerHTML属性会报错。 ​

HTMLCollection 集合

表示元素节点的集合,它也是一个类数组的动态集合,当它所包含的文档结构发生改变时,它会自动更新,类似于 NodeList;它只能包含元素节点 (Element 对象) 而不能是其他类型的节点。 ​

属性

  • length——返回当前集合内所有子元素的个数

方法

  • item()——获取集合中指定索引位置的元素
  • namedItem() —— 通过元素的 name 属性来获取指定子元素,这要求元素必须有 name 属性,否则会返回 null

前面提到的获取元素节点的所有方法返回的元素集合都继承了 HTMLCollection 接口,可以直接使用其方法。

ParentNode接口

如果一个节点拥有子节点,那么它就会继承 ParentNode 接口,这样它就有了访问和操作自身元素子节点的能力。

属性

  • children——返回一个 HTMLCollection 集合,包含了当前节点的所有元素子节点
  • firstElementChild——返回当前节点的第一个元素子节点
  • lastElementChild——返回当前节点的最后一个元素子节点

方法

  • append()——当前节点最后一个元素子节点的后面,追加一个或多个子节点
  • prepend()——当前节点第一个元素子节点的前面,追加一个或多个子节点

补充

append 与 appendChild 类似,但也有一些不同:

  • Element.append()允许追加DOMString 对象(表示 DOM 结构的字符串),而 Node.appendChild() 只接受 Node 对象。
  • Element.append() 没有返回值,而 Node.appendChild() 返回追加的 Node 对象。
  • Element.append() 可以追加多个节点和字符串,而 Node.appendChild() 只能追加一个节点。

元素类型节点的属性

dataset属性

有时候我们需要在 HTML 元素上自定义一些属性,以供Javascript调用,我们可以这样自定义属性:

<div id="app" myattribute="hello"></div>

这样虽然可以定义任意的属性,但是它不符合HTML规范,因为它不属于标准属性。所以,为了符合HTML规范,就要使用data-来定义任意的我们想要的属性且符合规范,像下面这样:

<div id="app" data-mycolor="blue"></div>
<p data-mytext="title">Hello World</p>

注意:自定义属性的名字不能是大写字母,只能数字、连词线、小写字母、点号、冒号和下划线,下面是一些示例:

<div id="app" data-mycolor="blue"></div> <!-- 合法 -->
<div id="app" data-my-color="blue"></div> <!-- 合法 -->
<div id="app" data-MyColor="blue"></div> <!-- 非法 -->
<div id="app" data-my:color="blue"></div> <!-- 合法 -->

通过元素节点的 dataset 属性来访问自定义的属性

<div id="app" data-mycolor="blue"></div>
<p data-mytext="title">Hello World</p>
document.getElementById("app").dataset.mycolor // blue

属性的操作

  • getAttribute()

返回当前元素节点的指定属性,如果这个属性不存在,则返回null

document.getElementById("app").getAtrribute("href")  // one
  • setAttribute()

用于给一个元素节点设置属性

document.getElementById("app").setAtrribute("href", "three")
  • hasAttribute()

判断一个元素节点是否有这个属性,返回一个布尔值

document.getElementById("app").hasAtrribute("href")  // fasle
  • removeAttribute()

用于从当前元素节点移除一个属性

document.getElementById("app").removeAtrribute("href")