DOM1级主要定义的是HTML和XML文档的底层结构。DOM2和DOM3级则在这个结构的基础上引入了更多的交互能力,也支持了更高级的XML特性。为此DOM2和DOM3级分为许多模块,这些模块如下:
DOM2级核心;
DOM2级视图;
DOM2级事件;
DOM2级样式;
DOM2级遍历和范围;
DOM2级HTML。
一、DOM变化
DOM2级和3级的目的在于扩展DOM API,以满足操作XML的所有需求,同时提供更好的错误处理及特性检测能力。
1.针对XML命名空间的变化
有了XML命名空间,不同的XML文档的元素就可以混在一起,共同构成良好的文档,而不必担心发生命名冲突。
命名空间要使用xmlns特性来指定。XHTML的命名空间是http://www.w3.org/1999/xhtml,在任何格式良好XHTML页面中,都应该将其包含在<html>元素中,如下面的例子所示:
<html xmlns = “http://www.w3.org/1999/xhtml”>
<head>
<title>Example XHTML page</title>
</head>
<body>
Hello world!
</body>
</head>
对这个例子而言,其中的所有元素都默认被视为XHTML命名空间中的元素。想要明确地为XML命名空间创建前缀,可以使用xmlns后跟冒号,再后跟前缀,如下所示:
<xhtml:html xmlns:xhtml =”http://www.w3.org/1999/xhtml”>
<xhtml:head>
<xhtml:title>Example XHTML page</xhtml:title>
</xhtml:head>
<xhtml:body>
Hello world!
</xhtml:body>
</xhtml:html>
①Node类型的变化
在DOM2级中,Node类型包含下列特定于命名空间的属性。
localName:不带命名空间前缀的节点名称
namespaceURI:命名空间URI或者(在未指定的情况下是)null。
prefix:命名空间前缀或者(在未指定的情况下是)null。
DOM3级在此基础上更进一步,又引入了下列与命名空间有关的方法:
isDefaultNamespace(namespaceURI):在指定的namespaceURI是当前节点的默认命名空间的情况下返回true。
lookupNamespaceURI(prefix):返回给定prefix的命名空间。
lookupPrefix(namespaceURI):返回给定namespaceURI的前缀。
②Document类型的变化
DOM2级中的Document类型也发生了变化,包含了下列与命名空间有关的方法:
createElementNS(namespaceURI,tagName):使用给定的tagName创建一个属于命名空间namespaceURI的新元素。
createAttributeNS(namespaceURI,attributeName):使用给定的attributeName创建一个属于命名空间namespaceURI的新特性。
getElementsByTagNameNS(namespaceURI,tagName):返回属于命名空间namespaceURI的tagName元素的NodeList。
③Element类型的变化
DOM2级核心中有关Element的变化,主要涉及操作特性。新增的方法如下:
getAttributeNS(namespaceURI,localName):取得属于命名空间namespaceURI且名为localName的特性。
getAttributeNodesNS(namespaceURI,localName):取得属于命名空间namespaceURI且名为localName的特性节点。
getElementsByTagNameNS(namespaceURI,localName):返回属于命名空间namespaceURI的tagName元素的NodeList。
has AttributeNS(namespaceURI,localName):确定当前元素是否有一个名为localName的特性,而且该命名空间是namespaceURI。
removeAttributeNS(namespaceURI,localName):删除属于命名空间namespaceURI且名为localName的特性。
setAttributeNS(namespaceURI,qualifiedName,value):设置属于命名空间namespaceURI且名为qualifiedName的特性的值为value。
setAttributeNodeNS(attNode):设置属于命名空间namespaceURI的特性节点。
④NamedNodeMap类型的变化
namedNodeMap类型也新增了下列与命名空间有关的方法。由于特性是通过NamedNodeMap表示的,因此这些方法多数情况下只针对特性使用。
getNamedItemNS(namespaceURI,localname):取得命名空间namespaceURI且名为localName的项。
removeNamedItemNS(namespaceURI,localName):移除属于命名空间namespaceURI且名为localName的项。
setNamedItemNS(node):添加node,这个节点已经事先指定了命名空间信息。
2.其他方面的变化
①DocumentType类型的变化
DocumentType类型新增了3个属性:publicId、systemId和internalSubset。其中,前两个属性表示的是文档类型声明中的两个信息段,这两个信息段在DOM1级中是没有办法访问到的。
②Document类型的变化
Document类型的变化中唯一与命名空间无关的方法是importNode()。这个方法的用途是从一个文档中取得一个节点,然后将其导入到另一个文档,使其成为这个文档结构的一部分。
importNode()方法与Element的cloneNode方法非常相似,它接受两个参数:要复制的节点和一个表示是否复制子节点的布尔值。返回的结果是原来节点的副本,但能够在当前文档中使用。
③Node类型的变化
Node类型中唯一与命名空间无关的变化,就是添加了isSupported()方法。与DOM1级为document.implementation引入的hasFeature()方法类似,isSupported()方法用于确定当前节点具有什么能力。这个方法也接受相同的两个参数:特性名和特性版本号。如果浏览器实现了相应特性,而且能够基于给定节点执行该特性,isSupported()就返回true。
DOM3级引入了两个辅助比较节点的方法:isSameNode()和isEqualNode()。这两个方法都接受一个节点参数,并在传入节点与引用的节点相同或相等时返回true。所谓相同,指的是两个节点引用的是同一个对象。所谓相等,指的是两个节点是相同的类型,具有相等的属性,而且它们的attributes和childNodes属性也相等。
DOM3级还针对DOM节点添加额外数据引入了新方法。其中,setUserData()方法会将数据指定给节点,它接受3个参数:要设置的键、实际的数据和处理的函数。以下代码可以将数据指定给一个节点。
document.body.setUserData(”name”,”Nicholas”,function(){ });
然后,使用getUserData()并传入相同的键,就可以获得该数据,如下所示:
var value=document.body.getUserData(”name”);
传入setUserData()中的处理函数会在带有数据的节点被复制、删除、重命名或引入一个文档时调用,因而你可以事先决定在上述操作发生时如何处理用户数据。处理函数接受5个参数:表示操作类型的数值(1表示复制、2表示导入、3表示删除、4表示重命名)、数据键、数据值、源节点和目标节点。在删除节点时,源节点是null;除在复制节点时,目标节点均为null。在函数内部,你可以决定如何存储数据。
④框架的变化
框架和内嵌框架分别用HTMLFrameElement和HTMLFrameElement表示,它们在DOM2级中都有了一个新属性,名叫contentDocument。这个属性包含一个指针。指向表示框架内容的文档对象。
二、样式
1.访问元素的样式
任何支持style特性的HTML元素在javascript中都有一个对应的style属性。这个style对象是CSSStyleDeclaration的实例,包含着通过HTML的style特性指定的所有样式信息,但不包含与外部样式表嵌入样式表经层叠而来的样式。在style特性中指定的任何CSS属性都将表现为这个style对象的相应属性。对于使用短划线的CSS属性名,必须将其转换成驼峰大小写形式,才能通过javascript来访问。例如:CSS中的background-image属性对应的javascript属性为style.backgroundImage。
多数情况下,都可以通过简单地转换属性名的格式来实现转换。其中一个不能直接转换的CSS属性就是float。由于float是javascript中的保留字因此不能用作属性名。
①DOM样式属性和方法
“DOM2级样式”规范还为style对象定义了一些属性和方法。这些属性和方法提供元素的style特性值的同时,也可以修改样式。下面列出了这些属性和方法:
cssText:如前所述,通过它能够访问到style特性中的CSS代码。
length:应用给元素的CSS属性的数量。
parentRule:表示CSS信息的CSSRule对象。
getPropertyCSSValue(propertyName):返回包含给定属性值的CSSValue对象。
getPropertyPriority(propertyName):如果给定的属性使用了!important设置,则返回“!important”;否则,返回空字符串。
getPropertyValue(propertyName):返回给定属性的字符串。
item(index):返回给定位置的CSS属性的名称。
removeProperty(propertyName):从样式中删除给定属性。
setProperty(propertyName,value,priority):将给定属性设置为相应的值,并加上优先权标志(“important”或者一个空字符串)。
②计算的样式
“DOM2级样式”增强了document.defaultView,提供了getComputedStyle()方法。这个方法接收两个参数:要取得计算机样式的元素和一个伪元素字符串,如“:after”,如果不需要伪元素信息,第二个参数可以是null。返回一个CSSStyleDeclaration对象,其中包含当前元素的所有计算的样式。例如:
<!DOCTYPE html>
<html>
<head>
<title>Computed Style Example</title>
<style type="text/css">
#myDiv{
background-color: blue;
width: 100px;
height: 200px;
}
</style>
</head>
<body>
<div id="myDiv" style="background-color:red;border:1px solid black"></div>
</body>
</html>
以下代码取得这个元素计算后的样式:
var myDiv=document.getElementById(“myDiv”) ;
var computedStyle = document.defaultView.getComputedStyle(myDiv , null) ;
alert(computedStyle.backgroundColor) ; // ”red”
alert(computedStyle.width) ; // “100px”
alert(computedStyle.height) ; //”200px”
alert(computedStyle.border) ; //在某些浏览器中是”1px solid black”
需要注意的是:所有计算的样式都是只读的;不能修改计算后样式对象中的CSS属性。此外,计算后的样式也包含属于浏览器内部样式表的样式信息,因此任何具有默认值的CSS属性都会表现在计算后的样式中。
2.操作样式表
CSSStyleSheet类型表示的是样式表,以下代码可以确定浏览器是否支持DOM2级样式表:
var supportsDOM2StyleSheets =
document.implementation.hasFeature(“StyleSheets”, “2.0”) ;
CSSStyleSheet继承自StyleSheet,后者可以作为一个基础接口来定义非CSS样式表。
①CSS规则
CSSRule对象表示样式表中的每一条规则。CSSRule是一个供其他多种类型继承的基类型,其中最常见的就是CSSStyleRule类型,表示样式信息。
②创建规则
DOM规定,要向现有样式表中添加新规则,需要使用insetRule()方法。这个方法接受两个参数:规则文本和表示在哪里插入规则的索引。下面是一个例子:
sheet.insertRule(“body { background-color : silver }” , 0 ) ; //DOM方法。
这个例子插入的规则会改变元素的背景颜色。插入的规则将成为样式表中的第一条规则。
IE及更早版本支持一个类似的方法,名叫addRule(),也接受两必选参数:选择符文本和CSS样式信息;一个可选参数:插入规则的位置。例如:
sheet.addRule(“body” , “background-color : silver” , 0 ) ; // 仅对IE有效
要以跨浏览器的方式向样式表中插入规则,可以使用下面的函数。这个函数接受4个参数:要向其中添加规则的样式表以及与addRule()相同的3个参数。下面是调用这个函数的代码:
insertRule(document.styleSheets[0] , “body”, “background-color : silver” , 0) ;
③删除规则
从样式表中删除规则的方法是deleteRule(),这个方法接受一个参数:要删除的规则的位置。例如,要删除样式表中的第一条规则,可以使用以下代码:
sheet.deleteRule(0); //DOM方法
IE支持类似的方法叫做removeRule(),使用方法相同,如下所示:
sheet.removeRule(0); //仅对IE有效
下面是一个能够跨浏览器删除规则的函数,第一个参数是要操作的样式表,第二个参数是要删除的规则的索引。
调用这个方法如下:
deleteRule(document.styleSheets[0] , 0) ;
3.元素大小
①偏移量
元素的可见大小由其高度、宽度决定,包括所有内边距、滚动条和边框大小。通过下列4个属性可以取得元素的偏移量:
offsetHeight:元素在垂直方向上占用的空间大小,以像素计。包括元素的高度、水平滚动条的高度、上边框高度和下边框高度。
offsetWidth:元素在水平方向上占用的空间大小,以像素计。包括元素的宽度、垂直滚动条的宽度、左边宽度和右边框宽度。
offsetLeft:元素的左外边框至包含元素的左内边框之间的像素距离。
offsetTop:元素的上外边框至包含元素的上内边框之间的像素距离。
其中,offsetLeft和offsetTop属性与包含元素有关,包含元素的引用保存在offsetParent属性中。offsetParent属性不一定与parentNode的值相等。
②客户区大小
元素的客户区大小,指的是元素内容及其内边距所占据的空间大小。有关客户区大小的属性有两个:clientWidth和clientHeight。其中,clientWidth属性时元素内容区宽度加上左右内边距宽度;clientHeight属性时元素内容区高度加上上下内边距高度。
③滚动大小
指的是包含滚动内容的元素的大小。以下是4个与滚动大小相关的属性
scrollHeight:在没有滚动条的情况下,元素内容的总高度。
scrollWidth:在没有滚动条的情况下,元素内容的总宽度。
scrollLeft:被隐藏在内容区域左侧的像素数。通过设置这个属性可以改变元素的滚动位置。
scrollTop:被隐藏在内容区域上方的像素数。通过设置这个属性可以改变元素的滚动位置。
scrollWidth和scrollHeight主要用于确定元素内容的实际大小。
④确定元素大小
getBoundingClientRect()方法。这个方法会返回一个矩形对象,包含4个属性:left、top、right、bottom。这些属性给出了元素在页面中相对于视口的位置。
三、遍历
“DOM2级遍历和范围”模块定义了两个用于辅助完成顺序遍历DOM结构的类型:NodeIterator和TreeWalker。这两个类型能够基于给定的起点对DOM结构执行深度优先的遍历操作。遍历以给定节点为根,不可能向上超出DOM树的根节点。
1.NodeIterator
Nodelterator类型是两者中比较简单的一个,可以使用document.createNodeIterator()方法创建它的新实例。这个方法接受4个参数:
root:想要作为搜索起点的树中的节点。
whatToShow:表示要访问哪些节点的数字代码
filter:是一个NodeFilter对象,或者一个表示应该接受还是拒绝某种特定节点的函数。
entityReferenceExpansion:布尔值,表示是否要扩展实体引用。
whatToShow参数是一个位掩码,通过应用一或多个过滤器来确定要访问哪些节点。
Nodelterator类型的两个方法是nextNode()和previousNode()。顾名思义,在深度优先的DOM子树遍历中,nextNode()方法用于向前前进一步,而previousNode()用于向后后退一步。
2.TreeWalker
TreeWalker是NodeIterator的一个更高级的版本。除了包括nextNode()和previousNode()在内的相同的功能之外,这个类型还提供了下列用于在不同方向上遍历DOM结构的方法:
parentNode():遍历到当前节点的父节点;
firstChild():遍历到当前节点的第一个子节点;
lastChild():遍历到当前节点的最后一个子节点;
nextSibling():遍历到当前节点的下一个同辈节点;
previousSibling():遍历到当前节点的上一个同辈节点。
创建TreeWalker对象要使用document.createTreeWalker( )方法,这个方法接受4个参数与document.createNodeIterator( )方法相同:作为遍历起点的根节点、要显示的节点类型、过滤器和一个表示是否扩展实体引用的布尔值。由于这两个创建方法很相似,所以很容易用TreeWalker来代替NodeIterator。
4.范围
(1)DOM中的范围
DOM2级在Document类型中定义了createRange( )方法。在兼容DOM的浏览器中,这个方法属于document对象。使用hasFeature()或者直接检测该方法,都可以确定浏览器是否支持范围。如果浏览器支持范围,那么就可以使用createRange()来创建DOM范围。
①用DOM范围实现简单选择
要使用范围来选择文档中的一部分,最简单的方式就是使用selectNode()或selectNodeContents()。这两个方法都接受一个参数:一个DOM节点。其中,selectNode()方法选择整个节点,包括其子节点;而selectNodeContents()方法则只选择节点的子节点。
②用DOM范围实现复杂选项
要创建复杂范围就得使用setStart()和setEnd()方法。这两个方法都接受两个参数:一个参照节点和一个偏移量值。对setStart()来说,参照节点会变成:startContainer,而偏移量值会变成startOffset。对于setEnd()来说,参照节点会变成endContainer,而偏移量值会变成endOffset。
③操作DOM范围中的内容
在创建范围时,内部会为这个范围创建一个文档片段,范围所属的全部节点都被添加到了这个文档片段中。为了创建这个文档片段,范围内容的格式必须正确有效。
第一个方法:deleteContents()。这个方法能够从文档中删除范围所包含的内容。例如:
var p1 = document.getElementById(“p1”) ;
helloNode = p1.firstChild.firstChild ;
worldNode = p1.lastChild ;
range = document.createRange() ;
range = setStart(helloNode , 2 ) ;
range = setEnd(worldNode , 3 ) ;
range.deleteContents( ) ;
执行以上代码后,页面中会显示如下HTML代码:
<p><b>He</b>rld!</p>
与deleteContents()方法相似,extractContents()也会从文档中移除范围选区。但这两个方法的区别在于,extractContents()会返回范围的文档片段。利用这个返回值,可以将范围的内容插入到文档中的其他地方。
还有一种做法,即使用cloneContents()创建范围对象的一个副本,然后在文档的其他地方插入该副本。
④插入DOM范围中的内容
使用insertNode()方法可以向范围选区的开始处插入一个节点。
使用surroundContents()方法可以环绕范围插入内容,这个方法接收一个参数,即环绕范围内容的节点,在环绕范围插入内容时,后台会执行下列步骤:
提取出范围中的内容;
将给定节点插入到文档中原来范围所在的位置上;
将文档片段的内容添加到给定节点中。
⑤折叠DOM范围
所谓折叠范围, 就是指范围中未选择文档的任何部分。
使用collapse()方法来折叠范围,这个方法接收一个参数,一个布尔值,表示要折叠到范围的哪一端。参数true表示折叠刀范围的起点,参数false表示折叠刀范围的终点。要确定范围已经折叠完毕,可以检查collapsed属性,如下所示:
range.collapse(true) ; //折叠到起点
alert(range.collapsed) ; //输出true
⑥比较DOM范围
在多个范围的情况下,可以使用compareBoundaryPoints()方法来确定这些范围是否有公共的边界。这个方法接受两个参数:表示比较方式的常量值和要比较的范围。
⑦复制DOM范围
可以使用cloneRange()方法复制范围。这个方法会创建调用它的范围的一个副本。
var newRange = range.cloneRange();
新创建的范围与原来的范围包含相同的属性,而修改它的端点不会影响原来的范围。
⑧清理DOM范围
使用完范围之后,最好是调用detach()方法,以便从创建范围的文档中分离出该范围。调用detach()之后,就可以放心地解除对范围的引用,从而让垃圾回收机制回收其内存了。
(2)IE8及更早版本的范围
①用IE范围实现简单的选择
选择页面中某一区域的最简单方式,就是使用范围的findText()方法。这个方法会找到第一次出现的给定文本,并将范围移过来以环绕该文本。
②使用IE范围实现复杂的选择
在IE中创建复杂范围的方法,就是以特定的增量向四周移动范围。为此,IE提供了4个方法:move()、moveStart()、moveEnd()、expand()。这些方法都接受两个参数:移动单位和移动单位的数量。
③操作IE范围中的内容
在IE中操作范围中的内容可以使用text属性或pasteHTML()方法。如前所述,通过text属性可以取得范围中的内容文本;但是,也可以通过这个属性设置范围中的内容文本。
④折叠IE范围
IE为范围提供的collapse()方法与相应的DOM方法用法一样。
⑤比较IE范围
IE中的compareEndPoints()方法与DOM范围的compareBoundaryPoints()方法类似。这个方法接受两个参数:比较的类型和要比较的范围。
⑥复制IE范围
在IE中使用duplicate()方法可以复制文本范围,结果会创建原范围的一个副本,如下面的例子所示:
var newRange = range.duplicate() ;
新创建的范围会带有与原范围完全相同的属性。