文章目录
12 DOM2和DOM3
DOM1 级主要定义的是 HTML 和 XML 文档的底层结构。 DOM2 和 DOM3 级则在这个结构的基础上引入了更多的交互能力,也支持了更高级的 XML 特性。
12.1 DOM变化
DOM2 级和 3 级的目的在于扩展 DOM API,以满足操作 XML 的所有需求,同时提供更好的错误处理及特性检测能力。
var supportsDOM2Core = document.implementation.hasFeature("Core", "2.0");
var supportsDOM3Core = document.implementation.hasFeature("Core", "3.0");
var supportsDOM2HTML = document.implementation.hasFeature("HTML", "2.0");
var supportsDOM2Views = document.implementation.hasFeature("Views", "2.0");
var supportsDOM2XML = document.implementation.hasFeature("XML", "2.0");
12.1.1 针对XML命名空间的变化
“DOM2 级核心”通过为大多数 DOM1 级方法提供特定于命名空间的版本解决了这个问题。
- 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
//创建一个新的 SVG 元素
var svg = document.createElementNS("http://www.w3.org/2000/svg","svg");
//创建一个属于某个命名空间的新特性
var att = document.createAttributeNS("http://www.somewhere.com", "random");
//取得所有 XHTML 元素
var elems = document.getElementsByTagNameNS("http://www.w3.org/1999/xhtml", "*");
- Element类型的变化
“DOM2 级核心”中有关 Element 的变化,主要涉及操作特性。新增的方法如下。
- getAttributeNS(namespaceURI,localName):取得属于命名空间 namespaceURI 且名为localName 的特性。
- getAttributeNodeNS(namespaceURI,localName):取得属于命名空间 namespaceURI 且名为 localName 的特性节点。
- getElementsByTagNameNS(namespaceURI, tagName):返回属于命名空间 namespaceURI的 tagName 元素的 NodeList。
- hasAttributeNS(namespaceURI,localName):确定当前元素是否有一个名为 localName的特性,而且该特性的命名空间是 namespaceURI。注意, “DOM2 级核心”也增加了一个hasAttribute()方法,用于不考虑命名空间的情况。
- removeAttriubteNS(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,这个节点已经事先指定了命名空间信息。
12.1.2 其他方面的变化
- DocumentType 类型的变化
DocumentType 类型新增了 3 个属性: publicId、 systemId 和 internalSubset。其中,前两个属性表示的是文档类型声明中的两个信息段,这两个信息段在 DOM1 级中是没有办法访问到的。 - Document 类型的变化
Document 类型的变化中唯一与命名空间无关的方法是 importNode()。这个方法的用途是从一个文档中取得一个节点,然后将其导入到另一个文档,使其成为这个文档结构的一部分。 - Node 类型的变化
Node 类型中唯一与命名空间无关的变化,就是添加了 isSupported()方法。与 DOM1 级为 document.implementation 引入的 hasFeature()方法类似, isSupported()方法用于确定当前节点具有什么能力。这个方法也接受相同的两个参数:特性名和特性版本号。如果浏览器实现了相应特性,而且能够基于给定节点执行该特性, isSupported()就返回 true。
if (document.body.isSupported("HTML", "2.0")){
//执行只有"DOM2 级 HTML"才支持的操作
}
var div1 = document.createElement("div");
div1.setAttribute("class", "box");
var div2 = document.createElement("div");
div2.setAttribute("class", "box");
alert(div1.isSameNode(div1)); //true
alert(div1.isEqualNode(div2)); //true
alert(div1.isSameNode(div2)); //false
- 框架的变化
框架和内嵌框架分别用 HTMLFrameElement 和 HTMLIFrameElement 表示, 它们在 DOM2 级中都有了一个新属性,名叫 contentDocument。这个属性包含一个指针,指向表示框架内容的文档对象。在此之前,无法直接通过元素取得这个文档对象(只能使用 frames 集合)
var iframe = document.getElementById("myIframe");
var iframeDoc = iframe.contentDocument; //在 IE8 以前的版本中无效
由于 contentDocument 属性是 Document 类型的实例,因此可以像使用其他 HTML 文档一样使用它,包括所有属性和方法。IE8 之前不支持框架中
的 contentDocument 属性,但支持一个名叫 contentWindow 的属性,该属性返回框架的 window 对象,而这个 window 对象又有一个 document 属性。
var iframe = document.getElementById("myIframe");
var iframeDoc = iframe.contentDocument || iframe.contentWindow.document;
所有浏览器都支持 contentWindow 属性
12.2 样式
var supportsDOM2CSS = document.implementation.hasFeature("CSS", "2.0");
var supportsDOM2CSS2 = document.implementation.hasFeature("CSS2", "2.0");
12.2.1 访问元素的样式
任何支持 style 特性的 HTML 元素在 JavaScript 中都有一个对应的 style 属性。 这个 style 对象是 CSSStyleDeclaration 的实例,包含着通过 HTML 的 style 特性指定的所有样式信息,但不包含与外部样式表或嵌入样式表经层叠而来的样式。
CSS 属性名,必须将其转换成驼峰大小写形式,才能通过 JavaScript 来访问。
CSS属性 | JavaScript属性 |
---|---|
background-image | style.backgroundImage |
color | style.color |
display | style.display |
font-family | style.fontFamily |
其中一个不能直接转换的 CSS 属性就是 float。由于 float 是 JavaScript 中的保留字,因此不能用作属性名。“DOM2 级样式”规范规定样式对象上相应的属性名应该是 cssFloat; Firefox、 Safari、 Opera 和 Chrome 都支持这个属性,而 IE支持的则是 styleFloat。
var myDiv = document.getElementById("myDiv");
//设置背景颜色
myDiv.style.backgroundColor = "red";
//改变大小
myDiv.style.width = "100px";
myDiv.style.height = "200px";
//指定边框
myDiv.style.border = "1px solid black";
- DOM 样式属性和方法
“DOM2 级样式”规范还为 style 对象定义了一些属性和方法。这些属性和方法在提供元素的 style特性值的同时,也可以修改样式。下面列出了这些属性和方法。
- cssText:如前所述,通过它能够访问到 style 特性中的 CSS 代码。
- length:应用给元素的 CSS 属性的数量。
- parentRule:表示 CSS 信息的 CSSRule 对象。本节后面将讨论 CSSRule 类型。
- getPropertyCSSValue(propertyName):返回包含给定属性值的 CSSValue 对象。
- getPropertyPriority(propertyName):如果给定的属性使用了!important 设置,则返回"important";否则,返回空字符串。
- getPropertyValue(propertyName):返回给定属性的字符串值。
- item(index):返回给定位置的 CSS 属性的名称。
- removeProperty(propertyName):从样式中删除给定属性。
- setProperty(propertyName,value,priority):将给定属性设置为相应的值,并加上优先权标志( "important"或者一个空字符串)。
- 计算的样式
虽然 style 对象能够提供支持 style 特性的任何元素的样式信息,但它不包含那些从其他样式表层叠而来并影响到当前元素的样式信息。
<!DOCTYPE html>
<html>
<head>
<title>Computed Styles 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"
在这个元素计算后的样式中,背景颜色的值是"red",宽度值是"100px",高度值是"200px"。我们注意到,背景颜色不是"blue",因为这个样式在自身的 style 特性中已经被覆盖了。IE 不支持 getComputedStyle()方法,但它有一种类似的概念。在 IE 中,每个具有 style 属性的元素还有一个 currentStyle 属性。
var myDiv = document.getElementById("myDiv");
var computedStyle = myDiv.currentStyle;
alert(computedStyle.backgroundColor); //"red"
alert(computedStyle.width); //"100px"
alert(computedStyle.height); //"200px"
alert(computedStyle.border); //undefined
12.2.2 操作样式表
CSSStyleSheet 类型表示的是样式表,包括通过<link>元素包含的样式表和在<style>元素中定义的样式表。使用下面的代码可以确定浏览器是否支持 DOM2 级样式表。
var supportsDOM2StyleSheets = document.implementation.hasFeature("StyleSheets", "2.0");
从StyleSheet 接口继承而来的属性如下。
- disabled:表示样式表是否被禁用的布尔值。这个属性是可读/写的,将这个值设置为 true 可以禁用样式表。
- href:如果样式表是通过<link>包含的,则是样式表的 URL;否则,是 null。
- media:当前样式表支持的所有媒体类型的集合。与所有 DOM 集合一样,这个集合也有一个length 属性和一个 item()方法。也可以使用方括号语法取得集合中特定的项。如果集合是空列表,表示样式表适用于所有媒体。在 IE 中, media 是一个反映<link>和<style>元素 media特性值的字符串。
- ownerNode:指向拥有当前样式表的节点的指针,样式表可能是在 HTML 中通过<link>或
<style/>引入的(在 XML 中可能是通过处理指令引入的)。如果当前样式表是其他样式表通过@import 导入的,则这个属性值为 null。 IE 不支持这个属性。 - parentStyleSheet:在当前样式表是通过@import 导入的情况下,这个属性是一个指向导入它的样式表的指针。
- title: ownerNode 中 title 属性的值。
- type:表示样式表类型的字符串。对 CSS 样式表而言,这个字符串是"type/css"。
除 了 disabled 属 性 之 外, 其 他 属 性都 是 只 读 的 。 在 支 持 以上 所 有 这 些属 性 的 基 础上 ,
CSSStyleSheet 类型还支持下列属性和方法: - cssRules:样式表中包含的样式规则的集合。 IE 不支持这个属性,但有一个类似的 rules 属性。
- ownerRule:如果样式表是通过@import 导入的,这个属性就是一个指针,指向表示导入的规则;否则,值为 null。 IE 不支持这个属性。
- deleteRule(index):删除 cssRules 集合中指定位置的规则。 IE 不支持这个方法,但支持一个类似的 removeRule()方法。
- insertRule(rule,index):向 cssRules 集合中指定的位置插入 rule 字符串。 IE 不支持这个方法,但支持一个类似的 addRule()方法。
这里的 getStyleSheet()返回的样式表对象与 document.styleSheets 集合中的样式表对象相同。
- CSS 规则
CSSRule 对象表示样式表中的每一条规则。实际上, CSSRule 是一个供其他多种类型继承的基类型,其中最常见的就是 CSSStyleRule 类型,表示样式信息(其他规则还有@import、 @font-face、
@page 和@charset,但这些规则很少有必要通过脚本来访问)。 CSSStyleRule 对象包含下列属性。
- cssText:返回整条规则对应的文本。由于浏览器对样式表的内部处理方式不同,返回的文本可能会与样式表中实际的文本不一样; Safari 始终都会将文本转换成全部小写。 IE 不支持这个属性。
- parentRule:如果当前规则是导入的规则,这个属性引用的就是导入规则;否则,这个值为null。 IE 不支持这个属性。
- parentStyleSheet:当前规则所属的样式表。 IE 不支持这个属性。
- selectorText:返回当前规则的选择符文本。由于浏览器对样式表的内部处理方式不同,返回的文本可能会与样式表中实际的文本不一样(例如, Safari 3 之前的版本始终会将文本转换成全部小写)。在 Firefox、 Safari、 Chrome 和 IE 中这个属性是只读的。 Opera 允许修改 selectorText。
- style:一个 CSSStyleDeclaration 对象,可以通过它设置和取得规则中特定的样式值。
- type:表示规则类型的常量值。对于样式规则,这个值是 1。 IE 不支持这个属性。其中三个最常用的属性是 cssText、 selectorText 和 style。
- 创建规则
DOM 规定,要向现有样式表中添加新规则,需要使用 insertRule()方法。这个方法接受两个参数:规则文本和表示在哪里插入规则的索引。 - 删除规则
从样式表中删除规则的方法是 deleteRule(),这个方法接受一个参数:要删除的规则的位置。
sheet.deleteRule(0); //DOM 方法
//IE 支持的类似方法叫 removeRule(),使用方法相同,如下所示:
sheet.removeRule(0); //仅对 IE 有效
function deleteRule(sheet,index){
if(sheet.deleteRule){
sheet.deleteRule(index);
}else if(sheet.removeRule){
sheet.removeRule(index);
}
}
}
12.2.3 元素大小
- 偏移量
首先要介绍的属性涉及偏移量(offset dimension),包括元素在屏幕上占用的所有可见的空间。元素的可见大小由其高度、宽度决定,包括所有内边距、滚动条和边框大小(不包括外边距)。
- offsetHeight:元素在垂直方向上占用的空间大小,以像素计。包括元素的高度、(可见的)
水平滚动条的高度、上边框高度和下边框高度。 - offsetWidth:元素在水平方向上占用的空间大小,以像素计。包括元素的宽度、(可见的)垂
直滚动条的宽度、左边框宽度和右边框宽度。 - offsetLeft:元素的左外边框至包含元素的左内边框之间的像素距离。
- offsetTop:元素的上外边框至包含元素的上内边框之间的像素距离。
function getElementLeft(element){
var actualLeft = element.offsetLeft;
var current = element.offsetParent;
while(current !==null){
actualLeft += current.offsetLeft;
current = current.offsetParent;
}
return actualHeight;
}
function getElementTop(element){
var actualTop = element.offsetTop;
var current = element.offsetParent;
while(current!==null){
actualTop += current.offsetTop;
current = current.offsetParent;
}
return actualTop;
}
- 客户区大小
元素的客户区大小(client dimension),指的是元素内容及其内边距所占据的空间大小。有关客户区大小的属性有两个: clientWidth 和 clientHeight。其中, clientWidth 属性是元素内容区宽度加上左右内边距宽度; clientHeight 属性是元素内容区高度加上上下内边距高度。
function getViewport(){
if(document.compatModez=="BackCompat"){
return {
width:document.body.clientWidth,
height:docuement.body.clientHeight
};
}else{
return {
width:document.documentElement.clientWidth,
height:docuement.documentElement.clientHeight
};
}
}
- 滚动大小
最后要介绍的是滚动大小(scroll dimension),指的是包含滚动内容的元素的大小。有些元素(例如<html>元素),即使没有执行任何代码也能自动地添加滚动条;但另外一些元素,则需要通过 CSS 的overflow 属性进行设置才能滚动。
- scrollHeight:在没有滚动条的情况下,元素内容的总高度。
- scrollWidth:在没有滚动条的情况下,元素内容的总宽度。
- scrollLeft:被隐藏在内容区域左侧的像素数。通过设置这个属性可以改变元素的滚动位置。
- scrollTop:被隐藏在内容区域上方的像素数。通过设置这个属性可以改变元素的滚动位置
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-pn4U0mcC-1579482366803)(/images/frontend-javascript-dom-scroll.jpg)]
var docHeight = Math.max(document.documentElement.scrollHeight,document.documentElement.clientHeight);
var docWidth = Math.max(document.documentElement.scrollWidth,document.documentElement.clientWidth);
- 确定元素大小
getBoundingClientRect()方法。这个方法返回会一个矩形对象,包含 4 个属性: left、 top、 right 和 bottom。
function getBoundingClientRect(element){
if(typeof arguments.callee.offset!="number"){
var scrollTop = document.documentElementscrollTop;
var temp = document.createElement("div");
temp.style.cssText = "position:absolute;left:0;top:0;";
document.body.appendChild(temp);
arguments.callee.offset = -temp.getBoundingClientRect().top-scrollTop;
document.body.removeChild(temp);
temp = null;
}
var rect = element.getBoundingClientRect();
var offset = arguments.callee.offset;
return {
left:rect.left+offset,
right:rect.right+offset,
top:rect.top+offset,
bottom:rect.bottom+offset
};
}
对于不支持 getBoundingClientRect()的浏览器,可以通过其他手段取得相同的信息。一般来说, right 和 left 的差值与 offsetWidth 的值相等,而 bottom 和 top 的差值与 offsetHeight相等。而且, left 和 top 属性大致等于使用本章前面定义的 getElementLeft()和 getElementTop()
函数取得的值。综合上述,就可以创建出下面这个跨浏览器的函数:
function getBoundingClientRect(element){
var scrollTop = document.documentElement.scrollTop;
var scrollLeft = document.documentElement.scrollLeft;
if (element.getBoundingClientRect){
if(typeof arguments.callee.offset!="number"){
var scrollTop = document.documentElementscrollTop;
var temp = document.createElement("div");
temp.style.cssText = "position:absolute;left:0;top:0;";
document.body.appendChild(temp);
arguments.callee.offset = -temp.getBoundingClientRect().top-scrollTop;
document.body.removeChild(temp);
temp = null;
}
var rect = element.getBoundingClientRect();
var offset = arguments.callee.offset;
return {
left:rect.left+offset,
right:rect.right+offset,
top:rect.top+offset,
bottom:rect.bottom+offset
};
}else{
var actualLeft = getElementLeft(element);
var actualRight = getElementRight(element);
}return {
left: actualLeft - scrollLeft,
right: actualLeft + element.offsetWidth - scrollLeft,
top: actualTop - scrollTop,
bottom: actualTop + element.offsetHeight - scrollTop
}
}
12.3 遍历
“DOM2 级遍历和范围”模块定义了两个用于辅助完成顺序遍历 DOM 结构的类型: NodeIterator和 TreeWalker。这两个类型能够基于给定的起点对 DOM 结构执行深度优先( depth-first)的遍历操作。
var supportsTraversals = document.implementation.hasFeature("Traversal", "2.0");
var supportsNodeIterator = (typeof document.createNodeIterator == "function");
var supportsTreeWalker = (typeof document.createTreeWalker == "function");
12.3.1 NodeIterator
NodeIterator 类型是两者中比较简单的一个,可以使用 document.createNodeIterator()方法创建它的新实例。
- root:想要作为搜索起点的树中的节点。
- whatToShow:表示要访问哪些节点的数字代码。
- filter:是一个 NodeFilter 对象,或者一个表示应该接受还是拒绝某种特定节点的函数。
- entityReferenceExpansion:布尔值,表示是否要扩展实体引用。这个参数在 HTML 页面中没有用,因为其中的实体引用不能扩展。
whatToShow 参数是一个位掩码,通过应用一或多个过滤器( filter)来确定要访问哪些节点。这个参数的值以常量形式在 NodeFilter 类型中定义,如下所示。 - NodeFilter.SHOW_ALL:显示所有类型的节点。
- NodeFilter.SHOW_ELEMENT:显示元素节点。
- NodeFilter.SHOW_ATTRIBUTE:显示特性节点。由于 DOM 结构原因,实际上不能使用这个值。
- NodeFilter.SHOW_TEXT:显示文本节点。
- NodeFilter.SHOW_CDATA_SECTION:显示 CDATA 节点。对 HTML 页面没有用。
- NodeFilter.SHOW_ENTITY_REFERENCE:显示实体引用节点。对 HTML 页面没有用。
- NodeFilter.SHOW_ENTITYE:显示实体节点。对 HTML 页面没有用。
- NodeFilter.SHOW_PROCESSING_INSTRUCTION:显示处理指令节点。对 HTML 页面没有用。
- NodeFilter.SHOW_COMMENT:显示注释节点。
- NodeFilter.SHOW_DOCUMENT:显示文档节点。
- NodeFilter.SHOW_DOCUMENT_TYPE:显示文档类型节点。
- NodeFilter.SHOW_DOCUMENT_FRAGMENT:显示文档片段节点。对 HTML 页面没有用。
- NodeFilter.SHOW_NOTATION:显示符号节点。对 HTML 页面没有用。
可以通过 createNodeIterator()方法的 filter 参数来指定自定义的 NodeFilter 对象,或者指定一个功能类似节点过滤器( node filter)的函数
var filter = function(node){
return node.tagName.toLowerCase() == "p" ?
NodeFilter.FILTER_ACCEPT :
NodeFilter.FILTER_SKIP;
};
var iterator = document.createNodeIterator(root, NodeFilter.SHOW_ELEMENT,filter, false);
//下面的代码创建了一个能够访问所有类型节点的简单的 NodeIterator。
var iterator = document.createNodeIterator(document, NodeFilter.SHOW_ALL,null, false);
12.3.2 TreeWalker
TreeWalker 是 NodeIterator 的一个更高级的版本。除了包括 nextNode()和 previousNode()在内的相同的功能之外,这个类型还提供了下列用于在不同方向上遍历 DOM 结构的方法。
- parentNode():遍历到当前节点的父节点;
- firstChild():遍历到当前节点的第一个子节点;
- lastChild():遍历到当前节点的最后一个子节点;
- nextSibling():遍历到当前节点的下一个同辈节点;
- previousSibling():遍历到当前节点的上一个同辈节点。
var div = document.getElementById("div1");
var filter = function(node){
return node.tagName.toLowerCase() == "li"?
NodeFilter.FILTER_ACCEPT :
NodeFilter.FILTER_SKIP;
};
var walker= document.createTreeWalker(div, NodeFilter.SHOW_ELEMENT,filter, false);
var node = iterator.nextNode();
while (node !== null) {
alert(node.tagName); //输出标签名
node = iterator.nextNode();
}
12.4 范围
为了让开发人员更方便地控制页面,“DOM2 级遍历和范围”模块定义了“范围”( range)接口。通过范围可以选择文档中的一个区域,而不必考虑节点的界限.
12.4.1 DOM中的范围
DOM2 级在 Document 类型中定义了 createRange()方法。在兼容 DOM 的浏览器中,这个方法属于 document 对象。使用 hasFeature()或者直接检测该方法,都可以确定浏览器是否支持范围。
var supportsRange = document.implementation.hasFeature("Range", "2.0");
var alsoSupportsRange = (typeof document.createRange == "function");
//如果浏览器支持范围,那么就可以使用 createRange()来创建 DOM 范围,如下所示:
var range = document.createRange();
- startContainer:包含范围起点的节点(即选区中第一个节点的父节点)。
- startOffset:范围在 startContainer 中起点的偏移量。如果 startContainer 是文本节点、注释节点或 CDATA 节点,那么 startOffset 就是范围起点之前跳过的字符数量。否则,
startOffset 就是范围中第一个子节点的索引。 - endContainer:包含范围终点的节点(即选区中最后一个节点的父节点)。
- endOffset:范围在 endContainer 中终点的偏移量(与 startOffset 遵循相同的取值规则)。
- commonAncestorContainer: startContainer 和 endContainer 共同的祖先节点在文档树中位置最深的那个
- 用 DOM 范围实现简单选择
要使用范围来选择文档中的一部分,最简的方式就是使用 selectNode()或 selectNodeContents()。为了更精细地控制将哪些节点包含在范围中,还可以使用下列方法。
- setStartBefore(refNode):将范围的起点设置在 refNode 之前,因此 refNode 也就是范围选区中的第一个子节点。同时会将 startContainer 属性设置为 refNode.parentNode,将startOffset 属性设置为 refNode 在其父节点的 childNodes 集合中的索引。
- setStartAfter(refNode):将范围的起点设置在 refNode 之后,因此 refNode 也就不在范围之内了,其下一个同辈节点才是范围选区中的第一个子节点。同时会将 startContainer 属性设置为 refNode.parentNode,将 startOffset 属性设置为 refNode 在其父节点的childNodes 集合中的索引加 1。
- setEndBefore(refNode):将范围的终点设置在 refNode 之前,因此 refNode 也就不在范围之内了,其上一个同辈节点才是范围选区中的最后一个子节点。同时会将 endContainer 属性设置为refNode.parentNode,将 endOffset 属性设置为 refNode 在其父节点的 childNodes集合中的索引。
- setEndAfter(refNode):将范围的终点设置在 refNode 之后,因此 refNode 也就是范围选区
中的最后一个子节点。同时会将 endContainer 属性设置为 refNode.parentNode,将endOffset 属性设置为 refNode 在其父节点的 childNodes 集合中的索引加 1。
- 用 DOM 范围实现复杂选择
要创建复杂的范围就得使用 setStart()和 setEnd()方法。这两个方法都接受两个参数:一个参照节点和一个偏移量值。对 setStart()来说,参照节点会变成 startContainer,而偏移量值会变成startOffset。
可以使用这两个方法来模仿 selectNode()和 selectNodeContents()。来看下面的例子:
var range1 = document.createRange();
range2 = document.createRange();
p1 = document.getElementById("p1");
p1Index = -1;
i, len;
for (i=0, len=p1.parentNode.childNodes.length; i < len; i++) {
if (p1.parentNode.childNodes[i] == p1) {
p1Index = i;
break;
}
}
range1.setStart(p1.parentNode, p1Index);
range1.setEnd(p1.parentNode, p1Index + 1);
range2.setStart(p1, 0);
range2.setEnd(p1, p1.childNodes.length);
DOMRangeExample2.
显然,要选择这个节点(使用 range1),就必须确定当前节点( p1)在其父节点的 childNodes集合中的索引。而要选择这个节点的内容(使用 range2),也不必计算什么;只要通过 setStart()和 setEnd()设置默认值即可。
3. 操作 DOM 范围中的内容
在创建范围时 ,内部会为这个范围创建一个文档片段,范围所属的全部节点都被添加到了这个文档片段中。为了创建这个文档片段,范围内容的格式必须正确有效。在前面的例子中,我们创建的选区分别开始和结束于两个文本节点的内部,因此不能算是格式良好的 DOM 结构,也就无法通过 DOM 来表示。
4. 插入 DOM 范围中的内容
利用范围,可以删除或复制内容,还可以像前面介绍的那样操作范围中的内容。 使用 insertNode()
方法可以向范围选区的开始处插入一个节点。假设我们想在前面例子中的 HTML 前面插入以下 HTML
代码:
<span style="color: red">Inserted text</span>
//那么,就可以使用下列代码:
var p1 = document.getElementById("p1");
helloNode = p1.firstChild.firstChild;
worldNode = p1.lastChild;
range = document.createRange();
range.setStart(helloNode, 2);
range.setEnd(worldNode, 3);
var span = document.createElement("span");
span.style.color = "red";
span.appendChild(document.createTextNode("Inserted text"));
range.insertNode(span);
- 折叠 DOM 范围
所谓折叠范围,就是指范围中未选择文档的任何部分。可以用文本框来描述折叠范围的过程。假设文本框中有一行文本,你用鼠标选择了其中一个完整的单词。然后,你单击鼠标左键,选区消失,而光标则落在了其中两个字母之间。同样,在折叠范围时,其位置会落在文档中的两个部分之间,可能是范围选区的开始位置,也可能是结束位置。 - 比较 DOM 范围
在有多个范围的情况下,可以使用 compareBoundaryPoints()方法来确定这些范围是否有公共的边界(起点或终点)。这个方法接受两个参数:表示比较方式的常量值和要比较的范围。表示比较方式的常量值如下所示。
- Range.START_TO_START(0):比较第一个范围和第二个范围的起点;
- Range.START_TO_END(1):比较第一个范围的起点和第二个范围的终点;
- Range.END_TO_END(2):比较第一个范围和第二个范围的终点;
- Range.END_TO_START(3):比较第一个范围的终点和第一个范围的起点。
var range1 = document.createRange();
var range2 = document.createRange();
var p1 = document.getElementById("p1");
range1.selectNodeContents(p1);
range2.selectNodeContents(p1);
range2.setEndBefore(p1.lastChild);
alert(range1.compareBoundaryPoints(Range.START_TO_START, range2)); //0
alert(range1.compareBoundaryPoints(Range.END_TO_END, range2)); //1
- 复制 DOM 范围
可以使用 cloneRange()方法复制范围。这个方法会创建调用它的范围的一个副本。
var newRange = range.cloneRange();
新创建的范围与原来的范围包含相同的属性,而修改它的端点不会影响原来的范围。
8. 清理 DOM 范围
在使用完范围之后,最好是调用 detach()方法,以便从创建范围的文档中分离出该范围。调用detach()之后,就可以放心地解除对范围的引用,从而让垃圾回收机制回收其内存了。来看下面的例子。
range.detach(); //从文档中分离
range = null; //解除引用
12.5 小结
DOM2 级规范定义了一些模块,用于增强 DOM1 级。“DOM2 级核心”为不同的 DOM 类型引入了一些与 XML 命名空间有关的方法。这些变化只在使用 XML 或 XHTML 文档时才有用;对于 HTML 文档没有实际意义。除了与 XML 命名空间有关的方法外, “DOM2 级核心”还定义了以编程方式创建Document 实例的方法,也支持了创建 DocumentType 对象。
“DOM2 级样式”模块主要针对操作元素的样式信息而开发,其特性简要总结如下。
- 每个元素都有一个关联的 style 对象,可以用来确定和修改行内的样式。
- 要确定某个元素的计算样式(包括应用给它的所有 CSS 规则), 可以使用 getComputedStyle()方法。
- IE 不支持 getComputedStyle()方法,但为所有元素都提供了能够返回相同信息 currentStyle属性。
- 可以通过 document.styleSheets 集合访问样式表。
- 除 IE 之外的所有浏览器都支持针对样式表的这个接口, IE 也为几乎所有相应的 DOM 功能提供了自己的一套属性和方法。
“DOM2 级遍历和范围”模块提供了与 DOM 结构交互的不同方式,简要总结如下。 - 遍历即使用 NodeIterator 或 TreeWalker 对 DOM 执行深度优先的遍历。
- NodeIterator 是一个简单的接口,只允许以一个节点的步幅前后移动。而 TreeWalker 在提供相同功能的同时,还支持在 DOM 结构的各个方向上移动,包括父节点、同辈节点和子节点等方向。
- 范围是选择 DOM 结构中特定部分,然后再执行相应操作的一种手段。
- 使用范围选区可以在删除文档中某些部分的同时,保持文档结构的格式良好,或者复制文档中的相应部分。
- IE8 及更早版本不支持“DOM2 级遍历和范围”模块,但它提供了一个专有的文本范围对象,可以用来完成简单的基于文本的范围操作。 IE9 完全支持 DOM 遍历。