本文主要内容包括DOM概念的介绍、DOM对象的一些常用属性和方法、如何获取DOM节点的父子、相邻元素以及常用的操作DOM节点的方法,也总结了常见方法的浏览器兼容性问题以及使用时需要注意的地方,内容还算全面,可当小手册使用。
1. 什么是DOM
DOM,文档对象模型(Document Object Model)。DOM是 W3C(万维网联盟)的标准,DOM定义了访问HTML和XML文档的标准。在W3C的标准中,DOM是独于平台和语言的接口,它允许程序和脚本动态地访问和更新文档的内容、结构和样式。
按照我的理解,DOM通俗的来说就是一个表示当前页面内容和结构树形Javascript对象,通过”这棵树“,Javascript脚本可以很方便的访问页面上的元素并且动态地对其进行操作,所以DOM其实是JS和页面之间的一个桥梁。
2. 什么是DOM节点
根据 W3C 的 HTML DOM 标准,HTML 文档中的所有内容都是节点:
- 整个文档是一个文档节点
- 每个 HTML 元素是元素节点
- HTML 元素内的文本是文本节点
- 每个 HTML 属性是属性节点
- 注释是注释节点
一个DOM节点可以嵌套任意的子节点。
3. DOM节点类型
为了区分不同的DOM节点,W3C制定了一个枚举值来表示不同的节点:
枚举值 | 类型 | 描述 |
---|---|---|
1 | Element | 代表元素 |
2 | Attr | 代表属性 |
3 | Text | 代表元素或属性中的文本内容 |
4 | CDATASection | 代表文档中的 CDATA 部分(不会由解析器解析) |
5 | EntityReference | 代表实体引用 |
6 | Entity | 代表实体 |
7 | ProcessingInstruction | 代表处理指令 |
8 | Comment | 代表注释 |
9 | Document | 代表整个文档(DOM 树的根节点) |
10 | DocumentType | 向为文档定义的实体提供接口 |
11 | DocumentFragment | 代表轻量级的 Document 对象,能够容纳文档的某个部分 |
12 | Notation | 代表 DTD 中声明的符号 |
可以通过DOM对象的nodeType
属性来获取上面的枚举值,例如:
document.nodeType; // 9
document.body.nodeType; // 1
其中,我们要重点记住的只有两个:
- 元素节点:
nodeType = 1
- 文本节点:
nodeType = 3
经常用来区分一个节点是元素节点还是文本节点,或者过滤出一个节点的子节点中所有的元素节点。
4. DOM属性
innerHTML
:代表元素的内容,可以重新赋值,但效率较低innerText
:代表元素内部的文本nodeName
:代表节点的名称-
nodeName
是只读的 - 元素节点的
nodeName
与标签名相同 - 属性节点的
nodeName
与属性名相同 - 文本节点的
nodeName
始终是#text
- 文档节点的
nodeName
始终是#document
-
nodeValue
:代表节点的值- 元素节点的
nodeValue
是undefined
或null
- 文本节点的
nodeValue
是文本本身 - 属性节点的
nodeValue
是属性值
- 元素节点的
nodeType
: 见上一节
5. DOM元素遍历:获取父子节点和兄弟节点
1. 获取子节点
通过以下属性可以获取元素的子节点,某些属性存在兼容性问题
-
childNodes
:获取所有的子节点 -
children
: 仅仅获取子元素节点*(1) -
firstChild
: 返回第一个子节点 -
firstElementChild
: 返回第一个元素节点*(2) -
lastChild
: 返回第一个子节点 -
lastElementChild
: 返回第一个元素节点*(3)
兼容性问题:
(1):children
属性IE9以下浏览器依然会包含非元素节点
(2)、(3):IE9以下浏览器不支持firstElementChild
和lastElementChild
,即使在受支持的IE版本上,这两个属性也不支持在Document
和DocumentFragment
对象上使用
建议:
通常情况下,childNodes
结合nodeType
属性可以解决绝大多数问题。
2. 获取父节点
-
parentNode
:返回父节点 -
parentElement
:返回父元素节点*(1) -
offsetParent
:返回一个指向最近的(指包含层级上的最近)包含该元素的定位(position
非默认值)元素*(2)
兼容性问题:
(1):IE下parentElement
仅仅可以在元素节点上使用
(2):如果没有定位的元素,则offsetParent
为最近的table
,table cell
或根元素(标准模式下为html
;quirks 模式下为body
)。当元素的style.display
设置为none
时,offsetParent
返回null
。
3. 获取兄弟节点
-
nextSibling
:下一个兄弟节点 -
nextElementSibling
:下一个兄弟元素节点*(1) -
previousSibling
:前一个兄弟节点 -
previousSiblingElement
:前一个兄弟元素节点*(2)
兼容性问题:
(1)、(2):IE下nextElementSibling
、previousSiblingElement
仅仅可以在元素节点上使用
6. 元素属性操作
-
.
操作符:document.body.style.backgroundColor = "red";
-
[]
操作符:document.body["style"]["backgroundColor"] = "red";
或document.body["style"]["background-color"] = "red";
-
setAttribute
方法
关于
attribute
和property
的区别请看:
https://www.cnblogs.com/imwtr/p/4765191.html
https://segmentfault.com/a/1190000003727646
7.元素的查找
getElementByXXX
系列方法基于文档
-
document.getElementById(id);
:根据id
查找 -
document.getElementsByTagName(tagName);
:根据标签名查找 -
document.getElementsByTagNameNS(ns, tagName);
:在指定的命名空间ns
下根据标签名查找,ns
或tagName
为*
时表示匹配所有标签 -
document.getElementsByName(name);
:根据name
属性查找 -
document.getElementsByClassName(className);
:根据class
查找*(1)
-
基于元素
-
element.getElementsByTagName(tagName);
:根据标签名查找 -
element.getElementsByTagNameNS(ns, tagName);
:在指定的命名空间ns
下根据标签名查找,ns
或tagName
为*
时表示匹配所有标签 -
element.getElementsByClassName(className);
:根据class
查找*(2)
-
querySelector[All]
方法
在Document
、Element
、DocumentFragment
实例上均可使用下面的方法:-
instance.querySelector(selector)
:查找匹配指定 CSS 选择器的第一个元素*(3) -
instance.querySelectorAll(selector)
:查找匹配指定 CSS 选择器的所有元素*(4)
-
兼容性问题:
(1)、(2):IE9以下不支持getElementsByClassName
方法
(3)、(4):IE8以下不支持querySelector
、querySelectorAll
方法
IE9以下不支持DocumentFragment
对象
8. DOM节点的操作:创建、插入、删除、修改/替换、移动
- 创建
-
document.createElement(tagName)
:创建新的元素节点 -
document.createAttribute(attrName)
:创建新的属性节点 -
document.createTextNode(text)
:创建新的文本节点 -
document.createComment(annotation)
: 创建新的注释节点 -
document.createDocumentFragment()
:创建文档片段节点
-
注意:创建后的节点只是存在于内存中,需要通过插入操作才会插入到页面上
- 插入
-
parent.appendChild(child)
:向父节点的最后一个子节点后追加新节点 -
parent.insertBefore(newChild, existingChild)
:向父节点的某个特定子节点之前插入新节点 -
element.setAttributeNode( attributeName )
:给元素增加属性节点
-
注意:
appendChild
方法接受的对象(child)可以是元素/文本/注释/文档碎片
节点,但方法调用者(parent)不能是文本/注释
节点,insertBefore
方法类似。
删除
-
parent.removeChild(child)
:删除已有的子节点,返回值为被删除的节点 -
element.removeAttribute('attrName')
: 删除具有指定属性名称的属性,无返回值 -
element.removeAttributeNode( attrNode )
:删除指定属性,返回值为被删除的属性
-
修改/替换
-
parent.replaceChild(newChild, existingChild)
:用新节点替换父节点中已有的子节点 -
element.setAttributeNode(attrName)
:若原元素已有该属性,会更新之,返回被替换掉的属性节点 -
element.setAttribute(attrName, attrValue )
:若原元素已有该属性,会更新之,无返回值
-
移动
- 若想将一个DOM元素从一个位置移动到另外一个位置,不需要先删除该节点再将该节点新增到新的位置,而只需要查找到该元素,然后直接在目标位置的父元素上直接调用
appendChild
或者insertBefore
方法即可,元素会被自动剪切过去。
- 若想将一个DOM元素从一个位置移动到另外一个位置,不需要先删除该节点再将该节点新增到新的位置,而只需要查找到该元素,然后直接在目标位置的父元素上直接调用
参考:
[1] MDN:https://developer.mozilla.org/zh-CN/
[2] CanIUse:https://caniuse.com/
[3] W3C:http://www.w3school.com.cn/htmldom/index.asp
[4] 博客园:https://www.cnblogs.com/yinshuige/p/5812095.html