第12章 DOM2和DOM3
1.DOM1 级主要定义的是 HTML 和 XML 文档的底层结构。DOM2 和 DOM3 级则在这个结构的基础上引入了更多的交互能力。
- DOM2 级核心(DOM Level 2 Core):在 1 级核心基础上构建,为节点添加了更多方法和属性。
- DOM2 级视图(DOM Level 2 Views):为文档定义了基于样式信息的不同视图。
- DOM2 级事件(DOM Level 2 Events):说明了如何使用事件与 DOM 文档交互。
- DOM2 级样式(DOM Level 2 Style):定义了如何以编程方式来访问和改变 CSS 样式信息。
- DOM2 级遍历和范围(DOM Level 2 Traversal and Range):引入了遍历 DOM 文档和选择其特定部分的新接口。
- DOM2 级 HTML(DOM Level 2 HTML):在 1 级 HTML 基础上构建,添加了更多属性、方法和新接口。
2.DocumentType 类型新增了 3 个属性:publicId、systemId 和 internalSubset。其中,前两个属性表示的是文档类型声明中的两个信息段,第三个用于访问包含在文档类型声明中的额外定义,很少需要在网页中访问此类信息。
3.Document 类型的变化:
- importNode()方法:从一个文档中取得一个节点,然后将其导入到另一个文档,使其成为这个文档结构的一部分,接受两个参数:要复制的节点和一个表示是否复制子节点的布尔值。
- defaultView 的属性,其中保存着一个指针,指向拥有给 定文档的窗口(或框架)。
- createDocumentType()方法:用于创建一个新的 DocumentType 节点,接受 3 个参数:文档类型名称、publicId、systemId。
- createDocument()方法:创建新文档,接受 3 个参数:针对文档中元素的 namesp- aceURI、文档元素的标签名、新文档的文档类型。
- createHTMLDocument()方法:创建一个完整的 HTML 文档,接受一个参数,即新创建文档的标题
4.Node 类型的变化:添加了 isSupported()方法,用于确定当前节点具有什么能力,接受相同的两个参数:特性名和特性版本号。
5.框架的变化:contentDocument属性包含一个指针,指向表示框架内容的文档对象。
6.在 HTML 中定义样式的方式有 3 种:通过<link/>元素包含外部样式表文件、使用<style/>元素 定义嵌入式样式,以及使用 style 特性定义针对特定元素的样式。任何支持 style 特性的 HTML 元素在 JavaScript 中都有一个对应的 style 属性。这个 style 对象 是 CSSStyleDeclaration 的实例,包含着通过 HTML 的 style 特性指定的所有样式信息,但不包含与外部样式表或嵌入样式表经层叠而来的样式。“DOM2 级样式”规范还为 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"或者一个空字符串)。
7.要确定某个元素的计算样式(包括应用给它的所有 CSS 规则),可以使用 getComputedStyle()方法,接受两个参数:要取得计算样式的元素和一个伪元素字符串(例 如":after")。如果不需要伪元素信息,第二个参数可以是 null。getComputedStyle()方法返回一个 CSSStyleDeclaration 对象(与 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"
8.可以通过 document.styleSheets 集合访问样式表。
9.通过 下列 4 个属性可以取得元素的偏移量:
- 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 actualLeft; } //取得元素的上偏移量 function getElementTop(element){ var actualTop = element.offsetTop; var current = element.offsetParent; while (current !== null){ actualTop += current. offsetTop; current = current.offsetParent; } return actualTop; }
10.元素的客户区大小(client dimension):元素内容及其内边距所占据的空间大小,clientWidth 属性是元素内容区宽度加上左右内边距宽度;clientHeight 属性是元素内容区高度加上上下内边距高度。
//确定浏览器 视口大小
function getViewport(){
if (document.compatMode == "BackCompat"){
return {
width: document.body.clientWidth,
height: document.body.clientHeight
};
} else {
return {
width: document.documentElement.clientWidth,
height: document.documentElement.clientHeight
};
}
}
11.滚动大小:包含滚动内容的元素的大小,4 个与滚动大小相关的属性。
- scrollHeight:在没有滚动条的情况下,元素内容的总高度。
- scrollWidth:在没有滚动条的情况下,元素内容的总宽度。
- scrollLeft:被隐藏在内容区域左侧的像素数。通过设置这个属性可以改变元素的滚动位置。
- scrollTop:被隐藏在内容区域上方的像素数。通过设置这个属性可以改变元素的滚动位置。
12.跨浏览器取得元素位置
function getBoundingClientRect(element){
var scrollTop = document.documentElement.scrollTop;
var scrollLeft = document.documentElement.scrollLeft;
if (element.getBoundingClientRect){
if (typeof arguments.callee.offset != "number"){
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 actualTop = getElementTop(element);
return {
left: actualLeft - scrollLeft,
right: actualLeft + element.offsetWidth - scrollLeft,
top: actualTop - scrollTop,
bottom: actualTop + element.offsetHeight - scrollTop
}
}
}
13.“DOM2 级遍历和范围”模块定义了两个用于辅助完成顺序遍历 DOM 结构的类型:NodeIterator 和 TreeWalker,对 DOM 结构执行深度优先(depth-first)的遍历操作。
(1)NodeIterator:使用 document.createNodeIterator()方 法创建它的新实例。这个方法接受下列 4 个参数:
- root:想要作为搜索起点的树中的节点。
- whatToShow:表示要访问哪些节点的数字代码。
- filter:是一个 NodeFilter 对象,或者一个表示应该接受还是拒绝某种特定节点的函数。
- entityReferenceExpansion:布尔值,表示是否要扩展实体引用。这个参数在 HTML 页面中没有用,因为其中的实体引用不能扩展。
两个主要方法:
- nextNode()方法:用于向前前进一步和。
- previousNode()方法:用于向后后退一步。
<div id="div1">
<p><b>Hello</b> world!</p>
<ul>
<li>List item 1</li>
<li>List item 2</li>
<li>List item 3</li>
</ul>
</div>
//遍历<div>元素中的所有元素
var div = document.getElementById("div1");
var iterator = document.createNodeIterator(div, NodeFilter.SHOW_ELEMENT,null, false);
var node = iterator.nextNode();
while (node !== null) {
alert(node.tagName);
node = iterator.nextNode();
}
//只返回遍历中遇到的<li>元素,使用一个过滤器
var div = document.getElementById("div1");
var filter = function(node){
return node.tagName.toLowerCase() == "li" ? NodeFilter.FILTER_ACCEPT : NodeFilter.FILTER_SKIP;
};
var iterator = document.createNodeIterator(div, NodeFilter.SHOW_ELEMENT, filter, false);
var node = iterator.nextNode();
while (node !== null) {
alert(node.tagName);
node = iterator.nextNode();
}
(2)TreeWalker 是 NodeIterator 的一个更高级的版本。除了包括 nextNode()和 previousNode() 在内的相同的功能之外,这个类型还提供了下列用于在不同方向上遍历 DOM 结构的方法:
- parentNode():遍历到当前节点的父节点;
- firstChild():遍历到当前节点的第一个子节点;
- lastChild():遍历到当前节点的最后一个子节点;
- nextSibling():遍历到当前节点的下一个同辈节点;
- previousSibling():遍历到当前节点的上一个同辈节点。
使用 document.createTreeWalker()方法创建 TreeWalker 对象,接受的 4 个参数 与 document.createNodeIterator()相同。
//不定义过滤器,也可以取得所有<li>元素
var div = document.getElementById("div1");
var walker = document.createTreeWalker(div, NodeFilter.SHOW_ELEMENT, null, false);
walker.firstChild();//转到<p>
walker.nextSibling();//转到<ul>
var node = walker.firstChild();//转到第一个<li>
while (node !== null) { alert(node.tagName);
node = walker.nextSibling();
}
14.“DOM2 级遍历和范围”模块定义了“范围”(range)接口。通过范围可以选择文档中的一个区域,而不必考虑节点的界限(选择在后台完成,对用户是不可见的)。 在常规的 DOM 操作不能更有效地修改文档时,使用范围往往可以达到目的。
(1)使用 createRange()来创建 DOM 范围,每个范围由一个 Range 类型的实例表示,这个实例拥有很多属性和方法,下列属性提供了当前范 围在文档中的位置信息:
- startContainer:包含范围起点的节点(即选区中第一个节点的父节点)。
- startOffset:范围在 startContainer 中起点的偏移量。如果 startContainer 是文本节 点、注释节点或 CDATA 节点,那么 startOffset 就是范围起点之前跳过的字符数量。否则,startOffset 就是范围中第一个子节点的索引。
- endContainer:包含范围终点的节点(即选区中最后一个节点的父节点)。
- endOffset:范围在 endContainer 中终点的偏移量(与 startOffset 遵循相同的取值规则)。
- commonAncestorContainer:startContainer 和 endContainer 共同的祖先节点在文档树中位置最深的那个。
(2)使用 selectNode()或 selectNodeContents()来选择文档中的一部分,两个函数都接受一个参数,即一个 DOM 节点,然后使用该节点中的信息来填充范围,selectNode()方法选择整个节点,包括其子节点;selectNodeContents()方法只选择节点的子节点。
<!DOCTYPE html>
<html>
<body>
<p id="p1"><b>Hello</b> world!</p>
</body>
</html>
var range1 = document.createRange(),
range2 = document.createRange(),
p1 = document.getElementById("p1"); 4
range1.selectNode(p1);//rang1 包含<p/>元素及其所有子元素
range2.selectNodeContents(p1);// rang2 包含<b/>元素、文本节点"Hello"和文本节点"world!"
此外,为了更精细地控制将哪些节点包含在范围中,还可以使用下列方法:
- 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。
(3)使用 setStart()和 setEnd()方法创建复杂的范围,这两个方法都接受两个参数:一个参 照节点和一个偏移量值。对 setStart()来说,参照节点会变成 startContainer,而偏移量值会变成startOffset。对于 setEnd()来说,参照节点会变成 endContainer,而偏移量值会变成 endOffset。
<!DOCTYPE html>
<html>
<body>
<p id="p1"><b>Hello</b> world!</p>
</body>
</html>
//选择 HTML 代码中从"Hello"的"llo"到"world!"的"o"
//取得所有节点的引用
var p1 = document.getElementById("p1"),
helloNode = p1.firstChild.firstChild,
worldNode = p1.lastChild;
var range = document.createRange();
//setStart()中传入 helloNode 的同时,传入了偏移量 2(即"e"的下一个位置;"H"的位置是 0)。
range.setStart(helloNode, 2);
//setEnd() 中传入 worldNode 的同时传入了偏移量 3,表示选区之外的第一个字符的位置,这个字符是"r",它的 位置是 3(位置 0 上还有一个空格)。
range.setEnd(worldNode, 3);
(4)在创建范围时 ,内部会为这个范围创建一个文档片段,范围所属的全部节点都被添加到了这个文档片段中。创建了范围之后,就可以使用各种方法对范围的内容进行操作了(注意,表示范围的内部文档片段中的所有节点,都只是指向文档中相应节点的指针)。
- deleteContents():从文档中删除范 围所包含的内容。
- extractContents():从文档中移除范围选区,返回范围的文档片段。利用这个返回的值,可以将范围的内容插入到文档中的其他地方。
- cloneContents():创建范围对象的一个副本
(5)插入 DOM 范围中的内容:
<span style="color: red">Inserted text</span>
- insertNode() 方法:向范围选区的开始处插入一个节点,用来插入一些帮助提示信息,例如在打开新窗口的链接旁边插入一幅图像。
-
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); //结果 <p id="p1"><b>He<span style="color: red">Inserted text</span>llo</b> world</p>
- surroundContents()方法:环绕范围插入内容,使用这种技术来突出显示网页中的某些词句。
-
var p1 = document.getElementById("p1"), helloNode = p1.firstChild.firstChild, worldNode = p1.lastChild, range = document.createRange(); range.selectNode(helloNode); var span = document.createElement("span"); span.style.backgroundColor = "yellow"; range.surroundContents(span); //给范围选区加上一个黄色的背景 <p><b><span style="background-color:yellow">Hello</span></b> world!</p>
(6)使用 collapse()方法来折叠范围:接受一个参数,一个布尔值,表示要折叠到范围的哪 一端。参数true表示折叠到范围的起点,参数false表示折叠到范围的终点。要确定范围已经折叠完 毕,可以检查 collapsed 属性。
(7)使用 compareBoundaryPoints()方法来确定多个范围是否有公共的边界(起点或终点),接受两个参数:表示比较方式的常量值和要比较的范围。表示比较方式的常量值如下:
- Range.START_TO_START(0):比较第一个范围和第二个范围的起点;
- Range.START_TO_END(1):比较第一个范围的起点和第二个范围的终点;
- Range.END_TO_END(2):比较第一个范围和第二个范围的终点;
- Range.END_TO_START(3):比较第一个范围的终点和第一个范围的起点。
可能的返回值如下:
- 如果第一个范围中的点位于第二个范围中的 点之前,返回-1;
- 如果两个点相等,返回 0;
- 如果第一个范围中的点位于第二个范围中的点之后,返回 1。
(8)使用 cloneRange()方法复制范围。这个方法会创建调用它的范围的一个副本。
(9)使用 detach()方法解除对范围的引用。