JavaScript 高级程序设计(第12章 DOM2和DOM3
第12章 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 基础上构建,添加了更多属性、方法和新接口。
可以通过下列代码来确定浏览器是否支持这些 DOM 模块。
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");
1. DOM 变化
DOM2 级和 3 级的目的在于扩展 DOM API,以满足操作 XML 的所有需求,同时提供更好的错误处理及特性检测能力。从某种意义上讲,实现这一目的很大程度意味着
对命名空间的支持
。
- DOM2 级核 心”没有引入新类型,它只是在 DOM1 级的基础上通过增加新方法和新属性来增强了既有类型。
- “DOM3 级核心”同样增强了既有类型,但也引入了一些新类型。
(1) 针对XML命名空间的变化
- 有了 XML 命名空间,不同 XML 文档的元素就可以混合在一起,共同构成格式良好的文档,而不必担心发生命名冲突。
- HTML 不支持 XML 命名空间,但
XHTML
支持 XML 命名空间。命名空间要使用 xmlns 特性来指定
。- XHTML 的命名空间是 http://www.w3.org/1999/xhtml,在任何 格式良好 XHTML 页面中,都应该将其包含在<html>元素中。
- 其中的所有元素默认都被视为 XHTML 命名空间中的元素。
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title>Example XHTML page</title>
</head>
<body>
Hello world!
</body>
</html>
- 要想明确地为 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 xhtml:class="home">//为了避免不同语言间的冲突,也需要使用命名空间来限定特性
Hello world!
</xhtml:body>
</xhtml:html>
- 在混合使用两种语言的情况下,命名空间的用处
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title>Example XHTML page</title>
</head>
<body>
<svg xmlns="http://www.w3.org/2000/svg" version="1.1"
viewBox="0 0 100 100" style="width:100%; height:100%">
<rect x="0" y="0" width="100" height="100" style="fill:red"/>
</svg>
</body>
</html>
* Node 类型的变化
- 在 DOM2 级中,Node 类型包含下列
特定于命名空间的属性
。
(1) localName: 不带命名空间前缀的节点名称。
(2) nameSpaceURI: 命名空间URI或者(在未指定的情况下是)null。
(3) prefix:命名空间前缀或者(在未指定的情况下是)null。
nodeName 等于 prefix+":"+ localName。
- DOM3 级在此基础上更进一步,又引入了下列与命名空间有关的方法。
(1) isDefaultNamespace(namespaceURI):在指定的 namespaceURI 是当前节点的默认命名空
间的情况下返回 true。
(2) lookupNamespaceURI(prefix):返回给定 prefix 的命名空间。
(3) lookupPrefix(namespaceURI):返回给定 namespaceURI 的前缀。
* Document 类型的变化
DOM2 级中的 Document 类型也发生了变化,包含了下列与命名空间有关的方法。
(1) createElementNS(namespaceURI, tagName):使用给定的 tagName 创建一个属于命名空 间 namespaceURI 的新元素。
(2) createAttributeNS(namespaceURI, attributeName):使用给定的 attributeName 创 建一个属于命名空间 namespaceURI 的新特性。
(3) getElementsByTagNameNS(namespaceURI, tagName):返回属于命名空间 namespaceURI 的 tagName 元素的 NodeList。
*Element 类型的变化
(1) getAttributeNS(namespaceURI,localName):取得属于命名空间 namespaceURI 且名为 localName 的特性。
(2) getAttributeNodeNS(namespaceURI,localName):取得属于命名空间 namespaceURI 且 名为 localName 的特性节点。
(3) getElementsByTagNameNS(namespaceURI, tagName):返回属于命名空间 namespaceURI 的 tagName 元素的 NodeList。
(4) hasAttributeNS(namespaceURI,localName):确定当前元素是否有一个名为 localName 的特性,而且该特性的命名空间是 namespaceURI。注意,“DOM2 级核心”也增加了一个 hasAttribute()方法,用于不考虑命名空间的情况。
(5) removeAttriubteNS(namespaceURI,localName):删除属于命名空间 namespaceURI 且名 为 localName 的特性。
(6) setAttributeNS(namespaceURI,qualifiedName,value):设置属于命名空间 namespace- URI 且名为 qualifiedName 的特性的值为 value。
(7) setAttributeNodeNS(attNode):设置属于命名空间 namespaceURI 的特性节点。
* NamedNodeMap 类型的变化
(1) getNamedItemNS(namespaceURI,localName):取得属于命名空间 namespaceURI 且名为
localName 的项。
(2) removeNamedItemNS(namespaceURI,localName):移除属于命名空间 namespaceURI 且名
为 localName 的项。
(3) setNamedItemNS(node):添加 node,这个节点已经事先指定了命名空间信息。
(2) 其他方面的变化
* DocumentType 类型的变化
DocumentType 类型新增了 3 个属性:publicId、systemId 和 internalSubset
。
- 前两 个属性表示的是文档类型声明中的两个信息段
属性 internalSubset
,用于访问包含在文档类型声明中的额外定义。
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN"
"http://www.w3.org/TR/html4/strict.dtd"
[<!ELEMENT name (#PCDATA)>]>
alert(document.doctype.publicId);
// "-//W3C//DTD HTML 4.01//EN"
alert(document.doctype.systemId);
// "http: //www.w3.org/TR/html4/strict.dtd"
alert(document.doctype.internalSubset);
// "<!ELEMENT name (#PCDATA)>"
*Document 类型的变化
importNode()方法
:
- 这个方法的用途是从一个文档中取得一个节点,然后将其导入到另一个文档,使其成为这个文档结构的一部分。
- 每个节点都有一个
ownerDocument 属性
,表示所属的文档。如果调用 appendChild()时传入的节点属于不同的文档(ownerDocument 属性的值不一样),则会导致错误。但在调用 importNode()时传入 不同文档的节点则会返回一个新节点,这个新节点的所有权归当前文档所有。接受两个参数
:要复制的节点和一个表示是否复制子节点的布尔值。
//导入节点及其所有子节点
var newNode = document.importNode(oldNode, true);
document.body.appendChild(newNode);
defaultView 的属性
:
- DOM2 级视图”模块属性,其中保存着一个指针,
指向拥有给 定文档的窗口(或框架)
。- 在 IE 中有一个等价的属性名叫 parentWindow(Opera 也支持这个属性)。
var parentWindow = document.defaultView || document.parentWindow;
DOM2 级核心”还为document.implementation 对象
规定了 两个新方法:createDocumentType()
和 createDocument()
。
createDocumentType() 方法
: 于创建一个新的 DocumentType 节点,接受 3 个参数:文档类型名称、publicId、systemId。createDocument()方法
:3 个参数:针对文档中元素的 namespaceURI、文档元素的标签名、新文档的文档类型。
var doctype = document.implementation.createDocumentType(
"html",
" -//W3C//DTD XHTML 1.0 Strict//EN",
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd");
var doc = document.implementation.createDocument(
"http://www.w3.org/1999/xhtml",
"html",
doctype);
DOM2 级 HTML”模块也为 document.implementation
新增了一个方法,名叫 createHTMLDocument()
。
createHTMLDocument()方法
:这个方法的用途是创建一个完整的 HTML 文档,包括<html>、<head>、<title>和 <body>元素。这个方法只接受一个参数,即新创建文档的标题(放在<title>元素中的字符串),返回 新的 HTML 文档
* Node 类型的变化
isSupported()方法
- DOM1 级为 document.implementation引入的hasFeature()方法类似。
- 用于确定当前节点具有 什么能力。这个方法也接受相同的两个参数:
特性名
和特性版本号
。如果浏览器实现了相应特性,而且能够基于给定节点执行该特性,isSupported()就返回 true。
DOM3 级引入了两个辅助比较节点的方法:isSameNode()
和 isEqualNode()
。
isSameNode()方法
接受 一个节点参数,并在传入节点与引用的节点相同时返回 true。isEqualNode()
接受 一个节点参数,并在传入节点与引用的节点等时返回 true。
//这里创建了两个具有相同特性的<div>元素。这两个元素相等,但不相同。
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
(1) 所谓
相同
,指的是两个节点引用的是同一个对象。
(2) 所谓相等
,指的是两个节点是相同的类型,具有相等的属性(nodeName、nodeValue, 等等),而且它们的 attributes 和 childNodes 属性也相等(相同位置包含相同的值)。
DOM3 级还针对为 DOM 节点添加额外数据引入了新方法。其中,setUserData()方法
会将数据指定给节点,它接受 3 个参数:要设置的键、实际的数据(可以是任何数据类型)和处理函数。
- 传入 setUserData()中的处理函数会在带有数据的节点被
复制、删除、重命名或引入一个文档时调用
,因而你可以事先决定在上述操作发生时如何处理用户数据。- 处理函数接受
5 个参数
:表示操作类 型的数值(1 表示复制,2 表示导入,3 表示删除,4 表示重命名)、数据键、数据值、源节点和目标节点。- 在删除节点时,源节点是 null;除在复制节点时,目标节点均为 null。在函数内部
var div = document.createElement("div");
div.setUserData("name", "Nicholas", function(operation, key, value, src, dest){
if (operation == 1){
dest.setUserData(key, value, function(){});
}
});
var newDiv = div.cloneNode(true);
alert(newDiv.getUserData("name")); //"Nicholas"
* 框架的变化
- 框架和内嵌框架分别用
HTMLFrameElement
和HTMLIFrameElement
表示,它们在 DOM2 级中都有 了一个新属性,名叫contentDocument
。这个属性包含一个指针,指向表示框架内容的文档对象。- IE8 之前不支持框架中的 contentDocument 属性,但支持一个名叫
contentWindow
的属性,该属性返回框架的 window 对象,而这个 window 对象又有一个 document 属性。
var iframe = document.getElementById("myIframe");
var iframeDoc = iframe.contentDocument ||
iframe.contentWindow.document;
2. 样式
在 HTML 中定义样式的方式有 3 种:通过
<link/>元素
包含外部样式表文件、使用<style/>元素
定义嵌入式样式,以及使用style 特性
定义针对特定元素的样式。
确定浏览器是否支持 DOM2 级定义的 CSS 能力:
var supportsDOM2CSS = document.implementation.hasFeature("CSS", "2.0");
var supportsDOM2CSS2 = document.implementation.hasFeature("CSS2", "2.0");
(1) 访问元素的样式
- 任何支持 style 特性的 HTML 元素在 JavaScript 中都有一个对应的 style 属性。
- 这个 style 对象 是
CSSStyleDeclaration 的实例
,包含着通过 HTML 的 style 特性指定的所有样式信息,但不包含与外部样式表或嵌入样式表经层叠而来的样式。- 在 style 特性中指定的任何 CSS 属性都将表现为这个 style 对象的相应属性。
- 对于使用短划线(分隔不同的词汇,例如 background-image)的 CSS 属性 名,必须将其转换成
驼峰大小写形式
,才能通过 JavaScript 来访问。
- 由于
float
是 JavaScript 中的保留字,因此不能用作属性名。“DOM2 级样式”规范规定 样式对象上相应的属性名应该是cssFloat
;Firefox、Safari、Opera 和 Chrome 都支持这个属性,而 IE 支持的则是styleFloat。
* DOM 样式属性和方法
“DOM2 级样式”规范还为 style 对象定义了一些属性和方法。这些属性和方法在提供元素的 style特性值的同时,也可以修改样式。
(1) cssText:如前所述,通过它能够访问到 style 特性中的 CSS 代码。
(2) length:应用给元素的 CSS 属性的数量。
(3) parentRule:表示 CSS 信息的 CSSRule 对象。
(4) getPropertyCSSValue(propertyName):返回包含给定属性值的 CSSValue 对象。
(5) getPropertyPriority(propertyName):如果给定的属性使用了!important 设置,则返回"important";否则,返回空字符串。
(6) getPropertyValue(propertyName):返回给定属性的字符串值。
(7) item(index):返回给定位置的 CSS 属性的名称。
(8) removeProperty(propertyName):从样式中删除给定属性。
(9) setProperty(propertyName,value,priority):将给定属性设置为相应的值,并加上优先权标志("important"或者一个空字符串)。
-
在
读取模式
下,cssText
返回浏览器对 style特性中 CSS 代码的内部表示。在写入模式
下,赋给 cssText 的值会重写整个 style 特性的值;也就是 说,以前通过 style 特性指定的样式信息都将丢失。 -
style 对象实际上就相当于一个集合,都可以使用
方括号
语法来代替item()
来取得给定位置的 CSS 属性。
var prop, value, i, len;
for (i=0, len=myDiv.style.length; i < len; i++){
prop = myDiv.style[i]; //或者 myDiv.style.item(i)
value = myDiv.style.getPropertyValue(prop);
alert(prop + " : " + value);
}
-
getPropertyCSSValue()方法
,它返回一个包含两个属性的 CSSValue 对象,这两个属性分别是:cssText
和cssValueType
。
(1)
cssText 属性
的值与 getPropertyValue()返回的值相同。
(2)cssValueType 属性
则是一个数值常量,表示值的类型:0 表示继承的值,1 表示基本的值,2 表示 值列表,3 表示自定义的值。
* 计算的样式
DOM2 级样式”增强了 document.defaultView,提供了 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"
- IE 不支持 getComputedStyle()方法,在 IE 中,每个具有 style 属性 的元素还有一个
currentStyle 属性
。这个属性是 CSSStyleDeclaration 的实例,包含当前元素全 部计算后的样式。
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
* 所有计算的样式都是只读的;不能修改计算后样式对象中的 CSS 属性。
* 计算后的样式也包含属于浏览器内部样式表的样式信息,因此任何具有默认值 的 CSS 属性都会表现在计算后的样式中。
(2) 操作样式表
-
CSSStyleSheet 类型
表示的是样式表,包括通过<link>元素
包含的样式表和在<style>元素
中定义的样式表。 -
<link>元素
,<style>元素
,这两个元素本身分别是由 HTMLLinkElement 和 HTMLStyleElement 类型表示的。
使用下面的代码可以确定浏览器是否支持 DOM2 级样式表:
var supportsDOM2StyleSheets =
document.implementation.hasFeature("StyleSheets", "2.0");
- CSSStyleSheet 继承自 StyleSheet,后者可以作为一个基础接口来定义非 CSS 样式表。
从 StyleSheet 接口继承而来的属性
如下:
(1) disabled:表示样式表是否被禁用的布尔值。这个属性是可读/写的,将这个值设置为 true 可 以禁用样式表。
(2) href:如果样式表是通过<link>包含的,则是样式表的 URL;否则,是 null。
(3) media:当前样式表支持的所有媒体类型的集合。与所有 DOM 集合一样,这个集合也有一个 length 属性和一个 item()方法。也可以使用方括号语法取得集合中特定的项。如果集合是空列表,表示样式表适用于所有媒体。在 IE 中,media 是一个反映<link>和<style>元素 media特性值的字符串。
(4) ownerNode:指向拥有当前样式表的节点的指针,样式表可能是在 HTML 中通过<link>或<style/>引入的(在 XML 中可能是通过处理指令引入的)。如果当前样式表是其他样式表通过
@import 导入的,则这个属性值为 null。IE 不支持这个属性。
(5) parentStyleSheet:在当前样式表是通过@import 导入的情况下,这个属性是一个指向导入它的样式表的指针。
(6) title:ownerNode 中 title 属性的值。
(7) type:表示样式表类型的字符串。对 CSS 样式表而言,这个字符串是"type/css"。
除了disabled 属性之外,其他属性都是只读的。
4.CSSStyleSheet 类型还支持下列属性和方法
:
(1) cssRules:样式表中包含的样式规则的集合。IE 不支持这个属性,但有一个类似的 rules 属性。
(2) ownerRule:如果样式表是通过@import 导入的,这个属性就是一个指针,指向表示导入的规则;否则,值为 null。IE 不支持这个属性。
(3) deleteRule(index):删除 cssRules 集合中指定位置的规则。IE 不支持这个方法,但支持一个类似的 removeRule()方法。
(4) insertRule(rule,index):向 cssRules 集合中指定的位置插入 rule 字符串。IE 不支持这个方法,但支持一个类似的 addRule()方法。
5.应用于文档的所有样式表是通过 document.styleSheets
集合来表示的。通过这个集合的 length属性可以获知文档中样式表的数量,而通过方括号语法或 item()方法可以访问每一个样式表。
var sheet = null;
for (var i=0, len=document.styleSheets.length; i < len; i++){
sheet = document.styleSheets[i];
alert(sheet.href);
}
-
直接通过<link>或<style>元素取得 CSSStyleSheet 对象
。DOM 规定了一个包含 CSSStyleSheet 对象的属性,名叫sheet
;除了 IE,其他浏览器都支持这个属性。IE 支持的是styleSheet 属性
。
function getStyleSheet(element){
return element.sheet || element.styleSheet;
}
//取得第一个<link/>元素引入的样式表
var link = document.getElementsByTagName("link")[0];
var sheet = getStylesheet(link);
*CSS 规则
CSSRule 对象表示样式表中的每一条规则。
CSSRule 是一个供其他多种类型继承的基类 型,其中最常见的就是
CSSStyleRule
类型,表示样式信息。(其他规则还有@import、@font-face、 @page 和@charset,但这些规则很少有必要通过脚本来访问)。
CSSStyleRule 对象包含下列属性:
(1)
cssText:返回整条规则对应的文本
。由于浏览器对样式表的内部处理方式不同,返回的文本 可能会与样式表中实际的文本不一样;Safari 始终都会将文本转换成全部小写。IE 不支持这个 属性。
(2)parentRule
:如果当前规则是导入的规则,这个属性引用的就是导入规则
;否则,这个值为 null。IE 不支持这个属性。
(3)parentStyleSheet:当前规则所属的样式表
。IE 不支持这个属性。
(4)selectorText:返回当前规则的选择符文本
。由于浏览器对样式表的内部处理方式不同,返回的文本可能会与样式表中实际的文本不一样(例如,Safari 3 之前的版本始终会将文本转换成全
部 小 写 )。 在 F i r e f o x 、 S a f a r i 、 C h r o m e 和 I E 中 这 个 属 性 是 只 读 的 。 O p e r a 允 许 修 改 s e l e c t o r T e x t 。
(5)style:一个 CSSStyleDeclaration 对象
,可以通过它设置和取得规则中特定的样式值。
(6)type:表示规则类型的常量值
。对于样式规则,这个值是 1。IE 不支持这个属性。
* cssText 属性与 style.cssText 属性类似,但并不相同。前者包含选择符文本和围绕样式信息的花括号,后者只包含样式信息。
* cssText 是只读的,而 style.cssText 也可以被重写。
div.box {
background-color: blue;
width: 100px; 2 height: 200px;
}
var sheet = document.styleSheets[0];
var rules = sheet.cssRules || sheet.rules;//取得规则列表
var rule = rules[0];//取得第一条规则
alert(rule.selectorText); //"div.box"
alert(rule.style.cssText);//完整的 CSS 代码
alert(rule.style.backgroundColor);//"blue"
alert(rule.style.width);//"100px"
alert(rule.style.height); //"200px"
* 创建规则
insertRule()方法:
向现有样式表中添加新规则,接受两个参 数:
规则文本
和表示在哪里插入规则的索引
。
var sheet = document.styleSheets[0];
sheet.insertRule("body { background-color: silver }", 0); //DOM 方法
addRule()方法:
接收两必选参数:
选择符文本
和CSS 样式信息
;一个可选参数:插入规则的位置
。
sheet.addRule("body", "background-color: silver", 0);
跨浏览器的方式向样式表中插入规则:
function insertRule(sheet, selectorText, cssText, position){
if (sheet.insertRule){
sheet.insertRule(selectorText + "{" + cssText + "}", position);
} else if (sheet.addRule){
sheet.addRule(selectorText, cssText, position);
}
}
insertRule(document.styleSheets[0], "body", "background-color: silver", 0);
*删除规则
deleteRule()方法:
接受一个参数:要删除的规则的位置。
sheet.deleteRule(0); //DOM 方法
removeRule()方法:
sheet.removeRule(0); //仅对 IE 有效
跨浏览器删除规则的函数:
function deleteRule(sheet, index){
if (sheet.deleteRule){
sheet.deleteRule(index);
} else if (sheet.removeRule){
sheet.removeRule(index);
}
}
deleteRule(document.styleSheets[0], 0);
(3) 元素大小
*偏移量
偏移量
(offset dimension),包括元素在屏幕上占用的所有可见的空间。元素的可见大小由其高度、宽度决定,包括所有内边距、滚动条和边框大小(注意,不包括外边距)。
取得元素偏移量的4个属性:
(1) offsetHeight:元素在垂直方向上占用的空间大小,以像素计。包括元素的高度、(可见的) 水平滚动条的高度、上边框高度和下边框高度。
(2) offsetWidth:元素在水平方向上占用的空间大小,以像素计。包括元素的宽度、(可见的)垂 直滚动条的宽度、左边框宽度和右边框宽度。
(3) offsetLeft:元素的左外边框至包含元素的左内边框之间的像素距离。
(4) offsetTop:元素的上外边框至包含元素的上内边框之间的像素距离。
*
包含元素
的引用保存在offsetParent 属性
中。offsetParent 属性不一定与 parentNode 的值相等。`
某个元素在页面上的偏移量
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;
}
一般来说, 页面中的所有元素都会被包含在几个<div>元素中,而这些<div>元素的 offsetParent 又是 <body>元素,所以
getElementLeft()
与getElementTop()
会返回与 offsetLeft 和 offsetTop 相同的值。
* 客户区大小
客户区大小
(client dimension),指的是元素内容及其内边距所占据的空间大小。
有关客户区 大小的属性有两个:clientWidth 和 clientHeight。
clientWidth 属性
是元素内容区宽度加 上左右内边距宽度;
clientHeight 属性
是元素内容区高度加上上下内边距高度。
要确定浏览器视口大小
,可以使用 document.documentElement 或 document.body(在 IE7 之前的版本中)的 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
}; }
}
*滚动大小
滚动大小
(scroll dimension),指的是包含滚动内容的元素的大小。
滚动大小相关的属性:
(1) scrollHeight:在没有滚动条的情况下,元素内容的总高度。
(2) scrollWidth:在没有滚动条的情况下,元素内容的总宽度。
(3) scrollLeft:被隐藏在内容区域左侧的像素数。通过设置这个属性可以改变元素的滚动位置。
(4) scrollTop:被隐藏在内容区域上方的像素数。通过设置这个属性可以改变元素的滚动位置。
1.在确定文档的总高度
时(包括基于视口的最小高度时),必须取得 scrollWidth/clientWidth 和 scrollHeight/clientHeight 中的最大值,才能保证在跨浏览器的环境下得到精确的结果。
var docHeight = Math.max(document.documentElement.scrollHeight, document.documentElement.clientHeight);
var docWidth = Math.max(document.documentElement.scrollWidth, document.documentElement.clientWidth);
- 通过 scrollLeft 和 scrollTop 属性既可以确定元素当前滚动的状态,也可以设置元素的滚动位 置。在元素尚未被滚动时,这两个属性的值都等于 0。如果元素被垂直滚动了,那么 scrollTop 的值 会大于 0,且表示元素上方不可见内容的像素高度。如果元素被水平滚动了,那么 scrollLeft 的值会 大于 0,且表示元素左侧不可见内容的像素宽度。这两个属性都是可以设置的,因此将元素的 scrollLeft 和 scrollTop 设置为 0,就可以重置元素的滚动位置。
* 确定元素大小
getBoundingClientRect()方法
:
- 这个方法返回会一个矩形对象,包含 4 个属性:left、top、right 和 bottom。
3.遍历
“DOM2 级遍历和范围”模块定义了两个用于辅助完成顺序遍历 DOM 结构的类型:
NodeIterator
和TreeWalker
。这两个类型能够基于给定的起点对 DOM 结构执行深度优先
(depth-first)的遍历操作。
检测浏览器 对 DOM2 级遍历能力的支持情况:
var supportsTraversals = document.implementation.hasFeature("Traversal", "2.0");
var supportsNodeIterator = (typeof document.createNodeIterator == "function");
var supportsTreeWalker = (typeof document.createTreeWalker == "function");
(1) NodeIterator
document.createNodeIterator()方法:
* 接受下列 4 个参数
:
(1) root:想要作为搜索起点的树中的节点。
(2) whatToShow:表示要访问哪些节点的数字代码。
(3) filter:是一个 NodeFilter 对象,或者一个表示应该接受还是拒绝某种特定节点的函数。
(4) entityReferenceExpansion:布尔值,表示是否要扩展实体引用。这个参数在 HTML 页面中没有用,因为其中的实体引用不能扩展。
* whatToShow
参数是一个位掩码,通过应用一或多个过滤器(filter)来确定要访问哪些节点。
(1) NodeFilter.SHOW_ALL:显示所有类型的节点。
(2) NodeFilter.SHOW_ELEMENT:显示元素节点。
(3) NodeFilter.SHOW_ATTRIBUTE:显示特性节点。由于 DOM 结构原因,实际上不能使用这个值。
(4) NodeFilter.SHOW_TEXT:显示文本节点。
(5) NodeFilter.SHOW_CDATA_SECTION:显示 CDATA 节点。对 HTML 页面没有用。
(6) NodeFilter.SHOW_ENTITY_REFERENCE:显示实体引用节点。对 HTML 页面没有用。
(7) NodeFilter.SHOW_ENTITYE:显示实体节点。对 HTML 页面没有用。
(8) NodeFilter.SHOW_PROCESSING_INSTRUCTION:显示处理指令节点。对 HTML 页面没有用。
(9) NodeFilter.SHOW_COMMENT:显示注释节点。
(10) NodeFilter.SHOW_DOCUMENT:显示文档节点。
(11) NodeFilter.SHOW_DOCUMENT_TYPE:显示文档类型节点。
(12) NodeFilter.SHOW_DOCUMENT_FRAGMENT:显示文档片段节点。对 HTML 页面没有用。
(13) NodeFilter.SHOW_NOTATION:显示符号节点。对 HTML 页面没有用。
除了 NodeFilter.SHOW_ALL 之外,可以使用按位或操作符来组合多个选项。
var whatToShow = NodeFilter.SHOW_ELEMENT | NodeFilter.SHOW_TEXT;
* 可以通过 createNodeIterator()方法的filter 参数
来指定自定义的NodeFilter 对象
,或者 指定一个功能类似节点过滤器(node filter)的函数。
- 每个 NodeFilter 对象只有一个方法,即
accept Node()
;如果应该访问给定的节点,该方法返回NodeFilter.FILTER_ACCEPT,
如果不应该访问给 定的节点,该方法返回NodeFilter.FILTER_SKIP
。- 由于 NodeFilter 是一个抽象的类型,因此不能 直接创建它的实例。在必要时,只要创建一个包含 acceptNode()方法的对象,然后将这个对象传入 createNodeIterator()中即可。
var filter = {
acceptNode: function(node){
return node.tagName.toLowerCase() == "p" ?
NodeFilter.FILTER_ACCEPT :
NodeFilter.FILTER_SKIP;
}
};
var iterator = document.createNodeIterator(root,
NodeFilter.SHOW_ELEMENT, filter, false);
*filter参数
也可以是一个与 acceptNode()方法类似的函数,如果不指定过滤器,那么应该在第三个参数的位置上传入 null。
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 类型的两个主要方法是 nextNode()
和 previousNode()
。
- 在刚刚创建的 NodeIterator 对象中,有一个内部指针指向根节点,因此第一次调用
nextNode()
会 返回根节点。当遍历到 DOM 子树的最后一个节点时,nextNode()返回 null。
2.previousNode()
方法 的工作机制类似。当遍历到 DOM 子树的最后一个节点,且 previousNode()返回根节点之后,再次调 用它就会返回 null。
<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>
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
这个类型还提供了下列用于在不同方向上遍历 DOM 结构的方法。
(1) parentNode():遍历到当前节点的父节点;
(2) firstChild():遍历到当前节点的第一个子节点;
(3) lastChild():遍历到当前节点的最后一个子节点;
(4) nextSibling():遍历到当前节点的下一个同辈节点;
(5) previousSibling():遍历到当前节点的上一个同辈节点。
document.createTreeWalker()方法:
接受的
4 个参数
与 document.createNodeIterator()方法相同:作为遍历起点的根节点
、要显示的节点类型
、过滤 器
和一个表示是否扩展实体引用的布尔值
。
filter 可以返回的值有所不同。除了
NodeFilter.FILTER_ACCEPT
和NodeFilter. FILTER_SKIP
之外,还可以使用NodeFilter.FILTER_REJECT
。在使用 NodeIterator 对象时, NodeFilter.FILTER_SKIP 与 NodeFilter.FILTER_REJECT 的作用相同:跳过指定的节点。但在使用 TreeWalker 对象时,NodeFilter.FILTER_SKIP
会跳过相应节点继续前进到子树中的下一个节点, 而 NodeFilter.FILTER_REJECT 则会跳过相应节点及该节点的整个子树。
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();
}
currentNode属性:
表示任何遍历方法在上一次遍历中返回的节点。通过设置这个属性也可以修改遍历继续进行的起点。
var node = walker.nextNode();
alert(node === walker.currentNode); //true walker.currentNode = document.body; //修改起点
4. 范围
(1) DOM中的范围
DOM2 级在 Document 类型中定义了
createRange()方法
。在兼容 DOM 的浏览器中,这个方法属于 document 对象。使用 hasFeature()或者直接检测该方法,都可以确定浏览器是否支持范围。
var supportsRange = document.implementation.hasFeature("Range", "2.0");
var alsoSupportsRange = (typeof document.createRange == "function");
createRange()方法:
var range = document.createRange();
- 与节点类似,新创建的范围也直接与创建它的文档关联在一起,不能用于其他文档。
- 每个范围由一个
Range 类型
的实例表示,这个实例拥有很多属性和方法。下列属性提供了当前范 围在文档中的位置信息。
(1) startContainer:包含范围起点的节点(即选区中第一个节点的父节点)。
(2) startOffset:范围在 startContainer 中起点的偏移量。如果 startContainer 是文本节点、注释节点或 CDATA 节点,那么 startOffset 就是范围起点之前跳过的字符数量。否则,
startOffset 就是范围中第一个子节点的索引。
(3) endContainer:包含范围终点的节点(即选区中最后一个节点的父节点)。
(4) endOffset:范围在 endContainer 中终点的偏移量(与 startOffset 遵循相同的取值规则)。
(5) commonAncestorContainer:startContainer 和 endContainer 共同的祖先节点在文档树中位置最深的那个。
*用 DOM 范围实现简单选择
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");
range1.selectNode(p1);
range2.selectNodeContents(p1);
- 在调用 selectNode()时,startContainer、endContainer 和 commonAncestorContainer 都等于传入节点的父节点,也就是这个例子中的 document.body。而 startOffset 属性等于给定节 点在其父节点的 childNodes 集合中的索引(在这个例子中是 1——因为兼容 DOM 的浏览器将空格算 作一个文本节点),endOffset 等于 startOffset 加 1(因为只选择了一个节点)。
- 在调用 selectNodeContents()时,startContainer、endContainer 和 commonAncestorConta- iner 等于传入的节点,即这个例子中的<p>元素。而 startOffset 属性始终等于 0,因为范围从给定节 点的第一个子节点开始。最后,endOffset 等于子节点的数量(node.childNodes.length),在这个例 子中是 2。
(1)
setStartBefore(refNode)
:将范围的起点设置在 refNode 之前,因此 refNode 也就是范围 选区中的第一个子节点。同时会将 startContainer 属性设置为 refNode.parentNode,将
startOffset 属性设置为 refNode 在其父节点的 childNodes 集合中的索引。
(2)setStartAfter(refNode)
:将范围的起点设置在 refNode 之后,因此 refNode 也就不在范 围之内了,其下一个同辈节点才是范围选区中的第一个子节点。同时会将 startContainer 属 性设置为 refNode.parentNode,将 startOffset 属性设置为 refNode 在其父节点的
childNodes 集合中的索引加 1。
(3)setEndBefore(refNode)
:将范围的终点设置在 refNode 之前,因此 refNode 也就不在范围之内了,其上一个同辈节点才是范围选区中的最后一个子节点。同时会将 endContainer 属性设置为 refNode.parentNode,将 endOffset 属性设置为 refNode 在其父节点的 childNodes集合中的索引。
(4)setEndAfter(refNode)
:将范围的终点设置在 refNode 之后,因此 refNode 也就是范围选区
中的最后一个子节点。同时会将 endContainer 属性设置为 refNode.parentNode,将endOffset 属性设置为 refNode 在其父节点的 childNodes 集合中的索引加 1。
* 用 DOM 范围实现复杂选择
setStart()和 setEnd()方法:
1.这两个方法都接受
两个参数
:一个参 照节点和一个偏移量值。
- 对
setStart()
来说,参照节点会变成 startContainer,而偏移量值会变成 startOffset。对于setEnd()
来说,参照节点会变成 endContainer,而偏移量值会变成 endOffset。
选择前面 HTML 示例代码中从"Hello"的"llo"到"world!"的"o"
var p1 = document.getElementById("p1");
helloNode = p1.firstChild.firstChild;
worldNode = p1.lastChild;
var range = document.createRange();
range.setStart(helloNode, 2);
range.setEnd(worldNode, 3);
*操作 DOM 范围中的内容
-
在创建范围时 ,内部会为这个范围创建一个
文档片段
,范围所属的全部节点都被添加到了这个文档 片段中。 -
范围知道自身缺少哪些开标签和闭标签,它能够重新构建有效的 DOM 结构以便我们对其进 行操作。
<p><b>He</b><b>llo</b> world!</p>
(1) deleteContents()
:这个方法能够从文档中删除范围所包含的内容。
var p1 = document.getElementById("p1");
helloNode = p1.firstChild.firstChild;
worldNode = p1.lastChild;
var range = document.createRange();
range.setStart(helloNode, 2);
range.setEnd(worldNode, 3);
range.deleteContents();
执行以上代码后,页面中会显示如下 HTML 代码:
<p><b>He</b>rld!</p>
(2) extractContents()
也会从文档中移除范围选区,extractContents()会返回范围的文档片段。
var p1 = document.getElementById("p1");
helloNode = p1.firstChild.firstChild;
worldNode = p1.lastChild;
var range = document.createRange();
range.setStart(helloNode, 2);
range.setEnd(worldNode, 3);
var fragment = range.extractContents();
p1.parentNode.appendChild(fragment);
执行以上代码后,页面中会显示如下 HTML 代码:
<p><b>He</b>rld!</p>
<b>llo</b> wo
(3)cloneContents()
创建范围对象的一个副本,然后在文档的其他地方插入该副本。
var p1 = document.getElementById("p1");
helloNode = p1.firstChild.firstChild;
worldNode = p1.lastChild;
var range = document.createRange();
range.setStart(helloNode, 2);
range.setEnd(worldNode, 3);
var fragment = range.cloneContents();
p1.parentNode.appendChild(fragment);
执行以上代码后,页面中会显示如下 HTML 代码:
<p><b>Hello</b> world!</p>
<b>llo</b> wo
*插入 DOM 范围中的内容
insertNode()
方法可以向范围选区的开始处插入一个节点。
var p1 = document.getElementById("p1");
helloNode = p1.firstChild.firstChild;
worldNode = p1.lastChild;
var 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);
执行以上代码后,页面中会显示如下 HTML 代码:
<p id="p1"><b>He<span style="color: red">Inserted text</span>llo</b> world</p>
surroundContents()
方法:这个方法接受一个参数,即环绕范围内容的节点。在环绕范围插入内容时,后台会执行下列步骤。
(1) 提取出范围中的内容(类似执行 extractContent());
(2) 将给定节点插入到文档中原来范围所在的位置上;
(3) 将文档片段的内容添加到给定节点中。
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);
会给范围选区加上一个黄色的背景。得到的 HTML 代码如下所示:
<p><b><span style="background-color:yellow">Hello</span></b> world!</p>
*折叠 DOM 范围
1.collapse()方法
来折叠范围,这个方法接受一个参数,一个布尔值,表示要折叠到范围的哪 一端。参数true表示折叠到范围的起点,参数false表示折叠到范围的终点。
- 要确定范围已经折叠完 毕,可以检查
collapsed 属性
。
检测某个范围是否处于折叠状态,可以帮我们确定范围中的两个节点是否紧密相邻。
<p id="p1">Paragraph 1</p><p id="p2">Paragraph 2</p>
var p1 = document.getElementById("p1"),
p2 = document.getElementById("p2"),
range = document.createRange();
range.setStartAfter(p1);
range.setStartBefore(p2);
alert(range.collapsed); //输出 true
*比较 DOM 范围
compareBoundaryPoints()方法
来确定这些范围是否有公共的边界(起点或终点)。
- 这个方法接受
两个参数
:表示比较方式的常量值和要比较的范围。
表示比较方式的常量值如下所示:
(1) Range.START_TO_START(0):比较第一个范围和第二个范围的起点;
(2) Range.START_TO_END(1):比较第一个范围的起点和第二个范围的终点; Range.END_TO_END(2):比较第一个范围和第二个范围的终点;
(3) Range.END_TO_START(3):比较第一个范围的终点和第一个范围的起点。
- compareBoundaryPoints()方法可能的
返回值
如下:如果第一个范围中的点位于第二个范围中的 点之前,返回-1;如果两个点相等,返回 0;如果第一个范围中的点位于第二个范围中的点之后,返回 1。
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();
*清理 DOM 范围
detach()方法
:从创建范围的文档中分离出该范围。调用 detach()之后,就可以放心地解除对范围的引用,从而让垃圾回收机制回收其内存了。
range.detach(); //从文档中分离
range = null; //解除引用
(2) IE8 及更早版本中的范围
- IE8 及早期版本支持一种类 似的概念,即
文本范围
(text range)。文本范围是 IE 专有的特性,其他浏览器都不支持。 - 文 本范围处理的主要是文本(不一定是 DOM 节点)。通过<body>、<button>、<input>和<textarea> 等这几个元素,可以调用
createTextRange()方法
来创建文本范围。
*用 IE 范围实现简单的选择
findText()方法
:
- 这个方法会找到
第一次出现的给定文本
,并将范围移过来以环绕该文本。如果没有找到文本,这个方法返回 false;否则返回 true。 - 范围的
text 属性
:这个属性返回范围中包含的文本。 - findText()
第二个参数
,一个表示向哪个方向继续搜索的数值。负值表示应该从当前位置向后搜索,而正值表示应该从当前位置向前搜索。
<p id="p1"><b>Hello</b> world!</p>
var range = document.body.createTextRange();
var found = range.findText("Hello");
alert(found); //true
alert(range.text); //"Hello"
var foundAgain = range.findText("Hello", 1);
moveToElementText()方法:
- IE 中与 DOM 中的 selectNode()方法,这个方法接受一 个 DOM 元素,并选择该元素的所有文本。
var range = document.body.createTextRange();
var p1 = document.getElementById("p1");
range.moveToElementText(p1);
htmlText 属性:
- 在文本范围中包含 HTML 的情况下,可以使用 htmlText 属性取得范围的全部内容,包括 HTML 和文本。
alert(range.htmlText);
parentElement()方法:
- 与 DOM 的 commonAncestorContainer 属性类似,得到的父元素始终都可以反映文本选区的父节点。
* 使用 IE 范围实现复杂的选择
在 IE 中创建复杂范围的方法,就是以特定的增量向四周移动范围。为此,IE 提供了 4 个方法:
move()、moveStart()、moveEnd()和 expand()
。这些方法都接受两个参数:移动单位
和移动单位的数量
。其中,移动单位是下列一种字符串值。
(1) "character":逐个字符地移动。
(2) "word":逐个单词(一系列非空格字符)地移动。
(3) "sentence":逐个句子(一系列以句号、问号或叹号结尾的字符)地移动。
(4) "textedit":移动到当前范围选区的开始或结束位置。
-
moveStart()
方法可以移动范围的起点。 -
moveEnd()
方法可以移动范围的终点。 -
expand()
方法可以将范围规范化。换句话说,expand()方法的作用是将任何部分选择的文 本全部选中。例如,当前选择的是一个单词中间的两个字符,调用 expand("word")可以将整个单词都 包含在范围之内。 -
move()方法
则首先会折叠当前范围(让起点和终点相等),然后再将范围移动指定的单位数量。调用 move()之后,范围的起点和终点相同,因此必须再使用 moveStart()或 moveEnd()创建新 的选区。
range.moveStart("word", 2); //起点移动 2 个单词 range.moveEnd("character", 1); //终点移动 1 个字符
range.move("character", 5); //移动 5 个字符
* 操作 IE 范围中的内容
在 IE 中操作范围中的内容可以使用text 属性
或 pasteHTML()方法
。
var range = document.body.createTextRange();
range.findText("Hello");
range.pasteHTML("<em>Howdy</em>");
执行这些代码后,会得到如下 HTML:
<p id="p1"><b><em>Howdy</em></b> world!</p>
*折叠 IE 范围
- IE 为范围提供的
collapse()方法
与相应的 DOM 方法用法一样:传入 true 把范围折叠到起点, 传入 false 把范围折叠到终点。 - 没有对应的 collapsed 属性让我们知道范围是否已经折叠完毕。为此,必须使用
boundingWidth 属性
,该属性返回范围的宽度(以像素为单位)。如果 boundingWidth 属性等于 0, 就说明范围已经折叠了
var isCollapsed = (range.boundingWidth == 0);
* 比较 IE 范围
compareEndPoints()方法:
- 与 DOM 范围的 compareBoundaryPoints()方法类似。这个 方法接受两个参数:比较的类型和要比较的范围。
- 比较类型的取值范围是下列几个字符串值:StartToStart"、"StartToEnd"、"EndToEnd"和"EndToStart"。
- 如果第一个范 围的边界位于第二个范围的边界前面,返回-1;如果二者边界相同,返回 0;如果第一个范围的边界位 于第二个范围的边界后面,返回 1。
isEqual()
用于确定两个范围是否相等。
inRange()
用于确定一个范围是否包含另一个范围。
* 复制 IE 范围
duplicate()方法
可以复制文本范围,结果会创建原范围的一个副本。