念奴娇 赤壁怀古
     [北宋]苏轼
大江东去,浪淘尽,千古风流人物。
故垒西边,人道是,三国周郎赤壁。
乱石穿空,惊涛拍岸,卷起千堆雪。
江山如画,一时多少豪杰。

遥想公瑾当年,小乔初嫁了,雄姿英发。
羽扇纶巾,谈笑间,樯橹灰飞烟灭。
故国神游,多情应笑我,早生华发。
人生如梦,一樽还酹江月。

JavaScript高级程序设计(第3版)学习笔记15——DOM基础

  从这篇笔记开始整理JavaScript的第三部分:文档对象模型DOMDocument Object Model)。DOM是针对HTMLXML文档的一个API,脱胎于DHTML,由W3C负责制定相关标准,现在已经成为表现和操作页面标记的真正的跨平台、语言中立的一种标准,除了JavaScript外,其它一些语言比如SVGMathML等也不同程度的实现了各自的DOM

1、DOM组成和级别

  DOM分为三个组成部分和三个级别:

组成部分 说明
核心DOM 用于任何结构化文档的标准模型
XML DOM 用于XML文档的标准模型,定义了所有XML元素的对象和属性,以及访问它们的方法(接口),换句话说,XML DOM是用于获取、更改、添加或删除XML元素的标准
HMTL DOM 用于HTML文档的标准模型,定义了所有HTML元素的对象和属性,以及访问它们的方法(接口)
DOM级别 浏览器支持情况 功能模块 说明
0级 IE4、Netscape4 W3C标准中,是没有0级的,通常所谓DOM 0级指的就是在DOM 1级规范之前的在IE4Netscape Navigator4中支持的DHTML
1级 几乎所有现代浏览器 DOM核心(DOM Core) 规定的是如何映射基于XML的文档结构,以便简化对文档中任意部分的访问和操作
DOM HTML DOM核心基础上加以扩展,添加了针对HTML的对象和方法
2级 IE 9+
Opera 7-9.9
(部分支持),OPera 10+
Safari 2+
(部分支持)
Chrome 1+
(部分支持)
Firefox 1+
(几乎全部)
DOM视图(DOM Views) 定义了跟踪不同文档(例如,应用CSS前后的文档)视图的接口
DOM事件(DOM Events) 定义了事件和事件处理的接口
DOM样式(DOM Style) 定义了基于CSS为元素应用样式的接口
DOM遍历和范围(DOM Traversal and Range) 定义了遍历和操作文档树的接口
DOM核心和HTML扩展 开始支持XML命名空间等
3级 IE9+
Opera9+
(部分支持)
Firefox1+
(部分支持)
DOM加载和保存(DOM Load and Save 引入了以统一方式加载和保存文档的方法
DOM验证(DOM Validation 验证文档的方法
DOM核心和HTML扩展 开始支持XML1.0规范,涉及XML InfosetXPathXMLBase

2、文档映射

DOMHTMLXML文档映射成一个由不同节点组成的树型机构,每种节点都对应于文档中的信息或标记,节点有自己的属性和方法,并和其他节点存在某种关系,节点之间的关系构成了节点层次,例如:

<html>
    <head>
        <title>标题</title>
    </head>
    <body>
        <p>测试</p>
    </body>
</html>

1)文档节点Document是每个文档的根节点
2)文档元素:如右图,文档节点只有一个子节点,即<html>元素,称之为文档元素。文档元素是文档的最外层元素,其他所有元素都包含在文档元素中,每个文档只能有一个文档元素。在HTML中,文档元素永远是<html>元素,但是在XML中,没有预定义元素,因此任何元素都可能成为文档元素。
3)文档中每一段标记都通过树中的一个节点来表示,比如HTML元素通过元素节点表示,特性(attribute)通过特性节点表示,文档类型通过文档类型节点表示,而注释则通过注释节点表示。在DOM1中,总共有12种节点类型,他们都继承自一个基类型。

 

3、Node接口

类别 属性/方法 /类型/返回类型 说明
属性 nodeName String 节点名字,根据节点类型定义。对于元素节点,就是原始的标签名
nodeValue String 节点值,根据节点类型定义。对于元素节点为null
nodeType Number 节点类型,返回12种节点类型值之一
ownerDocument Document 指向这个节点所属的文档,可以利用它直接访问文档节点,而不用层层回溯
parentNode Node 文档树中的父节点
childNodes NodeList 所有直接子节点组成的列表,不同浏览器对空白字符和<html>外的注释有不同处理,会导致childNodes不一致
firstChild Node 第一个直接子节点,没有子节点返回null
lastChild Node 最后一个直接子节点,没有子节点返回null
previousSibiling Node 前一个兄弟节点,没有则返回null
nextSibiling Node 后一个兄弟节点,没有则返回null
方法 hasChildNodes() Boolean 有子节点时返回true,否则返回false
appendChild(node) Node 在末尾添加子节点,返回新添加的节点。如果传入参数已经是文档中一部分,结果将是将该节点从原来的位置移向新位置(任何DOM节点不能同时出现在多个位置上)
removeChild(node) Node 移除节点并返回这个节点。删除后,节点仍然属于原来的文档,只是没有了位置
replaceChild(node,node) Node 传入新添加的节点和被替换的节点,返回被替换的节点。被替换的节点仍然属于原来的文档,只是没有了位置
insertBefore(node,node) Node 传入新添加的节点和参照节点,新添加节点会成为参照节点的前一个兄弟节点,如果参照节点为null,则插入到最后,相当于appendChild()。返回新添加的节点
cloneNode(boolean) Node 复制节点,参数为true时,复制节点及其所有子节点树,为false时,只复制节点本身。返回的节点属于文档所有,但没有指定父节点
normalize()   处理文档树中的文本节点,删除空文档节点或合并两个相邻的文本节点等

说明:

(1)关于节点类型nodeType,在DOM1中定义了12种常量,是作为Node类型构造函数的属性定义的(静态属性),它们对应于各自的节点类型:

节点 节点类型(nodeTypeNode的静态属性) 节点名称(nodeName 节点值(nodeValue 父节点(parentNode 子节点(childNodes 说明
Document Node.DOCUMENT_NODE(9) #document null null DocumentType(最多一个)|Element(最多一个)
|Comment|ProcessingInstruction
下有进一步叙述
Element Node.ELEMENT_NODE(1) 元素的标签名 null Document|Element Element|Text|Comment|
ProcessingInstruction|
CDATASection|EntityReference
Text Node.TEXT_NODE(3) #text 节点所包含的文本 Element (不支持)没有子节点
Comment Node.COMMENT_NODE(8) #comment 注释的内容 Document|Element (不支持)没有子节点 和Text继承自相同基类,拥有除splitText()外所有字符串方法,可通过nodeValue或data属性获取注释内容
CDATASection Node.CDATA_SECTION_NODE(4) #cdata-section CDATA区域的内容 Document|Element (不支持)没有子节点 继承自Text类型,拥有除splitText()外所有字符串方法
DocumentType Node.DOCUMENT_TYPE_NODE(10) doctype的名称 null Document (不支持)没有子节点 在DOM1中不能动态创建,DocumentType对象的3个属性:
name表示文档类型名称
entities表示描述文档类型实体的NamedNodeMap
notations表示文档类型描述的符号的NamedNodeMap
DocumentFragment Node.DOCUMENT_FRAGMENT_NODE(11) #document-fragment null null 同Element类型 下有进一步叙述
Attr Node.ATTRIBUTE_NODE(2) 特性名称 特性值 null HTML中不支持,XML中可以是TextEntityReference 有3个自己的属性:
name等于nodeName
value等于nodeValue
specified表示是否为默认设置
EntityReference Node.ENTITY_REFERENCE_NODE(5) 引用的实体名称 null     实体引用节点
Entity Node.ENTITY_NODE(6) entity name null     实体节点
ProcessingInstruction  Node.PROCESSING_INSTRUCTION_NODE(7) ProcessingInstruction.target 相同 ProcessingInstruction.data 相同     处理指令
Notation  Node.NOTATION_NODE(12) notation name null     DTD中定义的符号

这些节点类型都实现了Node接口,因此都可以访问Node类型的属性和方法(不支持子节点的节点类型上调用appendChild()、insertBefore()、replaceChild()、removeChild()等方法时会抛出异常)。经过测试在IE9中已经可以直接访问Node类型中定义的常量值,并且IE9中这些值不能改变,而在Firefox15的版本中仍然可以修改,这应该是Firefox实现的一个Bug(原书说IE中不可访问Node的论述似有不妥)。

Node.DOCUMENT_NODE = 2;
console.info(Node.DOCUMENT_NODE);//IE9输出9,而Firefox15输出的是2

2)关于NodeList类型,它也是一个类数组类型,有length属性,也可以通过方括号语法访问,还可以通过item()方法访问,但并不是Array的实例,如果要对其使用数组方法,必须像转换arguments对象一样来转换NodeList对象:

var node = document;//任意一个节点,这里使用文档根节点测试
console.info(node.childNodes.length);//2,直接子节点
console.info(node.childNodes[0].nodeName);//通过方括号语法访问第1个元素,索引从0开始
console.info(node.childNodes.item(1).nodeName);//通过item()方法访问第2个元素,索引从0开始
var arr = Array.prototype.slice.call(node.childNodes,0);//转换为真正的数组
console.info(arr.join(','));//可以使用数组方法了

需要特别注意的是,NodeList对象类型是一个有生命、有呼吸的对象,它的属性和元素是跟随文档变化而变化的:

var node = document;
var nodeList = node.childNodes;
var src = nodeList.length;
node.removeChild(nodeList[0]);//修改文档,会同时修改NodeList对象的属性和元素,即便是已经将其保存为另外一个变量
console.info(nodeList.length == src - 1);//true

类似NodeList这种动态变化的对象对象还有HTMLCollectionNamedNodeMap等,其中HTMLCollection还有一个namedItem()方法,可以通过元素的name获取集合中的项。

4、Document类型

  在JavaScript中,通过Document类型表示文档,而我们通常使用的document对象则是HTMLDocument(继承自Document类型)的一个实例,表示整个HTML页面。同时,docuemnt对象也是window对象的一个属性,可以作为全局对象来访问。document对象的主要属性和方法有:

类别 属性/方法 说明 备注
属性 documentElement 指向HTML页面中的<html>元素 作为document的子节点,<html>元素还可以通过document.firstChilddocument.childNodes[0]等方式访问
body 直接指向HTML页面中的<body>元素 document.body的使用频率非常高
doctype 表示<!DOCTYPE>相关信息,不同浏览器差异比较大,因此用处不大 有些浏览器会把<!DOCTYPE>作为注释处理
title 包含<title>元素中的文本,显示在浏览器窗口的标题和标签页上,可以通过它修改标题 修改title属性的值不会改变<title>元素
URL 页面完整的URL,即地址栏中显示的URL 这些信息都存在于请求的HTTP头部,这些属性是为了方便在JavaScript中访问,domain的设置需要注意:
1
、不能将domain设置为URL不包含的域,如URL='p2p.wrox.com'domain可以设置为wrox.com,但不能设置为ncz.net
2
、不能将domain由松散的设置为紧绷的,如原来domain='wrox.com',不能设置为domain='p2p.wrox.com' 
domain 页面的域名,可以设置
referrer 链接到当前页面的那个页面的URL,没有来源页面时,为空字符串
implementation 提供浏览器对DOM实现情况的描述对象,在DOM1只有一个hasFeature()方法 hasFeature()接受两个参数:DOM功能和版本号。由于浏览器实现问题,这个方法并非百分百准确
文档元素集合 anchors 包含文档中所有带name特性的<a>元素 这些集合都是HTMLCollection对象,集合中的项会随文档的变化而更新
applets 包含文档中所有<applet>元素,已经不推荐使用
forms 包含文档中所有<form>元素,相当于document.getElementsByTagName('form')
images 包含文档中所有<img>元素,相当于document.getElementsByTagName('img')
links 包含文档中所有带href特性的<a>元素
元素获取方法 getElementById() 接受一个参数:要获取元素的ID,匹配大小写,找不到时返回null,若有多个相同ID元素,返回第一个 IE8-中不区分大小写,并且表单元素(如<input>)中的name也被作为ID去查询
getElementsByTagName() 接受一个参数:要获取元素的标签名,返回包含0或多个元素的NodeList,在HTML中,返回HTMLCollection 传入*号时,则返回文档中所有元素,可以通过索引(调用item())、字符串(调用namedItem())、以及直接使用item()方法访问结果集
getElementsByName() HTMLDocument特有方法,返回name特性为传入参数值的元素集合,返回HTMLCollection对象 使用namedItem()时,只返回第一项,因为是按name获取的集合,集合中的name全部相同
文档写入方法 write() 输出文本,接受一个参数:写入到输出流的文本 write()writeln()被用来向页面动态地输入内容,需要注意的是:
1
、如果输出内容中含'</script>',为了防止做为标签解析而发生错误,需要特殊处理,可以写成'<\/script>'或拆分
2
、如果是页面加载完成之后调用,会重写整个页面
writeln() 输出文本,并添加换行符(\n),接受一个参数:写入到输出流的文本
open() 打开网页输出流
close() 关闭网页输出流
创建方法 createElement() 创建新元素,并设置ownerDocument属性,接受一个参数:要创建元素的标签名(在HTML中不分大小写,在XML中区分) IE中还可以传入包含属性的完整的元素标签来创建新元素
createTextNode() 创建文本节点,接受一个参数:要插入节点中的文本(会按HTML/XML格式编码),会同时设置ownerDocument 除非把新节点添加到文档中,否则不会显示新节点
createComment() 创建新注释,接受注释文本 浏览器不会识别<html>后代注释,如要访问注释节点,需要保证是<html>元素的后代 
createCDATASection() 在XML文件中创建CDATA区域,传入节点内容 CDATA区域只会出现XML文档中,因此多数浏览器会报CDATA区域错误地解析为Comment或Element
createDocumentFragment() 创建文档片段  
createAttribute() 创建新特性节点  

说明:

(1)一般情况下,不用在document对象上调用appendChild()insertBefore()removeChild()replaceChild()等方法,因为文档类型(如果存在的话)是只读的,而且它只能有一个元素子节点。

(2)document对象的创建方法,是实现动态加载脚本或样式的基础,从而可以进一步对代码模块化,实现按需加载,提升性能,这在Ext4库中已经很好地应用了。动态加载的一般方法:

function loadScript(url){//动态加载外部js文件,也可以类似的加载js代码
    var script = document.createElement("script");
    script.type = "text/javascript";
    script.src = url;
    document.body.appendChild(script);
}

function loadStyle(url){//动态加载外部css文件,也可以类似的通过style元素加载css代码
    var link = document.createElement("link");
    link.rel = "stylesheet";
    link.type = "text/css";
    link.href = url;
    var head = document.getElementsByTagName("head")[0];
    head.appendChild(link);
}

5、Element类型

  Element类型用于表现XMLHTML元素,提供了对元素标签名、子节点以及特性的访问。在HTML中,元素由HTMLElement类型(或其子类型)表示,HTMLElement继承了Element类型,并添加了对应每个HTML元素都存在的标准特性的属性。HTMLElement类型常用属性和方法总结如下:

类别 属性/特性 说明
Element类型属性 tagName 这个属性是所有Element都有的,值和nodeName相同,表示元素标签名,在HTML中,返回的标签名始终大写,在XML中,和源代码一致
HTML元素标准特性 id id 元素在文档中的唯一标识符
title title 有关元素的附件说明信息,一般通过工具条显示出来
lang lang 元素内容的语言代码,很少使用
dir dir 语言的方向,ltr:从左至右,rtl:从右至左,也很少使用
className class 元素的CSS类,因为class是保留字,所以属性命名为className
特性属性 attributes 这个是属性只有Element类型使用,是一个NamedNodeMap值,属于一个动态集合
特性方法 getAttribute() 参数:特性名。参数必须与实际的特性名相同,因此是class而不是className,给定特性不存在时,返回null,这个方法也可以获取自定义特性,特性名不区分大小写
setAttribute() 参数:特性名和特性值。如果已存在该特性,替换现有的值,如果不存在,就创建并赋值。通过这个方法设置特性时,会把特性名转换为小写形式
removeAttribute() 参数:特性名。彻底删除元素的特性,不仅会清除特性的值,也会从元素对象中删除特性
特性节点方法 getAttributeNode() 返回对应特性的Attr节点,element.getAttributeNode('align')相当于element.attributes['align']
setAttributeNode() 设置元素的Attr节点
其它方法 getElementsByTagName() 在当前元素的后代节点中搜索,其它用法和Document类型的同名方法一样

说明:

(1)特殊特性:

  A、class特性,其对应DOM对象的属性为className,在特性方法操作中参数需要传入class,而对象属性操作需要设置className属性。

  B、style特性,在通过getAttribute()访问时,返回的是style特性值中包含的CSS文本,而通过属性来访问它则会返回一个对象。

  C、事件处理特性,比如onclick,当在元素上使用时,onclick特性中包含的是JavaScript代码,如果通过getAttribute()访问,返回相应代码的字符串,而在访问onclick属性时,会返回一个JavaScript函数(没有相应特性时返回null)。

(2)关于自定义特性和属性,在HTML5规范中,需加上“data-”前缀。

  A、给节点设置特性值时,会同步修改相应DOM对象的属性值,但是设置自定义特性的值时,一般浏览器是不会为相应的DOM对象添加属性的,然而IE也会为自定义特性创建属性。

  B、直接给DOM对象设置属性值时,会同步修改相应节点元素的特性值,但是一般浏览器中自定义属性不会成为元素的特性,使用getArrtibute()时会返回null,然而IE会为自定义属性创建特性。

(3)HTMLElement类型是HTML中元素的基类型,对应不同的标签,还有很多更加具体的子类型,这些子类型也有与之相关的特性和方法,比如对应<body>标签有HTMLBodyElement类型、对应<table>标签有HTMLTableElement类型等。

(4)元素的子节点,可以有任意数目的子节点和后代节点,childNodes属性则包括了所有直接子节点,但是由于不同浏览器在处理注释、文本等节点的不同,会使得childNodes也不同,因此,如果需要遍历元素子节点的话,需要添加节点类型判断:

for(var i=0,l=element.childNodes.length; i < l; i++){
    if(element.childNodes[i].nodeType === 1)//过滤元素子节点
    {
    //对元素子节点做操作
    }
}

(5)元素的每一个特性都由一个Attr节点表示,每个节点都保存在一个NamedNodeMap对象中,这个对象和NodeList与HTMLCollection类似,是一个“动态”的,随文档变化而变化,它有下面的一些方法:

方法 说明
getNamesItem(name) 返回nodeName等于name的节点
removeNamedItem(name) 从列表中移除nodeName等于那么的节点,调用removeNamedItem()与在元素上调用removeAttribute()效果相同,只是前者返回被移除的Attr节点
setNamedItem(node) 向列表中添加节点,以节点的nodeName属性为索引
item(pos) 返回位于数字pos位置处的节点

元素的attributes属性返回的就是一个NamedNodeMap对象,其中包含一系列Attr节点,每个节点的nodeName就是特性名称,nodeValue就是特性值,可以使用attributes属性来遍历元素的特性(原书第268页):

function outputAttributes(element){
    var pairs = new Array(),
        attr;
    for(var i=0, len=element.attributes.length; i < len; i++){
        attr = element.attributes[i];
        if(attr.specified){//specified属性表示特性值是设置还是默认的,为true表示设置的
            pairs.push(attr.nodeName + "=\"" +attr.nodeValue + "\"");
        }    
    }
    return pairs.join(" ");//以空格连接各个特性并返回
}

6、Text类型

  文本节点由Text类型表示,包含的是可以按字面解释的纯文本内容,可以包含转义后的HTML字符,但不能包含HTML代码,Text类型的主要属性和方法有:

类别 属性/方法 说明
属性 length 文本字符数
data 文本字符,和nodeValue值相同
方法 appendData(text) 将text添加到文本节点末尾
deleteData(offset,count) 从offset指定的位置开始删除count个字符
insertData(offset,text) 从offset指定的位置插入text
replaceData(offset,count,text) 用text替换从offset指定的位置开始到offset+count为止处的文本
splitText(offset) 从offset指定的位置将当前文本节点分成两个文本节点
substringData(offset,count) 提取从offset指定的位置开始到offset+count为止处的字符串

说明:

(1)在Node类型中定义了一个normalize()方法,用于将相邻的两个或多个文本子节点合并,需要注意的是这个方法需要在文本节点的父节点上调用;与之相反的是,Text类型提供了splitText()方法,它会将原文本节点分成两个文本节点,原文本节点将包含从开始到指定位置之前的内容,新文本节点包含剩下的内容,最终返回新文本节点。

(2)默认情况下,每个可以包含内容的元素最多只能有一个文本子节点,而且文本不能为空。通过DOM脚本操作时,可能存在有多个文件子节点的情况。

(3)设置文本节点时需要注意,字符串会经过HTML或XML编码。

7、DocumentFragment类型

  在所有节点类型中,只有DocumentFragment是没有对应的标记的,DOM规定DocumentFragment是一种“轻量级”的文档,可以包含和控制节点,但不会像完整的文档那样占用额外的资源。DocumentFragment类型可以作为一个容器来使用,可以把后面要对文档进行的添加、修改、移除等操作先对DocumentFragment进行,然后再将DocumentFragment添加至文档中,从而避免DOM视图的多次渲染。例如:

function addItems(){        
    var fragment = document.createDocumentFragment();//创建一个文档片段作为容器
    var ul = document.getElementById("myList");
    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); //将容器中的节点添加到文档中(但容器本身不会添加至文档树),这样只需要渲染一次页面    
}

需要注意的是,在DocumentFragment中的节点不属于文档,如果将文本中的节点添加至DocumentFragment中,该节点将会从文档树中移除。

8、操作表格

  <table>元素是HTML中最复杂的结构之一,在HTML DOM中还为<table>、<tbody>、<tr>等元素添加了一些属性和方法(请注意table、tbody、tr及td之间的关系):

元素 类别 属性/方法 说明
<table> 属性 caption 保存着对<caption>元素(如果有)的指针
tBodies 是一个<tbody>元素的HTMLCollection
tHead 保存着对<thead>元素(如果有)的指针
tFoot 保存着对<tfoot>元素(如果有)的指针
rows 表格中所有行的HTMLCollection
方法 createTHead() 创建<thead>元素,将其放到表格中,返回引用
createTFoot() 创建<tfoot>元素,将其放到表格中,返回引用
createCaption() 创建<caption>元素,将其放到表格中,返回引用
deleteTHead() 删除<thead>元素
deleteTFoot() 删除<tfoot>元素
deleteCaption() 删除<caption>元素
deleteRow(pos) 删除指定位置的行
insertRow(pos) 向rows集合中的指定位置插入一行
<tbody> 属性 rows 保存着<tbody>元素中行的HTMLCollection
方法 deleteRow(pos) 删除指定位置的行
insertRow(pos) 向rows集合中的指定位置插入一行,返回新插入行的引用
<tr> 属性 cells 保存着<tr>元素中单元格的HTMLCollection
方法 deleteCell(pos) 删除指定位置的单元格
insertCell(pos) 想cells集合中的指定位置插入一个单元格,返回新插入单元格的引用

注意,上表中列出的是原书中提及元素的属性和方法,并不全面,比如对应<td>元素还有HTMLTableCellElement对象,而<tr>对应对象还有rowIndex属性等,实际使用时可以查阅相关的DOM参考手册。这里旨在通过主要的一些属性和方法明确概念,而非参考大全。

posted @ 2012-10-22 23:33  linjisong  阅读(2739)  评论(3编辑  收藏  举报