《JavaScript高级程序设计》Chapter 12 DOM 2 和 DOM 3

DOM 变化

  1. 如何确认浏览器是否支持 DOM 2 和 DOM 3 新增的模块:

    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')
    
  2. 针对 XML 命名空间的变化

    • 混合命名空间

      <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>
      
    • 在 DOM 2 中,Node 类型包含下列特定于命名空间的属性:

      • localName:不带命名空间前缀的节点名称
      • namespaceURI:命名空间 URI 或者(在未指定的情况下)null
      • prefix:命名空间前缀或者(在未指定的情况下)null
    • DOM 3 引入了下列与命名空间有关的方法:

      • isDefaultNamespace(namespaceURI):在指定的 namespaceURI是当前节点的默认命名空间时返回 true
      • lookupNamespaceURI(prefix):返回给定 prefix 的命名空间。
      • lookupPrefix(namespaceURI):返回给定 namespace 的前缀。
    • DOM 2 的 Document 也发生了变化,包含了下列与命名空间有关的方法:

      • createElementNS(namespaceURI, tagName):使用给定的 tagName 创建一个属于命名空间 namespace 的新元素。
      • createAttributeNS(namespaceURI, attributeName):使用给定的 attributeName 创建一个属于命名空间 namespaceURI 的新特性。
      • getElementsByTagNameNS(namespaceURI, tagName):返回属于命名空间 namespaceURItagName 元素的 NodeList
    • DOM 2 也为 Element 新增了一些方法:

      • getAttributeNS(namespaceURI, localName):取得属于命名空间 namespaceURI 且名为 localName 的特性。
      • getAttributeNodeNS(namespaceURI, localName):取得属于命名空间 namespaceURI 且名为 localName 的特性节点。
      • getElementsByTagNameNS(namespaceURI, tagName):返回属于命名空间 namespaceURItagName 元素的 NodeList
      • hasAttributeNS(namespaceURI, localName):确定当前元素是否有一个名为 localName 的特性,而且该特性的命名空间是 namespaceURI
      • removeAttributeNS(namespaceURI, localName):删除属于命名空间 namespaceURI 且名为 localName 的特性。
      • setAttributeNS(namespaceURI, qualifiedName, value):设置属于命名空间 namespaceURI 且名为 qualifiedName 的特性值为 value
      • setAttributeNodeNS(attNode):设置属于命名空间 namespaceURI 的特性节点。
    • NamedNodeMap 类型也新增了下列与命名空间有关的方法。由于特性是通过 NamedNodeMap 表示的,因此这些方法多数情况下只针对特性使用:

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

    • DocumentType 新增了三个属性:
      • publicId:文档类型声明中的信息段之一(- //W3C//DTD HTML 4.0.1//EN
      • systemId:文档类型声明中的信息段之一(http://www.w3.org/TR/html4/strict.dtd
      • internalSubset:访问包含在文档类型声明中的额外定义。
    • Document 类型的变化:
      • 新增 importNode() 用来从一个文档中获取一个节点,然后将其导入到另一个文档中,使其成为这个文档结构的一部分。

        • 每个节点都有一个 ownerDocument 属性,表示所属的文档。如果调用 appendChild() 时传入的节点属于不同的文档,会导致错误。但是在调用 importNode() 的时候回返回一个归当前文档所有的新的节点。
      • 新增 defaultView 属性,保存一个指针,指向拥有给定文档的窗口(或框架)。IE 不支持这个属性,但有一个等价的属性名叫 parentWindow。因此,要确定文档窗口,可以使用以下代码:

        var parentWindow = document.defaultView || document.parentWindow
        
      • DOM 2 Core 为 document.implementation 对象规定了两个新方法:

        • createDocumentType():创建一个新的 DocumentType 节点。接收三个参数:文档类型名称、publicIdsystemId。这个方法只在创建新文档时有用。

          var doctype = document.implementation.createDocumentType("html", "-//W3C//DTD HTML 4.01//EN", "http://www.w3.org/TR/html4/strict.dtd")
          
        • createDocument():创建新文档。接收三个参数:接受文档中元素的 namespaceURI、文档元素的标签名、新文档的文档类型。

          var doc = document.implementation.createDocument("http://www.w3.org/1999/xhtml", "html", doctype)
          
      • DOM 2 HTML 为 document.implementation 新增了一个方法:createHTMLDocument() 。这个方法的用途是创建一个完整的 HTML 文档,包括 html, head, title, body 元素。它只接受一个参数,即新创建的文档的标题(放在 title 里),返回新的 HTML 文档。只有 Opera 和 Safari 支持这个方法

    • Node 类型的变化
      • 添加了 isSupported() 方法:用于确定当前节点具有什么能力。接收两个参数:特性名、特性版本号。如果浏览器实现了相应特性,而且能够基于给定节点执行该特性,就返回 true

        document.body.isSupported("HTML", "2.0")
        
      • DOM 3 引入了两个辅助比较节点的方法:

        • isSameNode():接收一个节点参数,并在传入节点与引用的节点相同(即同一个对象)时返回 true
        • isEqualNode():接收一个节点参数,并在传入节点与引用的节点相等(即类型相同)时返回 true
      • DOM 3 针对为 DOM 节点添加额外数据引入了新方法:

        • setUserData():将数据指定给节点。接收三个参数:要设置的键、实际的数据(可以是任何数据类型)、处理函数。
        • getUserData():接收相同的三个参数,获取数据。
        • 传入 setUserData() 中的处理函数会在带有数据的节点被复制、删除、重命名或引入一个文档时调用。处理函数接收五个参数:表示操作类型的数值(1 - 复制,2 - 导入,3 - 删除,4 - 重命名),数据键、数据值、源节点和目标节点。删除时源节点是 null ;除复制外目标节点是 null
    • 框架的变化
      • HTMLFrameElement 和 HTMLIFrameElement 在 DOM 2 中都有了一个新属性:contentDocument。它包含一个指针,指向表示框架内容的文档对象。在此之前,无法直接通过元素取得这个文档对象(只能使用 frames 集合)。
        • contentDocument 属性是 Document 类型的实例,因此可以像使用其他 HTML 文档一样使用它,包括所有属性和方法。IE 8 之前不支持这个属性,但是支持一个名叫 contentWindow 的属性。

          var iframe = document.getElementById('myIframe')
          var iframeDoc = iframe.contentDocument || iframe.contentWindow.document
          

样式

  1. 确定浏览器是否支持 DOM 2 级定义的 CSS 能力:

    var supportsDOM2CSS = document.implementation.hasFeature('CSS', '2.0')
    var supportsDOM2CSS2 = document.implementation.hasFeature('CSS2', '2.0')
    
  2. 访问元素的样式:

    • 任何支持 style 特性的 HTML 元素在 JavaScript 中都有一个对应的 style 属性。这个 style 对象是 CSSStyleDeclaration 的实例,包含着通过 HTML 的 style 特性指定的所有样式信息,但不包含与外部样式表或嵌入样式表经层叠而来的样式。在 style 特性中指定的任何 CSS 属性都将表现为这个 style 对象的相应属性。对于使用短划线的 CSS 属性名,必须将其转换成驼峰大小写形式,才能通过 JavaScript 来访问。
      • 多数情况下,都可以通过简单地转换属性名的格式来实现转换。其中一个不能转换的 CSS 属性就是 float 。DOM 2 Style 规范规定样式对象上相应的属性名应该是 cssFloat,在 IE 上是 styleFloat
      • 在混杂模式下,没有给出度量单位的值,将默认为 px ,但是在标准模式下,这样的值会被忽略。
    • DOM 2 Style 规范为 style 对象定义了一些属性和方法。这些属性和方法在提供元素的 style 特性值的同时,也可以修改样式:
      • cssText:访问 style 特性中的 CSS 代码(可读可写)。
      • length:应用给元素的 CSS 属性的数量
      • parentRule:表示 CSS 信息的 CSSRule 对象。
      • getPropertyCSSValue(propertyName):返回包含给定属性值的 CSSValue 对象。
        • CSSValue 包含两个属性:cssTextcssValueTypecssValueType 是一个数值常量,表示值的类型:0 - 继承的值,1 - 基本的值,2 - 值列表, 3 - 自定义的值。
      • getPropertyPriority(propertyName):如果给定的属性使用了 !important 设置,则返回 important ,否则返回空字符串。
      • getPropertyValue(propertyName):返回给定属性的字符串值。
      • item(index):返回给定位置的 CSS 属性的名称。
      • removeProperty(propertyName):从样式中删除给定属性。
      • setProperty(propertyName, value, priority):将给定属性设置为相应的值,并加上优先权标志( important 或者一个空字符串)。
    • DOM 2 Style 增强了 document.defaultView ,提供了 getComputedStyle() 方法。这个方法接受两个参数:要取得计算样式的元素和一个伪元素字符串(例如: :after )。如果不需要伪元素信息可以传入 null 。这个方法返回一个 CSSStyleDeclaration 对象,其中包含当前元素的所有计算后的样式。
      • 由于浏览器解释综合( rollup )属性的方式不同,所以类似 border 这样的综合属性,不会在所有浏览器中都有返回值,但是可以通过分别访问四个边的属性(例如 .borderLeftWidth )得到值。
      • IE 不支持 getComputedStyle() 方法,但它有一种类似的概念。在 IE 中,每个具有 style 属性的元素还有一个 currentStyle 属性。这个属性是 CSSStyleDeclaration 的实例,包含当前元素全部计算后的样式。
    • 不能指望某个 CSS 属性的默认值在不同浏览器中是相同的,比如 visibility 属性在有些浏览器中默认值是 visible ,有些是 inherit。如果你需要元素具有某个特定的默认值,应该手工在样式表中指定该值。
  3. 操作样式表

    • CSSStyleSheet 类型表示的是样式表,包括通过 <link> 元素包含的样式表和在 <style> 元素中定义的样式表。它只表示样式表,不管这些样式表在 HTML 中是如何定义的。此外,这个对象是一套只读接口(有一个属性例外)。使用下列代码确认浏览器是否支持 DOM2 StyleSheet:

      var supportsDOM2StyleSheet = document.implementation.hasFeature('StyleSheets', '2.0')
      
      • CSSStyleSheet 继承自 StyleSheet ,所以我们可以使用后者作为基础接口来定义非 CSS 样式表。从 StyleSheet 接口继承而来的属性如下:

        • disabled:表示样式表是否被禁用。这个属性是可读 / 可写的,将这个属性设置为 true 可以禁用样式表。
        • href:如果样式表是通过 <link> 包含的,则是样式表的 URL ,否则,是 null
        • media:当前样式表支持的所有媒体类型(MIME Type)的集合。如果集合是空列表,表示样式表适用于所有媒体。在 IE 中,media 是一个反映 <link><style> 元素 media 特性值的字符串。
        • ownerNode:指向拥有当前样式表的节点的指针,样式表可能是在 HTML 中通过 <link><style /> 引入的(在 XML 中可能是通过处理指令引入的)。如果当前样式表是通过其他样式表通过 @import 导入的,则这个属性值为 null 。IE 不支持这个属性。
        • parentStyleSheet:在当前样式表是通过 @import 导入的情况下,这个属性是一个指向导入它的样式表的指针。
        • titleownerNodetitle 属性的值。
        • type:表示样式表类型的字符串。CSS 是 type/css

        以上属性,除了 disabled 之外,都是只读的。同时,CSSStyleSheet 还支持下列属性和方法:

        • cssRules:样式表中包含的样式规则的合集。IE 不支持这个属性,但是提供了一个类似的 rules 属性。
        • ownerRules:如果样式表是通过 @import 导入的,这个属性就是一个指针,指向表示导入的规则;否则,值为 null 。IE 不支持这个属性。
        • deleteRule(index):删除 cssRules 集合中指定位置的规则。IE 不支持,但提供了一个类似的 removeRule() 方法。
        • insertRule(rule, index):向 cssRules 集合中指定的位置插入 rule 字符串。IE 不支持,但提供了一个类似的 addRule() 方法。
        for (let i = 0; i < document.styleSheets.length; i++) {
            const sheet = document.styleSheets[i]
            console.log(sheet.href)
        }
        
      • 可以直接通过 <link><style> 元素取得 CSSStyleSheet 对象。DOM 规定了一个包含 CSSStyleSheet 对象的属性,叫 sheet 。除了 IE ,其他浏览器都支持这个属性。IE 支持的是 styleSheet 属性。要想在不同浏览器中都能取得样式表对象,可以使用如下代码:

        function getStyleSheet (element) {
            return element.sheet || element.styleSheet
        }
        const link = document.getElementsByTagName('link')[0]
        const sheet = getStyleSheet(link)
        
    • CSSRule 对象表示样式表中的每一条规则。实际上,CSSRule 是一个供其他多种类继承的基类型,其中最常见的是 CSSStyleRule 类型,表示样式表信息(其他规则还有 @import@font-face@page@charset ,但这些规则很少有必要通过脚本来访问。)CSSStyleRule 对象包含以下属性:

      • cssText:返回整条规则对应的文本。由于浏览器对样式表的内部处理方式不同,返回的文本可能会与样式表中实际的文本不一样。Safari 会全部转换为小写,IE 不支持这个属性。
        • cssText 包含选择符文本和围绕样式信息的花括号且只读,style.cssText 只包含样式信息且可被修改。
      • parentRule:如果当前规则是导入规则,这个属性引用的就是导入规则;否则,这个值为 null 。IE 不支持这个属性。
      • parentStyleSheet:当前规则所属的样式表。IE 不支持这个属性。
      • �selectorText:返回当前规则的选择符文本。由于浏览器对样式表的内部处理方式不同,返回的文本可能会与样式表中实际的文本不一样。
      • style:一个 CSSStyleDeclaration 对象,可以通过它设置和取得规则中特定的样式值。
      • type:表示规则类型的常量值。对于样式规则,这个值是 1。IE 不支持这个属性。
      const sheet = document.styleSheets[0]
      const rules = sheet.cssRules || sheet.rules
      const rule = rules[0]
      console.log(rule.cssText)
      console.log(rule.style.cssText)
      
    • DOM 规定,要向现有样式表中添加新规则,需要使用 insertRule() 方法。这个方法接受两个参数:规则文本和表示在哪里插入规则的索引:

      sheet.insertRule('body { background-color: #fff }', 0)
      

      上述语句的插入的规则将成为样式表中的第一条规则(插入到了位置 0)。
      IE 8 及更早的版本支持一个类似的方法,名叫 addRule() ,也接受两个必选参数:选择符文本和 CSS 样式信息;一个可选参数:插入规则的位置:

      sheet.addRule('body', 'background-color: #fff', 0)
      

      最多只能使用这个方法插入 4095 条样式规则。

    • 使用 deleteRule() 删除规则。这个方法接受一个参数:要删除的规则的位置。IE 支持的类似方法叫 removeRule() ,使用方法相同。

  4. 元素大小

    • DOM 中没有规定如何确定页面中元素的大小,所以下边的内容并不属于 DOM 2 Style 规范。IE 最早引入了一些属性,目前,所有主要浏览器都支持这些属性。

    • 偏移量( offset dimension ):包括元素在屏幕上占用的所有可见空间。元素的可见大小由其高度、宽度决定,包括所有内边距、滚动条和边框大小(不包括外边距)。通过下列四个属性可以取得元素的偏移量:

      • offsetHeight:元素在垂直方向上占用的空间大小,以像素计。
      • offsetWidth:元素在水平方向占用的空间大小。
      • offsetLeft:元素的左外边框至包含元素的左内边框之间的像素距离。
      • offsetTop:元素的上外边框至包含元素的上内边框之间的像素距离。
        • offsetLeftoffsetTop 与包含元素有关。包含元素的引用保存在 offsetParent 属性中。这个属性不一定与 parentNode 的值相等。例如:<td> 元素的 offsetParent 是作为其祖先元素的 <table> 元素,因为 <table> 是在 DOM 层次中距 <td> 最近的一个具有大小的元素。
        • 要想知道某个元素在页面上的偏移量,将这个元素的 offsetLeftoffsetTop 与其 offsetParent 的相同属性相加,如此循环直至根元素,就可以得到一个基本准确的值。

      所有这些偏移量属性都是只读的,并且每次访问时都要重新计算。为了减少内存开销,我们可以将这些偏移量保存在局部变量中。

    • 客户区大小( 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
              }
          }
      }
      

      客户区大小也是只读的,也需要重新计算。

    • 滚动大小( scroll dimension ):包含滚动内容的元素的大小。包含以下四个属性:

      • scrollHeight:在没有滚动条的情况下,元素内容的总高度。
      • �scrollWidth:在没有滚动条的情况下,元素内容的总宽度。
      • scrollLeft:被隐藏在内容区域左侧的像素数。通过设置该属性可以改变元素的滚动位置。
      • scrollTop:被隐藏在内容区域上方的像素数。通过设置该属性可以改变元素的滚动位置。
        由于一些兼容性差异,在确定文档的总高度时(包括基于视口的最小高度时),必须取得 scrollWidth / clientWidthscrollHeight / clientHeight 中的最大值,才能保证在跨浏览器的环境下得到精确的结果。对于运行在混杂模式下的 IE ,要使用 document.body 代替 document.documentElement
    • 所有的浏览器都提供了 getBoundingClientRect() 方法用来确定元素大小。这个方法会返回一个矩形对象,包含四个属性:lefttoprightbottom。这些属性给出了元素在页面中相对于视口的位置。在 IE 及更早版本的浏览器中,文档左上角的起点坐标是 (2, 2) 而其他浏览器是 (0, 0) 。所以需要检查一下位于 (0, 0) 处元素的位置:

      function getBoundingClientRect (element) {
          if (typeof arguments.callee.offset !== 'number') {
              var scrollTop = document.documentElement.scrollTop
              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
      

      Poly Fill: Custom getBoundingClientRect() method [详见书 P326]

遍历

  1. DOM 2 Traversal 定义了两个用于辅助完成顺序遍历 DOM 结构的类型:NodeIterator 和 TreeWalker 。这两个属性能够基于给定的起点对 DOM 结构进行深度优先遍历操作。IE 不支持 DOM 遍历。使用下列代码检测:

    var supportsTraversals = document.implementation.hasFeature('Traversal', '2.0')
    var supportsNodeIterator = (typeof document.createNodeIterator === 'function')
    var supportsTreeWalker = (typeof document.createTreeWalker === 'function')
    
  2. NodeIterator 类型是两者中比较简单的一个,可以使用 document.createNodeIterator() 方法创建它的新实例。它接收以下四个参数:

    • root:想要作为搜索起点的树中的节点。
    • 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_ENTITY:显示实体节点。对 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 页面没有作用。

        除了 SHOW_ALL 之外,可以用 | 运算符组合多个选项。

    • filter:一个 NodeFilter 对象,或者一个表示应该接受还是拒绝某种特定节点的函数。
      • 每个 FilterNode 对象只有一个方法,即 acceptNode() 。如果应该访问给定的节点,该方法返回 NodeFilter.FILTER_ACCEPT ,如果不应该则返回 NodeFilter.FILTER_SKIP 。这是一个抽象类型,不能直接创建它的实例。在必要时,只要创建一个包含 acceptNode() 方法的对象,然后将这个对象传入 createNodeIterator() 中即可。
    • entityReferenceExpansion:布尔值,表示是否要扩展实体引用。这个参数在 HTML 页面中没有用,因为其中的实体引用不能扩展。

    NodeIterator 类型的两个主要方法:

    • nextNode():向后前进一步,当遍历到最后一个节点时,返回 null
    • previousNode():向前倒退一步,当遍历到根节点时,返回 null

    例子:返回遍历中遇到的 <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) {
        console.log(node.tagName)
        node = iterator.nextNode()
    }
    
  3. TreeWalker 是 NodeIterator 的一个更高级的版本。除了包括 nextNode()previousNode() 在内的相同功能之外,这个类型还提供了下列用于在不同方向上遍历 DOM 结构的方法。

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

    创建 TreeWalker 对象要使用 document.createTreeWalker() 方法,这个方法接受四个参数,与 document.createNodeIterator 相同。

    • filter 的返回值,除了 FILTER_ACCEPTFILTER_SKIP 之外,还可以使用 FILTER_REJECT ,在 NodeIterator 中,NodeFilter.FILTER_SKIPNodeFilter.FILTER_REJECT 作用相同:跳过指定节点。但在 TreeWalker 中,FILTER_SKIP 会跳过相应节点继续前进到子树中的下一个节点,而 FILTER_REJECT 会跳过相应节点及该节点的整个子树。

范围

  1. DOM 2 Traversal & Range 模块定义了范围( range )接口。通过范围可以选择文档中的一个区域,而不必考虑节点的界限(选择在后台完成,对用户是不可见的。)IE 实现范围特性的方式不同。

  2. DOM 2 在 Document 类型中定义了 createRange() 方法。在兼容 DOM 的浏览器中,这个方法属于 document 对象。检测方法:

    var supportsRange = document.implementation.hasFeature('Range', '2.0')
    var alsoSupportsRange = (typeof document.createRange === 'function')
    
  3. 可以使用 document.createRange() 创建一个 Range 类型的实例。该实例包含以下属性,他们提供了当前范围在文档中的位置信息:

    • startContainer:包含范围起点的节点(即选中区第一个节点的父节点)
    • startOffset:范围在 startContainer 中起点的偏移量。如果 startContainer 是文本节点、注释节点或 CDATA 节点,那么 startOffset 就是范围起点之前跳过的字符数量,否则,就是范围中第一个子节点的索引。
    • endContainer:包含范围终点的节点(即选区中最后一个节点的父节点)。
    • endOffset:范围在 endContainer 中终点的偏移量(与 startOffset 遵循相同的取值规则)。
    • commonAncestorContainerstartContainerendContainer 共同的祖先节点在文档树中位置最深的那个。
  4. 用 DOM 范围实现简单选择:selectNode()selectNodeContents() 。它们都接收一个参数,即一个 DOM 节点。然后使用该节点中的信息来填充范围。其中,selectNode() 方法选择整个节点,包括子节点;selectNodeContents() 只选择节点的子节点。

    var range = document.createRange()
    range.selectNode(document.getElementById('test')
    

    为了更精细的控制将哪些节点包含在范围中,还可以使用下列方法:

    • setStartBefore(refNode):将范围起点设置在 refNode 之前,即 refNode 是范围选区中的第一个子节点。
    • setStartAfter(refNode):将范围起点设置在 refNode 之后,即 refNode 的下一个同辈节点是范围选区中的第一个子节点。
    • �setEndBefore(refNode):将范围终点设置在 refNode 之前,即 refNode 的上一个同辈节点是范围选区中的最后一个子节点。
    • �setEndAfter(refNode):将范围终点设置在 refNode 之后,即 refNode 是范围选区中的最后一个子节点。
  5. 用 DOM 范围实现复杂选择:setStart()setEnd()。这两个方法都接受两个参数:参照节点和偏移量,分别对应 startContainer / endContainerstartOffset / endOffset

  6. 操作 DOM 范围中的内容:创建范围时,内部会为这个范围创建一个文档片段,范围所属的全部节点都被添加到了这个文档片段中。范围会自动完善 DOM 结构,变成一个有效的 DOM 。于是,就可以使用下列方法进行操作了:

    • deleteContents:从文档中删除范围所包含的内容。
    • extractContents:从文档中移除范围选区(返回范围的文档片段)。
    • cloneContents:返回范围中节点的副本作为文档片段。
  7. 插入 DOM 范围中的内容:insertNode()

  8. 折叠 DOM 范围:collapse()

  9. 比较 DOM 范围:compareBoundaryPoints() 比较是否有公共边界(起点或终点)

  10. 复制 DOM 范围:range.cloneRange()

  11. 清理 DOM 范围:

    range.detach()
    range = null
    
  12. IE 8 及更早版本的范围
    [详见书 P340]

推荐阅读更多精彩内容