第十二章 DOM2和DOM3

350 阅读14分钟

DOM变化

针对XML命名空间的变化

命名空间要使用 xmlns 特性来指定。XHTML 的命名空间是 www.w3.org/1999/xhtml ,在任何格式良好 XHTML 页面中,都应该将其包含在<html>元素中。如下例中,混合了 XHTML 和 SVG 语言

<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> 
  1. Node类型的变化

    DOM2级中,Node类型包含下列特定于命名空间的属性:

    • localName:不带命名空间前缀的节点名称
    • namespaceURI:命名空间 URI ,在未指定的情况下是null
    • prefix:命名空间前缀或者,在未指定的情况下是null

    DOM3级中,引入了下列与命名空间有关的方法:

    • isDefaultNamespace(namespaceURI):在指定的 namespaceURI 是当前节点的默认命名空间的情况下返回 true
    • lookupNamespaceURI(prefix):返回给定 prefix 的命名空间
    • lookupPrefix(namespaceURI):返回给定 namespaceURI 的前缀
    <html xmlns="http://www.w3.org/1999/xhtml"> 
      <head> 
      	<title>Example XHTML page</title> 
      </head> 
      <body> 
      	<s:svg xmlns:s="http://www.w3.org/2000/svg" version="1.1" 
      		viewBox="0 0 100 100" style="width:100%; height:100%"> 
      		<s:rect x="0" y="0" width="100" height="100" style="fill:red"/> 
      	</s:svg> 
      </body> 
    </html> 
    
    alert(document.body.isDefaultNamespace("http://www.w3.org/1999/xhtml"); //true 
    //假设 svg 中包含着对<s:svg>的引用
    alert(svg.lookupPrefix("http://www.w3.org/2000/svg")); //"s" 
    alert(svg.lookupNamespaceURI("s")); //"http://www.w3.org/2000/svg" 
    
  2. Document类型的变化

    DOM2级包含了下列与命名空间的方法:

    • 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", "*"); 
    
  3. 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 的特性节点。
  4. NameNodeMap类型的变化

    • getNamedItemNS(namespaceURI,localName):取得属于命名空间 namespaceURI 且名为localName 的项。
    • removeNamedItemNS(namespaceURI,localName):移除属于命名空间 namespaceURI 且为 localName 的项。
    • setNamedItemNS(node):添加 node,这个节点已经事先指定了命名空间信息。

其他方面的变化

  1. DocumentType类型的变化

    DocumentType类型新增了3个属性:publicId、systemId、internalSubset。

    <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN"  
    "http://www.w3.org/TR/html4/strict.dtd" 
    [<!ELEMENT name (#PCDATA)>] >
    

    在上例中,document.doctype.publicId 等于 "-//W3C//DTD HTML 4.01//EN",document.doctype.systemId 等于 "www.w3.org/TR/html4/st…" ,document.doctype.internalSubset 访问在文档声明中的额外定义,等于<!ELEMENT name (#PCDATA)>

  2. Document类型的变化

    • importNode():从一个文档中取得一个节点,然后将其导入到另一个文档,使其成为这个文档结构的一部分。接收两个参数:要复制的节点和否复制子结点的布尔值

    • defaultView属性:保存着一个指针,指向拥有给定文档的窗口(或框架)。

    • document.implementation新增两个方法

      • createDocumentType():创建新的DocumentType节点,接收三个参数:文档类型名称、publicId、systemId。只在创建新文档时有用

      • createDocument():创建新文档。接收三个参数:针对文档中元素的 namespaceURI、文档元素的标签名、新文档的文档类型

      • createHTMLDocument():创建一个完整的HTML文档。接收一个参数,新建的文档标题(放在<title>元素中的字符串),返回新的HTML文档

        //创建一个新的 HTML 4.01 Strict 文档类型
        var doctype = document.implementation.createDocumentType("html", "-//W3C//DTD HTML 4.01//EN","http://www.w3.org/TR/html4/strict.dtd"); 
        //创建一个空的新XML文档
        var doc = document.implementation.createDocument("", "root", null);
        //创建一个标题为New Doc的HTML文档
        var htmldoc = document.implementation.createHTMLDocument("New Doc"); 
        
  3. Node类型的变化

    • isSupported():确定当前节点具有什么能力。接收两个参数:特性名和特性版本号。如果浏览器实现了相应特性,而且能够基于给定节点执行该特性,isSupported()就返回 true。
    • isSameNode():判断传入节点与引用方法的节点是否为同一个节点
    • isEqualNode():判断传入节点与引用方法的节点是否相等(类型相同、属性相等、子节点相等)
    • setUserData():将数据指定给节点。接受三个参数:要设置的键、实际的数据、处理函数。传入 setUserData()中的处理函数会在带有数据的节点被复制、删除、重命名或引入一个文档时调用,因而你可以事先决定在上述操作发生时如何处理用户数据。处理函数接受 5 个参数:表示操作类型的数值(1 表示复制,2 表示导入,3 表示删除,4 表示重命名)、数据键、数据值、源节点和目标节点。在删除节点时,源节点是 null;除在复制节点时,目标节点均为 null。
    • getUserData():获取节点键的值
    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 
    
    document.body.setUserData("name", "Nicholas", function(){}); 
    var value = document.body.getUserData("name"); 
    
  4. 框架的变化

    contentDocument:包含一个指针,指向表示框架内容的文档对象

    var iframe = document.getElementById("myIframe"); 
    var iframeDoc = iframe.contentDocument; //在 IE8 以前的版本中无效
    

样式

访问元素的样式

通过style属性指定css样式访问元素的样式,短划线的属性转换为驼峰大小写的形式。

如"background-image"——"style.backgroundImage";"color"——"style.color"。只有float不能通过转换属性名的方式获取,因为float是js的保留字,获取float样式的方式:style.cssFloat,IE中为style.styleFloat

  1. DOM样式属性和方法

    • 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"或者一个空字符)
  2. 计算的样式

    getComputedStyle():接收两个参数:要取得计算样式的元素、一个伪元素字符串(null时返回包含当前元素的所有计算样式的对象)

操作样式表

  • disabled:表示样式表是否被禁用的布尔值。这个属性是可读/写的,将这个值设置为 true 可以禁用样式表
  • href:如果样式表是通过<link>包含的,则是样式表的 URL;否则,是 null
  • media:当前样式表支持的所有媒体类型的集合
  • ownerNode:指向拥有当前样式表的节点的指针,样式表可能是在 HTML 中通过<link>或<style/>引入的(在 XML 中可能是通过处理指令引入的)。如果当前样式表是其他样式表通过@import 导入的,则这个属性值为 null
  • parentStyleSheet:在当前样式表是通过@import 导入的情况下,这个属性是一个指向导入它的样式表的指针
  • title:ownerNode 中 title 属性的值。
  • type:表示样式表类型的字符串。对 CSS 样式表而言,这个字符串是"type/css"。
  • cssRules:样式表中包含的样式规则的集合
  • ownerRule:如果样式表是通过@import 导入的,这个属性就是一个指针,指向表示导入的规则;否则,值为 null
  • deleteRule(index):删除 cssRules 集合中指定位置的规则
  • insertRule(rule,index):向 cssRules 集合中指定的位置插入 rule 字符串
  1. CSS规则

    CSSRule 对象表示样式表中的每一条规则。实际上,CSSRule 是一个供其他多种类型继承的基类型,其中最常见的就是 CSSStyleRule 类型,表示样式信息,CSSStyleRule 对象包含下列属性:

    • cssText:返回整条规则对应的文本。由于浏览器对样式表的内部处理方式不同,返回的文可能会与样式表中实际的文本不一样
    • parentRule:如果当前规则是导入的规则,这个属性引用的就是导入规则;否则,这个值为null
    • parentStyleSheet:当前规则所属的样式表
    • selectorText:返回当前规则的选择符文本
    • style:一个 CSSStyleDeclaration 对象,可以通过它设置和取得规则中特定的样式值
    • type:表示规则类型的常量值。对于样式规则,这个值是 1
  2. 创建规则

    insertRule():向现有样式表中添加新规则。接收两个参数:规则文本、插入规则的索引

    sheet.insertRule("body { background-color: silver }", 0); //DOM 方法
    
  3. 删除规则

    deleteRule():从样式表中删除规则。接收一个参数:要删除规则的位置

    sheet.deleteRule(0); //删除样式表中的第一条规则 
    

元素大小

  1. 偏移量:只读

    包括元素在屏幕上占用的所有可见的空间

    • offsetHeight:元素在垂直方向上占用的空间大小,以像素计。包括元素的高度、(可见的)水平滚动条的高度、上边框高度和下边框高度
    • offsetWidth:元素在水平方向上占用的空间大小,以像素计。包括元素的宽度、(可见的)垂直滚动条的宽度、左边框宽度和右边框宽度
    • offsetLeft:元素的左外边框至包含元素的左内边框之间的像素距离
    • offsetTop:元素的上外边框至包含元素的上内边框之间的像素距离
  1. 客户区大小:只读

    元素内容及其内边距所占据的空间大小

    • clientWidth:元素内容宽度 + 左右内边距宽度
    • clientHeight:元素内容高度 + 上下内边距高度
  1. 滚动大小:可读可写

    包含滚动内容的元素的大小

    • scrollHeight:在没有滚动条的情况下,元素内容的总高度。
    • scrollWidth:在没有滚动条的情况下,元素内容的总宽度。
    • scrollLeft:被隐藏在内容区域左侧的像素数。通过设置这个属性可以改变元素的滚动位置。
    • scrollTop:被隐藏在内容区域上方的像素数。通过设置这个属性可以改变元素的滚动位置。
  2. 确定元素大小

    getBoundingClientRect():返回一个矩形对象,包含4个属性:left、top、right和bottom。这些属性给出了元素在页面中相对于视口的位置。

遍历

“DOM2 级遍历和范围”模块定义了两个用于辅助完成顺序遍历 DOM 结构的类型:NodeIterator和 TreeWalker。

NodeIterator

可以使用 document.createNodeIterator()创建新实例。这个方法接受下列 4 个参数。

  • root:搜索起点。
  • whatToShow:要访问节点的数字代码。
  • filter:一个 NodeFilter 对象,或者一个表示应该接受还是拒绝某种特定节点的函数。可以通过该参数指定自定义的NodeFilter对象。每个 NodeFilter 对象只有一个方法,即 acceptNode();如果需要访问给定的节点,该方法返回 NodeFilter.FILTER_ACCEPT,如果不需要访问给定的节点,该方法返回 NodeFilter.FILTER_SKIP。该参数也可以是一个与 acceptNode()方法类似的函数,如下例中的filter1所示
  • entityReferenceExpansion:布尔值,表示是否要扩展实体引用

NodeIterator类型的两个主要方法是nextNode()和previousNode()。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> 
//返回遍历中遇到的<li>元素
var div = document.getElementById("div1"); 
var filter = {
    acceptNode:function(node){
        return node.tagName.toLowerCase() == "li" ? NodeFilter.FILTER_ACCEPT : NodeFilter.FILTER_SKIP;
    }
}
var filter1 = function(node){ 
    return node.tagName.toLowerCase() == "li" ? NodeFilter.FILTER_ACCEPT : NodeFilter.FILTER_SKIP; 
}; 
var iterator = document.createNodeIterator(div, NodeFilter.SHOW_ELEMENT, filter1, false); 
var node = iterator.nextNode(); 
while (node !== null) { 
    console.log(node.tagName); //输出标签名
    node = iterator.nextNode(); 
} 

TreeWalker

TreeWalker除了包括nextNode()和previousNode()外,还包括下列不同方向上遍历DOM结构的方法:

  • parentNode():遍历到当前节点的父节点
  • firstChild():遍历到当前节点的第一个子节点
  • lastChild():遍历到当前节点的最后一个子节点
  • nextSibling():遍历到当前节点的下一个同辈节点
  • previousSibling():遍历到当前节点的上一个同辈节点

创建 TreeWalker 对象要使用 document.createTreeWalker()方法,该方法接收以下4个参数:

  • root:搜索起点。
  • whatToShow:要访问节点的数字代码。
  • filter:可取值除了NodeFilter.FILTER_ACCEPT 和 NodeFilter. FILTER_SKIP 之外,还可以使用 NodeFilter.FILTER_REJECT。NodeFilter.FILTER_SKIP 会跳过相应节点继续前进到子树中的下一个节点,而 NodeFilter.FILTER_REJECT 则会跳过相应节点及该节点的整个子树
  • entityReferenceExpansion:布尔值,表示是否要扩展实体引用

范围

“DOM2 级遍历和范围”模块定义了“范围”(range)接口

DOM中的范围

使用document.createRange() 方法创建DOM范围——Range类型的实例。实例中包含以下属性:

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

    selectNode() 和 selectNodeContents() 方法可以使用范围来选择文档中的一部分。两个方法都接收一个DOM节点参数。selectNode() 方法选择整个节点,包括其子结点;selectNodeContents() 方法只选择子节点

    <!DOCTYPE html> 
    <html> 
      <body> 
      	<p id="p1"><b>Hello</b> world!</p> 
      </body> 
    </html> 
    
    var range1 = document.createRange();
    var range2 = document.createRange();
    var p1 = document.getElementById("p1");
    range1.selectNode(p1);
    range2.selectNodeContents(p1);
    

    可以使用下列方法更精细地控制将哪些节点包含在范围内:

    • setStartBefore(refNode):将范围的起点设置在refNode之前
    • setStartAfter(refNode):将范围的起点设置在refNode之后
    • setEndBefore(refNode):将范围的终点设置在refNode之前
    • setEndAfter(refNode):将范围的终点设置在refNode之后
  2. 用DOM范围实现复杂选择

    使用setStart()和setEnd()方法创建复杂的范围。这两个方法接收两个参数:参照节点和偏移量

  3. 操作DOM范围中的内容

    deleteContents():从文档中删除范围所包含的内容,不会返回删除的片段

    extractContents():从文档中删除范围选区,并返回所删除的文档片段,可以将返回的片段插入到文档的其他地方

    cloneContents():创建范围对象的一个副本,然后可以在文档的其他地方插入该副本

  4. 插入DOM范围中的内容

    insertNode(refNode):向范围选区的开始处插入一个节点

    surroundContents(refNode):在范围选区插入内容,该方法接受一个参数,环绕范围内容的节点

    <!DOCTYPE html> 
    <html> 
      <body> 
      	<p id="p1"><b>Hello</b> world!</p> 
      </body> 
    </html> 
    
    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> 
    
    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> 
    
  5. 折叠DOM范围

    折叠范围,指范围中未选择文档的任何部分

    collapse():折叠范围。接受一个布尔值参数,表示要折叠到范围的一端,true 表示折叠到范围的起点,参数 false 表示折叠到范围的终点。要确定范围已经折叠完毕,可以检查 collapsed 属性

  6. 比较DOM范围

    compareBoundaryPoints():有多个范围时,确定这些范围是否有公共的边界(起点或终点)。接受两个参数:比较方式的常量值和要比较的范围,表示方式的常量值如下:

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

    该方法的返回值:-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 
    
  7. 复制DOM范围

    cloneRange():创建调用它的范围的一个副本了,新创建的范围与原来的范围包含相同的属性,并且修改副本的端点不会影响原来的范围。

    var nerRange = range.cloneRange();
    
  8. 清理DOM范围

    detach():从创建范围的文档中分离出该范围。在使用范围之后最好调用该方法。

    //使用范围最后再执行这两个步骤
    range.detach();//从文档中分离
    range = null;//解除引用