1. 概念
DOM(文档对象模型)是针对HTML和XML的一个API(应用程序编程接口)。
1998年10月DOM1级规范成为W3C的推荐标准。
本章主要讨论与浏览器HTML页面相关的DOM1级特性和应用,以及JavaScript对DOM1级的实现。
注意:IE中的所有DOM对象都是以COM对象的形式实现的,所以很多DOM行为跟其他浏览器不一致
2. 节点层次
文档节点<document>是每一个文档的根节点。
文档节点的子节点可以有文档类型节点(如<!DOCTYPE>)、元素节点(如<html>)、注释节点(如<!--注释-->)这三种类型。其中在HTML页面中<html>称之为文档元素,包含了其他所有元素。
2.1 Node类型
DOM的节点类型有12种,但都是继承自DOM1定义的Node接口,在JavaScript中用同名的Node类型实现,所有节点都是继承自Node类型,所以所有的节点类型会有一些共有的属性和方法。注意:除了在IE中,其他浏览器都可以访问Node类型。
2.1.1 共有的属性和方法
- nodeType
- nodeName
- nodeValue
- ownerDocument
- hasChildNodes()
- cloneNode()
- normalize()
nodeType表示节点的类型,Node类型定义了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_FRAFGMENT_NODE(11)
- Node.NOTATION_NODE(12) //符号节点
其中,括号中的数值即对应类型在nodeType中的数值表示
再次提醒,IE中没有公开Node类型的构造函数,因此Node的常量值在IE中是会报错的,这时候使用括号中的数值表示是最合适的,如
/*检查节点的类型*/
if (someNode.nodeType == 1) { //适用于所有浏览器
console.log("this node is an element");
}
nodeName表示节点名字,如果是元素节点,则直接给出标签名(大写形式),例如:
<!--nodeName-->
<!DOCTYPE html>
<html>
</html>
var commentNode = document.childNodes[0];
var docTyoeNode = document.childNodes[1];
var elementNode = document.childNodes[2];
commentNode.nodeName; //#comment
docTypeNode.nodeName; //html
elementNode.nodeName; //HTML
nodeValue在元素节点中值为null,其他类型节点尚且没去研究。
ownerDocument指向文档节点<document>,让任何节点都可以直接访问其文档节点。
hasChildNodes(),没有参数;有一个或者多个子节点则返回true,否则返回false。该方法用于查看是否有子节点。
cloneNode(isDeep),参数是true/false,表示是否深复制;返回复制的节点。该方法用于复制某个节点。
当参数为true,表示深复制,将复制该节点及其整个子节点树;当参数为false,则只复制该节点。
normalize(),用于处理文档树种的文本节点。调用该方法,将查询该节点的后代节点是否有空文本节点,有则删除它;是否有相邻的文本节点,有则将它们合并为一个文本节点。
2.1.2 其他属性和方法
- childNodes
- parentNode
- firstNode
- lastNode
- previousSibling
- nextSibling
所有的元素节点都有以上属性,但是不是所有节点类型都有
childNodes保存着所有子节点的引用,是一个NodeList对象(类数组对象,按序保存存储所有子节点)
NodeList是对DOM结构动态查询的结果,即具有实时性;访问NodeList中的元素可以使用方括号,可以用特有的item()方法,如下:
var firstNode = someNode.childNodes[0]; //第一个子节点,括号形式更常用
var length = someNode.childNodes.length; //子节点个数
var lastNode = someNode.childNodes.item(length - 1); //最后一个子节点
parentNode直接指向父节点。
firstNode和astNode分别指向第一个子节点和最后一个子节点。
previousSibling和nextSibling分别表示指向上一个兄弟节点和下一个兄弟节点(同胞节点)
2.1.3 常用操作节点的方法
- appendChild()
- insertBefore()
- replaceChild()
- removeChild()
appendChild(newNode)参数是新增的节点;返回值是新增的节点。该方法用于向childNodes列表的末尾添加一个节点。
注意:如果参数node是文档中已经存在的节点,则将该节点从原来的位置转移到childNodes列表末尾。
insertBefore(insertNode, referNode),参数1是要插入的节点,参数2是参照节点;返回要插入的节点。该方法用于调整节点的位置,将插入节点插入到参照节点的前面,成为其同胞节点,但是以下操作则成为子节点:
/*插入后成为最后一个子节点,等同于appendChild*/
returnNode = insertBefore(insertNode, null);
/*插入后成为第一个子节点*/
returnNode = insertBefore(insertNode, someNode.firstNode);
replaceChild(insertNode, replaceNode),参数1是要插入的节点,参数2是被替换的节点;返回被替换的节点。该方法用于替换某个节点。
removeChild(removeNode),参数是被移除的节点;返回被移除的节点。该方法用于移除某个节点。
注意:
- repalceChild()和removeChild()方中被替换和移除的节点仍然存在文档中,只是没有了位置,仍然可以使用引用。
- 这些操作方法都基于操作子节点,若没有子节点的节点类型使用这些操作方法,则会报错。
2.2 Document类型
Document类型可以表示HTML或XML的文档。
在JavaScript中用同名Document类型来表示文档。
在浏览器中,document对象是HTMLDocument(继承自Document类型)的一个实例,表示整个HTML页面,同时也是window对象的一个属性对象,可作为全局对象使用。
文档节点<document>有以下特性:
- nodeType值为9
- nodeName值为"#document"
- parentNode、nodeValue、ownerDocument值为null
- 子节点类型只能是文档类型节点(最多一个)、元素节点(如<html>,最多一个)、ProcessingInstruction或者注释节点(可以有多个)
2.2.1 获取文档的子节点
前文已经两次介绍文档节点子节点的类型,这里不赘诉。
DOM标准规定中内置有访问文档节点的子节点的快捷方式,两个属性是(所有浏览器都支持):
- documentElement
- body
documentElement直接指向文档中<html>元素节点。
body直接指向文档中<body>元素节点。
另外一个属性是doctype(使用较少),doctype直接指向文档中的文档类型节点。浏览器支持情况如下:
- IE8及之前版本,该属性值为null,并且会将文档类型节点当成一个注释节点。
- IE9+、FireFox及Chorme,该属性值为第一个文档类型节点,同时会将文档节点作为第一个子节点。
2.2.2 获取文档信息
document对象以下属性存储着文档的信息:
- title
- URL
- domain
- referrer
var title = document.title; //获取文档标题
title = "test title of document"; //设置文档标题
/*与网页请求相关的属性*/
var url = document.URL; //获取完整的URL
var domain = document.domain; //获取域名
var referrer = document.referrer; //获取上一个页面的url
注意:与网页请求相关的三个属性其实都取值自HTTP报头部,用于我们方便在js中访问(location对象也有类似属性可以使用)
2.2.3 获取元素
document对象提供了获取文档中元素的方法:
- getElementById()
- getElementsByTagName()
- getElementsByName()
- getElementsByClassName()
前两个方法是Document类型规定给出的方法,后两个是HTMLDocument特有的方法。
getElementByID(id),参数是元素节点的id值;返回匹配的元素。
注意:
- 这里ID属性区别大小写,但是IE8-不区分ID属性的大小写
- 当有多个相同的ID值匹配时,返回第一个匹配的元素
getElementsByTagName(tagName),参数是元素的标签名;返回一个HTMLCollection对象(与NodeList对象特性类似),保存着匹配的元素集合,访问对象中元素的方法如下:
var images = document.getElementsByTagName("img");
console.log(images[0].src); //查看第一张图片的src,方括号方法
console.log(images.item(0).src); //item()方法
console.log(images.namedItem("apple")); // 通过name属性匹配,该对象特有的
console.log(images.length); //集合的大小
/*获取文档中所有元素*/
var elments = document.getElementsByTagName("*"); //在IE中注释节点会被解释成元素,所以也会返回
getElementsBy(name),参数是元素的name属性值;返回一个HTMLCollection对象。常用于表单。
getElementsByClassName(className),参数是元素的class属性值;返回一个HTMLCollection对象。
注意:
- getElementsByTagName()不区分大小写,但是在XHTML中会区分大小写。
- 后两个方法访问对象中元素的方法参照getElementsByTagName()
2.2.4 特殊的属性集合
document对象还提供了一些特殊的属性,这些属性都是HTMLCollection对象,用以快速的访问文档中的元素,包括:
- anchors,包含文档中所有带name属性的<a>元素
- forms,包含文档中所有的<form>元素
- images,包含文档中所有的<img>元素
- links,包含文档中所有带href属性的<a>元素
2.2.5 DOM一致性检测
检查浏览器实现的DOM级别,通过document.implementation可以查看,查看方法:
- document.implementation.hasFeature()
hasFeature(domFunc, ver),参数1是要检查的DOM功能,参数2是该功能的版本号;若浏览器支持给定的功能和版本号则返回true。例子如:
var hasXmlDom = document.implementation.hasFeature("XML", "1.0");
考虑到hasFeature()方法并不能一定保证检查的结果,所以为了检测浏览器的功能,还是要进行能力检测(查看客户端检测章节)
2.2.6 文档写入
document对象还有一个重要的功能,就是将输出流写入到网页中。相关的方法有:
- write()
- writeln()
- open()
- close()
方法功能一眼就能明白,下面说两个例子:
/*通常要为写入的文档创建一个标签包裹起来,方便后面对其进行操作*/
document.write("<div id = 'newText'>smash boom boom</div>");
/*动态引入js文件注意要使用转义字符"\",否则会与第一个<scirpt>标签匹配而结束>*/
<script type="text/javascript">
document.write("<script><\/script>")
</script>
2.3 Element类型
Element类型是Web编程中另外一个常用的类型,提供了对元素标签名、子节点及特性的访问。
2.3.1 元素属性和特性的区别和联系
有在HTML页面中出现的和通过setAttribute()设置的都属于特性(Attribute)。
一些公认的、常用的特性以及通过字面量设置的(如div.color = "red")都属于属性(property)。
有些常用的特性如id、class、title、stytle、onclick等会在DOM对象中存储对应的属性,即这部分既是特性也是属性。可以说它们是”脚踏两条船“了。同时,它们在特性和属性的值会保持同步。
还有一个区别是,特性的名称是不区分大小写的。根据HTML5规范,自定义特性应该加上data-前缀。
2.3.2 获取标签名
除了nodeName,Element类型还有与其一样效果的属性:
- tagName
主要是为了在元素操作时更清楚的表示标签名。
注意:这两者在HTML中返回值都是大写,而在XML、XHTML中则返回与源代码保持一样的大小写形式,因此,在检测的时候最好是对获取到的标签名进行大小写转换,如:
if (element.tagName.toLowerCase == "div") {
alert("this element is a div")
}
另外,除了IE8-和safari2-外,所有浏览器都可以访问Element类型的原型和构造函数。
2.3.3 元素特性属性的获取和设置
所有的HTML元素都属于或者继承自HTMLElement类型。
设置属性的方法:
/*通过"."属性名字面量直接赋值*/
element.propertyName = antType
这种设置的属性不会有对应的特性生成,因此不能通过getAttribute()获得。
访问属性同理,注意属性值可以是任何类型。
有关特性的有三个方法:
- setAttribute()
- getAttribute()
- removeAttribute()
这三个方法可以应用于任何特性(包括自定义属性),同时传入的参数和返回值都是字符串类型。特性的介绍看前文。
setAttribute(atrName, atrValue):参数1是特性名(如果是大写会被转换成小写形式),参数2是特性值;无返回值。用于设置元素的特性。
getAttribute(atrName):参数是特性名;返回特性值。用于获取元素的特性。
removeAttribute(atrName):参数是特性名;无返回值。用于删除元素的特性。IE6-不支持该方法。
HTMLElement属性中既是属性又是特性的有:
- id
- title
- class
- style
- onclick
其中class特性对应的是className属性,这是由于在ESMAScript中class是保留字。
由于style和onclick的属性值分别是CSSStyleDeclaration对象和JS函数,而通过getAttribue()方法获得的都是字符串形式(除了IE7-)。
因此,由于存在以上差异,简单粗暴总结一下:
- 一般情况下直接使用元素对象的属性,即用"."属性名方式。
- 当需要获取自定义特性时使用getAttribute(),即html页面元素中非公认特性的特性,嗯!