《JavaScript高级程序设计》Chapter 10 DOM

  • 之前提到过 DHTML->DOM
  • 本章介绍与浏览器相关的DOM节点以及JS对DOM的实现
  • 注意到,IE中的DOM都是通过COM实现的,与一般的DOM的运行机制有差异(考虑能力检测、浏览器兼容)
  • 节点层次
    • 映射 
    • Node类型(一些通用的节点方法或者属性) go
    • Document类型(注意<head>中一些网页信息的处理) go
    • Element类型(访问和处理元素属性的几种方法) go
    • Text类型(文本) go
    • Comment类型(注释) go
    • CDATASection类型(针对XML) go
    • DocumentType类型(doctype) go
    • DocumentFragment类型(子节点树的“仓库”) go
    • Attr类型(属性节点) go
  • DOM操作技术:动态脚本、动态样式、表格<table>的操作、NodeList相关注意事项。go
 

节点层次

  • DOM将HTML或者XML文件映射成节点树。从前了解的节点类型大致上分为3种(元素节点、属性节点和文档节点),然而本书中进一步细分为12种类型,继承自一个基类型。
  • 每个节点实际上是一个节点对象,可以通过获取得到,并调用相关属性和方法进行处理。
  • DOM实际上是一套API,提供了各种接口(节点类型接口)。
  • Node类型(一些通用的方法和属性)
    • 存储了一些节点的通用方法,其他类型的节点都继承了这个节点。但是需要注意IE8及之前的版本并不具备这个对象类型。
    • nodeType属性。存储了一些常量,表示节点类型。
    • nodeName和nodeValue属性:顾名思义。并不是所有节点的这两个属性有值,有时是null
    • 节点关系:构成了节点树,访问其他节点的方法或者属性:
      • childNodes属性:动态的NodeList,是类数组对象,具有length。可以通过Array.prototype.slice()转换为数组。注意IE8及之前的处理需要用遍历的方法处理。
      • parentNode属性
      • previousSibling属性、nextSibling属性
      • firstChild和lastChild属性
      • hasChildNodes()方法
      • ownerDocument :指向DOM节点
      • 有些节点并不存在子节点,这里会存在差异。
    • 操作节点:上述关系节点是只读的,可以通过操作节点进行一些操作。
      • appendChild():注意到如果传入的节点已经是文档的一部分了,那么这个节点会转移到新的位置(指针的改变)。
      • DOM中的节点不会同时出现在两个或者多个DOM中
      • insertBefore(newNode, null/oldNode)
      • replaceChild(newNode, waitToReplacedNode)
      • 被代替的节点仍然存在在文档中,只不过在文档中没了它的位置:各种指针关系已经不存在(或者被替代了)。无法通过其他节点访问到它,所以说它没有自己的位置
      • removeChild():只是移除
      • 这些操作是在“关系”的基础上:主要因为DOM通过“关系”(指针)来访问节点
    • 其他方法:
      • cloneNode():只复制特性,不复制其他操作(如事件处理)。IE这里存在bug,建议复制之前,移除事件处理程序。
      • cloneNode(true):克隆节点以及其下完整的其他节点
      • cloneNode(false):只复制该节点,需要借助appendChild()等将其加入到文档中。
      • normalize():处理异常的文档节点(删除空白文档节点、合并相邻的文档节点)
  • Document类型
    • document与html的关系:document是HTMLDocument(继承自Document类型)的一个实例。也是window对象的属性。
    • Document节点特征:
      • nodeType为9
      • nodeName为"#document"
      • nodeValue为null
      • parentNode为null
      • ownerDocument为null
      • 其子节点可能是DocumentType(最多一个)、Element(最多一个)、ProcessingInstruction或者Comment
    • 子节点
      • 可能是DocumentType(最多一个)、Element(最多一个)、ProcessingInstruction或者Comment
      • document.documentElement:指向html
      • document.body:指向body
      • DocumentType:document.doctype处理<!DOCTYPE>。各浏览器处理方式不同
      • 注释:各浏览器处理方式也不同
      • 由于这些差异性存在,所以一般没什么用处。也不需要调用appendChild()等方法进行处理
    • 文档信息(作为HTMLDocument实例)
      • 网页的信息(放在<head>中)
      • document.title:动态改变标题栏标题(文档中的title属性不变)
      • document.url/document.domain/document.referrer:
        • 不建议改变,没多大用处:但是将多个框架的document.domain改一致,有助于通信
        • domain不可以改的完全不同
        • 不可将松散的(wrox.com)改作紧绷的(p2p.wrox.com)
    • 查找元素
      • getElementById():区分大小写,返回第一个找到的id(有时会返回同名的name,所以建议name和id属性不要一样)
      • getElementsByTagName():返回类似NodeList的HTMLCollection对象。
        • getElementsByTagName(*):顺序返回所有元素
        • getElementsByTagName("*"):返回注释元素
      • HTMLCollection对象:
        • 方括号传入数字==调用item()方法
        • 方括号传入字符==调用nameItem()方法通过name属性获取
      • getElementsByName():HTMLDocument类型的方法
      • TIPS:
        • 1、对于表单元素,id不同以使<label>元素应用到单个标签
        • 2、name相同以确保三个值只有一个给浏览器
    • 特殊集合(HTMLDocument对象):document.anchors/document.applets(已不用)/document.forms/document.images/document.links
    • DOM一致性检测:hasFeature()+必要的能力检测
    • 文档写入:write()/writeln()/open()/close()
      • 注意写入"<script>...</script>"的时候分开来,并写作"<script>..."+"<\/script>"
      • 在文档调用完毕之后执行write()/writeln()方法会重写整个页面
  • Element类型(元素节点),主要用于表现XML或者HTML文档
    • 特征:nodeType为1,nodeName为标签名,nodeValue为null,parentNode/子节点灵活。
    • nodeName属性、tagName属性访问元素的标签名,后者一律返回大写,注意这点,尤其在HTML文档中,可全部处理成小写。
    • HTML元素(由HTMLElement或者其子类型表示)
      • id/title/lang/dir/className属性。注意到没有用class是因为“class”是JS的保留字
      • 格式 div.id
    • 取得特性:getAttribute()/setAttribute()/removeAttribute()
      • getAttribute()可以取得自定义特性(不区分大小写,且HTML5中自定义特性需要加上data-前缀),而这些自定义属性是不会调价到DOM元素的属性中的(IE除外)。
    • 设置特性:setAttribute()、removeAttribute()
      • 会处理自定义属性,特姓名统一转换为小写
    • attributes属性(处理属性节点)
      • Element类型是使用这个属性的唯一一个DOM节点类型
      • 包含类似NodeList的NamedNodeMap集合。
      • element.attributes.getNamedItem("id").nodeValue
        element.attributes["id"].nodeValue
        element.attributes.removeNamedItem("id")//返回删除的属性节点
        element.attributes.setNamedItem("newAttr");
      • 不常使用。可以用在遍历元素特性上。
      • 返回的特性的顺序不一定。
    • 创建元素
      • document.creatElement("div")
      • IE中 :document.creatElement("html代码")//避开IE的一些漏洞
    • 元素子节点:对于某个标签下有多少个子节点的问题
      • IE只考虑元素类型
      • 其他浏览器还会考虑换行符(文本类型)
      • 所以注意考虑nodeType
      • 当然可以使用getElementById()或者getElementsByTagName()
    • 访问or设置元素的属性方法
      • HTMLElement或者子类型方法
      • getAttribute()/setAttribute()/removeAttribute()
      • DOM对象的属性来访问:不会设置自定义属性
      • attributes属性
      • 注意:style和onclick等在getAttribute()和DOM属性调用中返回的不一样:style通过getAttribute()返回CSS文本,属性返回对象;onclick等通过属性返回JS函数,getAttribute返回代码的字符串。
      • 若以编程方式操作的时候,避免使用getAttribute();getAttribute()适合处理自定义属性。
  • Text类型
    • 通过nodeValue属性、data属性以及createTextNode()等方式都可以访问或者修改Text节点(但是注意,修改时传入的字符串会经过HTML或XML的编码)
    • createTextNode()创建文本节点
    • 在element上调用element.normalize()规范下面的文本节点
    • splitText(index)分割文本节点,分割到index前一个位置为止。
    • 注意到:可以通过lastChild访问元素的文本节点(文本节点通常是元素的最后一个节点?)
  • Comment类型(注释)
    • 不支持子节点,但可以通过父节点访问(comment.data)。
    • 与Text类型继承自相同基类,拥有除了splitText()外的方法
    • document.createComment()
    • 少用。需要确保在<html></html>间,不然会有不同的处理方式(参照Document节点类型那里的讨论)
  • CDATASection类型(针对基于XML的文档,表示CDATA区域)
    • 同样继承自Text类型,拥有除了splitText()外的方法
    • document.createCDataSection()
  • DocumentType类型
    • 仅FireFox、Safari、Opera和Chrome4.0支持
    • DOM1中不能动态创建,只能通过document.doctype访问
    • 属性:name、entities、notations;针对HTML和XHTML,后两者都是空列表。所以name属性比较有用,保存文档类型的名称。
    • 之前也提到过,不同浏览器的处理方式不同。
  • DocumentFragment类型
    • 轻量级。只有它在文档中没有对应的标记。包含和控制节点,但不占用额外资源。
    • 可以做“仓库”使用,保存将来可能会添加到文档中的节点(待通过appendChild()等方法添加至树中,或者从树中移除的)
    • 创建:document.createDocumentFragment();
    • 文档片段,继承了Node的所有方法。
    • 例如,为ul添加3个li:
      var fragment = document.createDocumentFragment();
      var ul = document.getElementById("ul");
      var li = null;
      for (var i=0;i<3;i++){
          li = document.createElement("li");
          li.appendChild(document.createTextNode("Item"+(i+1));
          fragment.appendChild(li);
      }
      ul.appendChild(fragment); //fragment中的所有li被删除并转移到ul中
  • Attr类型(属性类型)
    • 一般不认为是节点树中的成员,常使用其它方法访问,而不是使用属性节点进行操作。
    • 属性:name/value/specified
    • document.createAttribute()
    • element.setAttributeNode(attr);//其中attr是属性节点
      element.getAttributeNode(attr)

DOM操作技术

    • 动态脚本
      • 通过DOM动态添加JS脚本<script>
      • 添加外部脚本:何时加载完毕无法确认,但可以通过“事件”进行控制。
      • 添加内嵌脚本:普遍支持向script标记添加文本节点,但IE不允许访问script的子节点,则可通过对script.text进行处理来添加这个内嵌脚本。
    • 动态样式
      • 通过DOM动态应用CSS样式:
      1. link添加样式表;
      2. style添加嵌入样式;
      3. 修改元素的属性:
        • 修改元素的样式属性;
        • 修改元素的class属性以匹配已存在的样式
      • 外部样式表是异步加载的,是否知道样式表已经加载完成并不重要。但同样可以通过“事件”对这个过程进行检测。
      • 对于<style>标记,IE同样不可以访问其子节点,这个时候可以通过style.stylesheet.cssText处理。注意处理这个属性有可能导致浏览器崩溃。
    • 操作表格<table>元素
      • <table>,<tbody>/<thead>,<tr>,<th>/<td>对于多行多列的表格需要进行大量的重复性工作,HTML DOM为此给<table>,<tbody>,<tr>增加了一些属性和方法。
      • 还可以利用document.write() or innerHTML写入创建的代码,其中行列可以通过双重for循环处理,注意需要采取尽量少的调用innerHTML的循环算法,考虑内存的使用情况。
    • 使用NodeList(如getElementsByTagName)的注意事项
      • 以及NamedNodeMap和HTMLCollection都是类数组对象,且都是动态的:文档结构变化的时候,他们随之改变(实时运行的查询)
      • 每次重新访问NodeList等的时候,都会重新对NodeList进行查询,更新其length属性,所以效率略低,应该减少对NodeList等的访问,并且可以将其中取得的值缓存起来使用(而不是每次都去查询一遍)
      • 理解DOM的关键,就是理解DOM对性能的影响。DOM操作往往是JS程序最大的开销部分
posted @ 2017-10-26 09:46  nebulium  阅读(186)  评论(0编辑  收藏  举报