DOM
DOM 是浏览器提供给 JavaScript 操作网页的接口,全称为“文档对象模型”(Document Object Model),DOM 是 HTML 页面结构的抽象表示,整个网页的DOM结构就像是一颗树,树的根就是 document 节点(根节点),每个 DOM 节点都可以有父节点、子节点、兄弟节点。
DOM 的最小组成单位是节点,一共有7个节点类型
| 节点名称 | 节点类型 | 类型常量 | 类型数值 |
|---|---|---|---|
| Element | 元素 | Node.ELEMENT_NODE | 1 |
| Attribute | 属性 | 2 | |
| Text | 文本 | Node.TEXT_NODE | 3 |
| Document | 文档 | Node.DOCUMENT_NODE | 9 |
| DocumentType | 文档类型 | Node.DOCUMENT_TYPE_NODE | 10 |
| Comment | 注释类型 | Node.COMMENT_NODE | 8 |
| DocumentFragment | 文档片段 | Node.DOCUMENT_FRAGMENT_NODE | 11 |
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方法使用,触发指定事件。接收的参数是事件的类型,例如UIEvents、MouseEvents、MutationEvents、HTMLEvents。
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会将节点内出现的<、>等符号转为>、<。当需要读取节点的文本内容的时候,最好使用 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")