第十四章:DOM
文档对象模型(Document Object Model)是HTML 和 XML文档的编程接口;就是开发者可以通过JavaScript编程,添加、删除、修改页面的各个部分;
14.1 节点层级
任何HTML\XML文档都可以表示为由节点构成的层级结构。
<html>
<head>
<title>Sample Page</title>
</head>
<body>
<p>Hello World!</p>
</body>
</html>

在HTML中,文档元素(document)每个文档只能拥有一个,而且是最外层元素,其有且只有一个直接子元素
<html>
14.1.1 Node类型
DOM Level 1描述了Node的接口,这个接口是所有DOM节点类型都必须实现的;
Node接口在JavaScript
中被实现为Node类型。
在JavaScript中,所有节点类型都继承Node类型,一共有12种类型并用12个常量表示
(也可以使用其中的数字代替):
-
Node.ELEMENT_NODE(1)
-
Node.ATTRIBUTE_NODE(2)
-
Node.TEXT_NODE(3)
-
Node.CDATA_SECTION_NODE(4)
-
Node.ENTITY_REFERENCE_NODE(5)
-
Node.ENTITY_NODE(6)
-
Node.PROCESSING_INSTRUCTION_NODE(7)
-
Node.COMMENT_NODE(8)
-
Node.DOCUMENT_NODE(9)
-
Node.DOCUMENT_TYPE_NODE(10)
-
Node.DOCUMENT_FRAGMENT_NODE(11)
-
Node.NOTATION_NODE(12)
节点信息
- nodeName:存储着节点的名字,如div、img
- nodeValue:存储着节点的值,若为标签,则值为
null
- nodeType:存储着节点类型,对应着上面的12种类型
节点关系
文档中各节点都具有关系,相当于一个族谱,主要为父元素和子元素两种区别;
childNodes
:每个节点都拥有该属性,该属性包含一个NodeList
实例,是一个类数组对象,有序存储着该节点的所有直接子节点。NodeList
实例是实时的活动对象,这就意味着,DOM树改变、NodeList也会改变,反之亦然;- 由于是一个类数组对象,所以可以通过
[]
访问其中元素; - 也可以通过
length
属性,获取子元素个数。
- 由于是一个类数组对象,所以可以通过
parentNode
:指向父节点previousSibling
:指向前一个兄弟节点,childNodes[0].previousSibling
为null
nextSibling
:指向后一个兄弟节点,childNodes[childNodes.length-1].nextSibling
为null
firstChild/lastChild
:指向第一个子节点、最后一个子节点hasChildNodes()
:返回true,表示有一个或多个节点;ownerDocument
:指向文档中唯一的文档节点(document)。
操纵节点
添加节点:
-
appendChild():向尾部追加一个节点;接收一个参数:
- 需要添加的节点
-
insertBefore():在参照节点前插入一个节点;接收两个参数:
- 需要添加的节点;
- 参照节点;
如果参照节点为
null
,则效果与appendChild()
一样; -
replaceChild():替换目标节点;接收两个参数:
- 要插入的节点
- 要替换的节点
被替换的节点,会完全消失在DOM树中。
删除节点:
-
removeChild():删除一个节点;接收一个参数:
- 要删除的节点
会返回被删除的节点
克隆节点:
-
cloneNode():克隆一个节点,接收一个参数:
- 布尔值,表示是否深克隆;如果为
true
,则克隆该节点以及其子节点。
返回被克隆的节点;如果被克隆的节点没有指定父节点,则称为孤儿节点。
注意,该克隆方法,不会克隆节点的
JavaScript
属性,如时间处理程序,只复制HTML
属性。 - 布尔值,表示是否深克隆;如果为
14.1.2 Document类型
JavaScript
中表示文档节点类型。在浏览器中,document
是HTMLDocument
的实例,表示整个页面;
document对象是只读的,一般不会存在诸如appendChild()
等方法
Document类型有如下特征:
- nodeType等于9;
- nodeName值为"#document";
- nodeValue值为null;
- parentNode值为null;
- ownerDocument值为null;
- 子节点可以是DocumentType(最多一个) 、 Element(最多一个) 、 ProcessingInstruction或Comment类型。
文档子节点:
document提供了三个访问子节点的快捷方式:
- document.documentElement:始终指向
<html>
元素 - document.body:始终指向
<body>
元素 - document.doctype:始终指向
<!DOCTYPE>
元素
文档信息
document提供了浏览器加载网页的信息
- document.title:获取
<title>
标签的内容,可修改,并会渲染到网页中。 - document.URL:获取当前页面的URL
- document.domain:获取当前页面的域名
- document.referrer:获取当前页面来源
注意:
-
URL和domain是相关的,若URL为
https://www.baidu.com/search=xxxx
,则域名为www.baidu.com
; -
后面三个属性中,只有
domain
是可设置的。处于安全考虑,domain
只能设置为URL中的子集:// 当前URL为:www.p2p.baidu.com document.domain = 'p2p.baidu'; // 成功 document.domain = 'baidu.com'; // 成功 document.domain = 'hahaha.net'; // 失败
-
当页面包含来自不同子域的窗格(
<frame>
)或内嵌窗格(<iframe>
)时,设置domain
是有用的。因为跨源通信存在安全隐患;所以可以把每个页面上的domain
都设置为相同的值,就可以访问对方的JavaScript对象。 -
domain
属性,一旦收紧了,就不能再放松了// 当前URL为:www.p2p.baidu.com document.domain = 'p2p.baidu.com'; // 放松了,成功 document.domain = 'baidu.com'; // 继续放松了,成功 document.domain = 'p2p.baidu.com'; // 再次收紧了,错误!
定位元素
document提供了定位元素的方法
- getElementById():根据id属性获取节点。因为id是唯一的,所以返回第一个找到的。
- getElementsByClassName():根据class属性获取节点。
- getElementsByTagName():根据标签名获取节点。
- getElementsByName():根据name属性获取节点。
除了第一个方法,其余的都是返回一个HTMLCollection
对象;该对象也是类数组对象。
特殊集合
- document.anchors包含文档中所有带name属性的
<a>
元素; - document.forms包含文档中所有
<form>
元素; - document.images包含文档中所有
<img>
元素; - document.links包含文档中所有带href属性的
<a>
元素。
文档写入
- write():向文档输入信息,接收一个参数:输入内容
- writeln():上前者一致,只不过会自动添加换行符
注意:
-
这两个方法需要只能在页面渲染器使用,如果渲染完毕在调用,内容会覆盖整个页面
-
当使用这个方法动态写入外部资源时,需要注意:
<html> <head> <title>document.write() Example</title> </head> <body> <script type="text/javascript"> document.write("<script type=\"text/javascript\" src=\"file.js\">" + "<\/script>");</script> </body> </html>
在输出内容的第二个script前面,必需加上转义字符,不然会匹配错误;
14.1.3 Element类型
除Document类型外,Element类型就是Web开发中最常用的类型;Elment表示HTML元素,对外暴露访问元素标签名、子节点和属性的能力。
Element类型具有以下特征:
- nodeType等于1;
- nodeName值为元素的标签名;
- nodeValue值为null;
- parentNode值为Document或Element对象;子节点可以是Element、 Text、 Comment、ProcessingInstruction、 CDATASection类型。
特征提取
nodeName
或tagName
属性都可以获取Elment
元素的标签名,且在HTML
中,元素标签名始终以全大写表示;
为了防止提取标签名的时候,大小写混乱,推荐如下操作:
if(element.tagName.toLowerCase() == 'div'){
// do something
}
HTML元素
所有HTML元素,都是通过HTMLElement
类型标识,包括直接实例和间接实例;
HTMLElement
直接继承自Element,并增加了一些标准属性:
id
, 元素在文档中的唯一标识符;title
, 包含元素的额外信息, 通常以提示条形式展示;lang
, 元素内容的语言代码(很少用) ;dir
, 语言的书写方向("ltr"表示从左到右, "rtl"表示从右到左, 同样很少用) ;className
, 相当于class属性, 用于指定元素的CSS类(因为class是ECMAScript关键字, 所以不能直接用这个名字)。
可以直接获取、修改这些值:
// <div id='myDiv' class='myClass' title='hello world'>
let div = document.getElementById('myDiv');
console.log(div.class); // myClass
div.class = 'myLess';
console.log(div.class); // myLess
修改这些属性会对页面产生影响。
操作属性
每个元素有零个或多个属性,我们可以通过如下三个方法来操作属性:
- getAttribute():获取属性,接收一个参数:属性名
- setAttribute():设置属性,接收两个参数:属性名,属性值
- removeAttribute():移除属性,接收一个参数:属性名
注意:以上说所三种操作属性的方法:
- 既可以操作标准属性(又称:公认属性)、也可以操作自定义属性;
- 属性名不区分大小写
- 在HTML5的规范中,,自定义属性需要加
data-
为前缀,方便验证; - 自定义属性不会称为DOM对象属性,即不会称为公认属性或标准属性;
- 不仅仅可以获取一般属性,还可以获取事件属性:如onclick;
- 如果使用
getAttribute()
来获取事件属性值,则以字符串的形式返回源代码;通常直接访问事件属性:element.onclick
,则返回一个函数; - 开发者一般使用
getAttribute()
来访问自定义属性,而公认属性可以直接访问:element.id
attributes属性
attributes
属性与childNodes
属性类似,也是类似NodeList
的实时集合,每个属性表示为Attr节点,保存在NamedNodeMap
对象中,其包含如下方法:
getNamedItem(name)
, 返回nodeName属性等于name的节点;removeNamedItem(name)
, 删除nodeName属性等于name的节点;setNamedItem(node)
, 向列表中添加node节点, 以其no