JavaScript < ES5、ES6、ES7、… >

JavaScript 高级程序设计(第12章 DOM2和DOM3

2018-08-14  本文已影响16人  yinxmm

第12章 DOM2和DOM3

  1. DOM2 级核心(DOM Level 2 Core):在 1 级核心基础上构建,为节点添加了更多方法和属性。
  2. DOM2 级视图(DOM Level 2 Views):为文档定义了基于样式信息的不同视图。
  3. DOM2 级事件(DOM Level 2 Events):说明了如何使用事件与 DOM 文档交互。
  4. DOM2 级样式(DOM Level 2 Style):定义了如何以编程方式来访问和改变 CSS 样式信息。
  5. DOM2 级遍历和范围(DOM Level 2 Traversal and Range):引入了遍历 DOM 文档和选择其特定
    部分的新接口。
  6. 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 的所有需求,同时提供更好的错误处理及特性检测能力。从某种意义上讲,实现这一目的很大程度意味着对命名空间的支持

  1. DOM2 级核 心”没有引入新类型,它只是在 DOM1 级的基础上通过增加新方法和新属性来增强了既有类型。
  2. “DOM3 级核心”同样增强了既有类型,但也引入了一些新类型。

(1) 针对XML命名空间的变化

  1. 有了 XML 命名空间,不同 XML 文档的元素就可以混合在一起,共同构成格式良好的文档,而不必担心发生命名冲突。
  2. HTML 不支持 XML 命名空间,但 XHTML 支持 XML 命名空间。
  3. 命名空间要使用 xmlns 特性来指定
  4. 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>
</html>
<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 类型的变化
  1. 在 DOM2 级中,Node 类型包含下列特定于命名空间的属性

(1) localName: 不带命名空间前缀的节点名称。
(2) nameSpaceURI: 命名空间URI或者(在未指定的情况下是)null。
(3) prefix:命名空间前缀或者(在未指定的情况下是)null。
nodeName 等于 prefix+":"+ localName。

  1. 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

  1. 前两 个属性表示的是文档类型声明中的两个信息段
  2. 属性 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()方法:

  1. 这个方法的用途是从一个文档中取得一个节点,然后将其导入到另一个文档,使其成为这个文档结构的一部分。
  2. 每个节点都有一个 ownerDocument 属性,表示所属的文档。如果调用 appendChild()时传入的节点属于不同的文档(ownerDocument 属性的值不一样),则会导致错误。但在调用 importNode()时传入 不同文档的节点则会返回一个新节点,这个新节点的所有权归当前文档所有。
  3. 接受两个参数:要复制的节点和一个表示是否复制子节点的布尔值。
//导入节点及其所有子节点
var newNode = document.importNode(oldNode, true); 
document.body.appendChild(newNode);

defaultView 的属性:

  1. DOM2 级视图”模块属性,其中保存着一个指针,指向拥有给 定文档的窗口(或框架)
  2. 在 IE 中有一个等价的属性名叫 parentWindow(Opera 也支持这个属性)。
var parentWindow = document.defaultView || document.parentWindow;
DOM2 级核心”还为document.implementation 对象规定了 两个新方法:createDocumentType()createDocument()
  1. createDocumentType() 方法 : 于创建一个新的 DocumentType 节点,接受 3 个参数:文档类型名称、publicId、systemId。
  2. 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()
  1. createHTMLDocument()方法:这个方法的用途是创建一个完整的 HTML 文档,包括<html>、<head>、<title>和 <body>元素。这个方法只接受一个参数,即新创建文档的标题(放在<title>元素中的字符串),返回 新的 HTML 文档
* Node 类型的变化

isSupported()方法

  1. DOM1 级为 document.implementation引入的hasFeature()方法类似。
  2. 用于确定当前节点具有 什么能力。这个方法也接受相同的两个参数:特性名特性版本号。如果浏览器实现了相应特性,而且能够基于给定节点执行该特性,isSupported()就返回 true。
DOM3 级引入了两个辅助比较节点的方法:isSameNode()isEqualNode()
  1. isSameNode()方法接受 一个节点参数,并在传入节点与引用的节点相同时返回 true。
  2. 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 个参数:要设置的键、实际的数据(可以是任何数据类型)和处理函数。
  1. 传入 setUserData()中的处理函数会在带有数据的节点被复制、删除、重命名或引入一个文档时调用,因而你可以事先决定在上述操作发生时如何处理用户数据。
  2. 处理函数接受5 个参数:表示操作类 型的数值(1 表示复制,2 表示导入,3 表示删除,4 表示重命名)、数据键、数据值、源节点和目标节点。
  3. 在删除节点时,源节点是 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"

* 框架的变化
  1. 框架和内嵌框架分别用HTMLFrameElementHTMLIFrameElement 表示,它们在 DOM2 级中都有 了一个新属性,名叫 contentDocument。这个属性包含一个指针,指向表示框架内容的文档对象。
  2. 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) 访问元素的样式

  1. 任何支持 style 特性的 HTML 元素在 JavaScript 中都有一个对应的 style 属性。
  2. 这个 style 对象 是 CSSStyleDeclaration 的实例,包含着通过 HTML 的 style 特性指定的所有样式信息,但不包含与外部样式表或嵌入样式表经层叠而来的样式。
  3. 在 style 特性中指定的任何 CSS 属性都将表现为这个 style 对象的相应属性。
  4. 对于使用短划线(分隔不同的词汇,例如 background-image)的 CSS 属性 名,必须将其转换成驼峰大小写形式,才能通过 JavaScript 来访问。
  1. 由于 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"或者一个空字符串)。

  1. 读取模式下,cssText 返回浏览器对 style特性中 CSS 代码的内部表示。在写入模式下,赋给 cssText 的值会重写整个 style 特性的值;也就是 说,以前通过 style 特性指定的样式信息都将丢失。

  2. 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);
}
  1. getPropertyCSSValue()方法,它返回一个包含两个属性的 CSSValue 对象,这两个属性分别是:cssTextcssValueType

(1) cssText 属性的值与 getPropertyValue()返回的值相同。
(2) cssValueType 属性则是一个数值常量,表示值的类型:0 表示继承的值,1 表示基本的值,2 表示 值列表,3 表示自定义的值。

* 计算的样式

DOM2 级样式”增强了 document.defaultView,提供了 getComputedStyle()方法

  1. 这个方法接受两个参数:要取得计算样式的元素和一个伪元素字符串(例 如":after")。如果不需要伪元素信息,第二个参数可以是 null。
  2. 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"
  1. 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) 操作样式表

  1. CSSStyleSheet 类型表示的是样式表,包括通过<link>元素包含的样式表和在<style>元素中定义的样式表。

  2. <link>元素,<style>元素,这两个元素本身分别是由 HTMLLinkElement 和 HTMLStyleElement 类型表示的。

使用下面的代码可以确定浏览器是否支持 DOM2 级样式表:

var supportsDOM2StyleSheets = 
document.implementation.hasFeature("StyleSheets", "2.0");
  1. 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);
}
  1. 直接通过<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);
  1. 通过 scrollLeft 和 scrollTop 属性既可以确定元素当前滚动的状态,也可以设置元素的滚动位 置。在元素尚未被滚动时,这两个属性的值都等于 0。如果元素被垂直滚动了,那么 scrollTop 的值 会大于 0,且表示元素上方不可见内容的像素高度。如果元素被水平滚动了,那么 scrollLeft 的值会 大于 0,且表示元素左侧不可见内容的像素宽度。这两个属性都是可以设置的,因此将元素的 scrollLeft 和 scrollTop 设置为 0,就可以重置元素的滚动位置。
* 确定元素大小

getBoundingClientRect()方法:

  1. 这个方法返回会一个矩形对象,包含 4 个属性:left、top、right 和 bottom。

3.遍历

“DOM2 级遍历和范围”模块定义了两个用于辅助完成顺序遍历 DOM 结构的类型:NodeIteratorTreeWalker。这两个类型能够基于给定的起点对 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)的函数。
  1. 每个 NodeFilter 对象只有一个方法,即 accept Node();如果应该访问给定的节点,该方法返回 NodeFilter.FILTER_ACCEPT,如果不应该访问给 定的节点,该方法返回 NodeFilter.FILTER_SKIP
  2. 由于 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()
  1. 在刚刚创建的 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_ACCEPTNodeFilter. 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();
  1. 与节点类似,新创建的范围也直接与创建它的文档关联在一起,不能用于其他文档。
  2. 每个范围由一个 Range 类型的实例表示,这个实例拥有很多属性和方法。下列属性提供了当前范 围在文档中的位置信息。

(1) startContainer:包含范围起点的节点(即选区中第一个节点的父节点)。
(2) startOffset:范围在 startContainer 中起点的偏移量。如果 startContainer 是文本节点、注释节点或 CDATA 节点,那么 startOffset 就是范围起点之前跳过的字符数量。否则,
startOffset 就是范围中第一个子节点的索引。
(3) endContainer:包含范围终点的节点(即选区中最后一个节点的父节点)。
(4) endOffset:范围在 endContainer 中终点的偏移量(与 startOffset 遵循相同的取值规则)。
(5) commonAncestorContainer:startContainer 和 endContainer 共同的祖先节点在文档树中位置最深的那个。

*用 DOM 范围实现简单选择

selectNode()selectNodeContents():

  1. 接受一个参数,即一个 DOM 节点,然后使用该节点中的信息来填充范围。
  2. 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);
  1. 在调用 selectNode()时,startContainer、endContainer 和 commonAncestorContainer 都等于传入节点的父节点,也就是这个例子中的 document.body。而 startOffset 属性等于给定节 点在其父节点的 childNodes 集合中的索引(在这个例子中是 1——因为兼容 DOM 的浏览器将空格算 作一个文本节点),endOffset 等于 startOffset 加 1(因为只选择了一个节点)。
  2. 在调用 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.这两个方法都接受两个参数:一个参 照节点和一个偏移量值。

  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 范围中的内容
  1. 在创建范围时 ,内部会为这个范围创建一个文档片段,范围所属的全部节点都被添加到了这个文档 片段中。

  2. 范围知道自身缺少哪些开标签和闭标签,它能够重新构建有效的 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表示折叠到范围的终点。

  1. 要确定范围已经折叠完 毕,可以检查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. 这个方法接受两个参数:表示比较方式的常量值和要比较的范围。
    表示比较方式的常量值如下所示:

(1) Range.START_TO_START(0):比较第一个范围和第二个范围的起点;
(2) Range.START_TO_END(1):比较第一个范围的起点和第二个范围的终点;  Range.END_TO_END(2):比较第一个范围和第二个范围的终点;

(3) Range.END_TO_START(3):比较第一个范围的终点和第一个范围的起点。

  1. 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 及更早版本中的范围

  1. IE8 及早期版本支持一种类 似的概念,即文本范围(text range)。文本范围是 IE 专有的特性,其他浏览器都不支持。
  2. 文 本范围处理的主要是文本(不一定是 DOM 节点)。通过<body>、<button>、<input>和<textarea> 等这几个元素,可以调用 createTextRange()方法来创建文本范围。
*用 IE 范围实现简单的选择

findText()方法

  1. 这个方法会找到第一次出现的给定文本,并将范围移过来以环绕该文本。如果没有找到文本,这个方法返回 false;否则返回 true。
  2. 范围的 text 属性:这个属性返回范围中包含的文本。
  3. 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()方法:

  1. IE 中与 DOM 中的 selectNode()方法,这个方法接受一 个 DOM 元素,并选择该元素的所有文本。
var range = document.body.createTextRange();
var p1 = document.getElementById("p1");
range.moveToElementText(p1);

htmlText 属性:

  1. 在文本范围中包含 HTML 的情况下,可以使用 htmlText 属性取得范围的全部内容,包括 HTML 和文本。
alert(range.htmlText);

parentElement()方法:

  1. 与 DOM 的 commonAncestorContainer 属性类似,得到的父元素始终都可以反映文本选区的父节点。
* 使用 IE 范围实现复杂的选择

在 IE 中创建复杂范围的方法,就是以特定的增量向四周移动范围。为此,IE 提供了 4 个方法: move()、moveStart()、moveEnd()和 expand()。这些方法都接受两个参数:移动单位移动单位的数量。其中,移动单位是下列一种字符串值。

(1) "character":逐个字符地移动。
(2) "word":逐个单词(一系列非空格字符)地移动。
(3) "sentence":逐个句子(一系列以句号、问号或叹号结尾的字符)地移动。
(4) "textedit":移动到当前范围选区的开始或结束位置。

  1. moveStart()方法可以移动范围的起点。
  2. moveEnd()方法可以移动范围的终点。
  3. expand()方法可以将范围规范化。换句话说,expand()方法的作用是将任何部分选择的文 本全部选中。例如,当前选择的是一个单词中间的两个字符,调用 expand("word")可以将整个单词都 包含在范围之内。
  4. 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 范围
  1. IE 为范围提供的 collapse()方法与相应的 DOM 方法用法一样:传入 true 把范围折叠到起点, 传入 false 把范围折叠到终点。
  2. 没有对应的 collapsed 属性让我们知道范围是否已经折叠完毕。为此,必须使用 boundingWidth 属性,该属性返回范围的宽度(以像素为单位)。如果 boundingWidth 属性等于 0, 就说明范围已经折叠了
var isCollapsed = (range.boundingWidth == 0);
* 比较 IE 范围

compareEndPoints()方法:

  1. 与 DOM 范围的 compareBoundaryPoints()方法类似。这个 方法接受两个参数:比较的类型和要比较的范围。
  2. 比较类型的取值范围是下列几个字符串值:StartToStart"、"StartToEnd"、"EndToEnd"和"EndToStart"。
  3. 如果第一个范 围的边界位于第二个范围的边界前面,返回-1;如果二者边界相同,返回 0;如果第一个范围的边界位 于第二个范围的边界后面,返回 1。

isEqual()用于确定两个范围是否相等。
inRange()用于确定一个范围是否包含另一个范围。

* 复制 IE 范围

duplicate()方法可以复制文本范围,结果会创建原范围的一个副本。

上一篇下一篇

猜你喜欢

热点阅读