节点层次
DOM 可以将任何 HTML 和 XML 文档描绘成一个由多层节点构成的结构。节点分为几种不同的类型,每种类型分别表示文档中不同的信息和标记。每个节点都拥有各自的特点、数据和方法,另外也与其他节点存在某种关系。节点之间的关系构成了层次,而所有页面标记则表现为一个以特定节点为根节点的树形结构。
-
Node 类型
- DOM1 级定义了一个 Node 接口,该接口将由 DOM 中的所有节点类型实现。这个 Node 接口在 JavaScript 中是作为 Node 类型实现的;除了 IE 之外,在其他所有浏览器中都可以访问到这个类型。JavaScript 中的所有节点类型都继承自 Node 类型,因此所有节点类型由在 Node 类型中定义的下列 12 个数值常量表示:
- Node.ELEMENT_NODE(1);
- Node.ATTRIBUTE_NODE(2);
- Node.TEXT_NODE(3);
- Node.CDATA_SELECTION_NODE(4);
- Node.ENTITY_REFERENCE_NODE(5);
- Node.ENTITY_NODE(6);
- Node.PROCESSING_INSTRUCTION_NODE(7);
- Node.COMMENT_NODE(8);
- Node.DOCUMENT_NODE(9);
- Node.DOCUMENT_TYPE_NODE(10);
- Node.DOCUMENT_FRAGMENT_NODE(11);
- Node.NOTATION_NODE(12);
- 将 nodeType 属性与数字值进行比较,可以确定节点的类型。
if(someNode.nodeType == 1) { alert('Is a Element'); }
- 详细信息:
-
nodeName 和 nodeValue 属性
- 对于元素节点,nodeName 中保存的始终都是元素的标签名,而 nodeValue 的值则始终为 null。
-
节点关系
- 每一个节点都有一个 childNodes 属性,其中保存着一个 NodeList 对象。NodeList 是一种类数组对象,用于保存一组有序的节点,可以通过位置来访问这些节点。但是,NodeList 不是 Array 的实例,它实际上是基于 DOM 结构动态执行查询的结果,因此 DOM 结构的变化能够自动反映在 NodeList 对象中。由于 IE8 及更早版本将 NodeList 实现为一个 COM 对象,所以不能使用正常的操作方法操作这种对象。必须手动枚举 NodeList 中的所有成员
function convertToArray(nodes) { var array = null; try { array = Array.prototype.slice.call(nodes, 0); } catch (ex) { array = new Array(); for(var i = 0, len = nodes.length; i < len; i++) { array.push(nodes[i]); } } return array; }
- 每一个节点都有一个 parentNode 属性,该属性指向文档树种的父节点。包含在 childNodes 列表中的所有节点都具有相同的父节点,因此它们的 parentNode 属性都指向同一个节点。包含在 childNodes 列表中的每个节点相互之间都是同胞节点。通过使用列表中每个节点的 previousSibling 属性和 nextSibling 属性,可以访问同一列表中的其他节点。
- 父节点的 firstChild 和 lastChild 属性分别指向其 childNodes 列表中的第一个和最后一个节点。
- hasChildNodes() 在节点包含一个或多个子节点的情况下返回 true。(比查询 length 更简单)
- ownerDocument 属性指向表示整个文档的文档节点。任何节点都属于它所在的文档,任何节点都不能同时存在于两个或多个文档中。用此属性可以直接访问文档节点。
-
操作节点
- appendChild() 用于向 childNodes 列表的末尾添加一个节点。添加节点后,childNodes 的新增节点、父节点及以前的最后一个子节点的关系指针都会相应地得到更新。更新完成后,appendChild() 返回新增的节点。
- 任何 DOM 节点都不能同时出现在文档中的多个位置上。因此,如果在调用 appendChild() 时传入了父节点的第一个子节点,那么该节点就会成为父节点的最后一个子节点。
- insertBefore() 将节点放在 childNodes 列表中某个特定的位置上。接收两个参数:要插入的节点和作为参照的节点。要插入的节点会作为变成参照节点的前一个同胞节点(previousSibling),同时被方法返回。如果参照节点是 null ,则与 appendChild() 相同。
- replaceChild() 替换节点。接收两个参数:要插入的节点和要被替换的节点。将由这个方法删除要被替换的节点,同时由要插入的节点占据其位置。
- removeChild() 移除节点。返回被移除的节点。
-
其他方法
- cloneNode() 用于创建调用这个方法的节点的一个完全相同的副本。接收一个 boolean 类型参数,表示是否执行深复制(复制节点及其整个子节点树)。如果是 false 执行浅复制(只复制节点本身)。
- normalize() 处理文档树中的文本节点。当在某个节点上调用这个方法时,就会在该节点的后代节点中查找上述两种情况。如果找到了空文本节点则删除它;如果找到了相邻的文本节点,则将它们合并为一个文本节点。
-
- DOM1 级定义了一个 Node 接口,该接口将由 DOM 中的所有节点类型实现。这个 Node 接口在 JavaScript 中是作为 Node 类型实现的;除了 IE 之外,在其他所有浏览器中都可以访问到这个类型。JavaScript 中的所有节点类型都继承自 Node 类型,因此所有节点类型由在 Node 类型中定义的下列 12 个数值常量表示:
-
Document 类型
- 在浏览器中,document 对象是 HTMLDocument(继承自 Document 类型)的一个实例,表示整个 HTML 页面。
- Document 节点具有如下特征:
- nodeType 9
- nodeName '#document'
- nodeValue null
- parentNode null
- ownerDocument null
- 子节点可能是一个 DocumentType(最多一个)、Element(最多一个)、ProcessingInstruction 或 Comment 。
- Document 类型可以表示 HTML 页面或者其他基于 XML 的文档。最常见的应用还是作为 HTMLDocument 实例的 document 对象。
- Document 节点有两个内置的访问其子节点的快捷方式:documentElement 属性,始终指向 HTML 页面中的 <html> 元素;通过 childNodes 列表访问文档元素。
- 作为 HTMLDocument 的实例,document 对象还有一个 body 属性,直接指向 <body> 元素。
- Document 另一个可能的子节点是 DocumentType。通常将 <!DOCTYPE> 标签看成一个与文档其他部分不同的实体,可以通过 doctype 属性(在浏览器中是 document.doctype )来访问它的信息。但是在 IE8 中会将 DOCTYPE 作为注释,所以 doctype 值始终为 null。
- document 的其他标准属性
- title 包含 <title> 元素中的文本。
- URL 包含页面完整的 URL。
- domain 包含页面的域名。
- referrer 保存着链接到当前页面的那个页面的 URL;在没有来源页面的情况下,referrer 属性可能包含空字符串。
- 查找元素
- getElementById()
- getElementsByTagName()
- IE8 以下,元素 ID 不区分大小写。
- namedItem() 通过元素的 name 特性取得集合中的项。(也可以使用 [] 进行访问)
- 调用 document.getElementsByTagName('*') 取得文档中的所有元素。
- (只有 HTMLDocument 才有)getElementsByName() (如果使用 namedItem() 只会取得所有与传入 name 吻合的元素中的第一个元素)
- 特殊集合
- document.anchors
- document.applets
- document.forms
- document.images
- document.links
- DOM 一致性检测
- document.implementation 检测浏览器实现了 DOM 的哪些部分。
- DOM1 只为 document.implementation 规定了 hasFeature(),接受两个参数:要检测的 DOM 功能的名称及版本号。如果浏览器支持给定名称和版本的功能,则返回 true。(查文档)
- document.implementation 检测浏览器实现了 DOM 的哪些部分。
- 文档写入
- write() / writeln() 接收一个字符串参数,即要写入到输出流中的文本。
- 在文档加载结束后调用 document.write() 那么输出的内容将会重写整个页面。
- open() / close() 打开 / 关闭 当前网页的输出流。如果在页面加载期间使用 write() / writeln(),则不需要使用这两个方法。
- write() / writeln() 接收一个字符串参数,即要写入到输出流中的文本。
-
Element 类型
-
Element 类型用于表现 XML 或 HTML 元素,提供了对元素标签名、子节点及特性的访问。Element 节点具有以下特性:
- nodeType 1
- nodeName 元素标签名
- nodeValue null
- parentNode 可能是 Document 可能是 null
- 其他子节点可能是 Element Text Comment ProcessingInstruction CDATASection EntityReference
访问元素的标签名,可以使用 nodeName 也可以使用 tagName。(在 HTML 中,标签名始终以全部大写表示,而 XML 则始终与源代码中保持一致。)
HTML 元素的关联关系(查手册)
-
取得特性(不区分大小写)
- getAttribute()
-
设置特性
- setAttribute()
- removeAttribute()
-
attributes 属性
- Element 类型是使用 attributes 属性的唯一一个 DOM 类型节点。attributes 属性中包含一个 NamedNodeMap,与 NodeList 类似,也是一个”动态“的集合。元素的每一个特性都由一个 Attr 节点表示,每个节点都保存在 NamedNodeMap 对象中。NamedNodeMap 对象拥有下列方法:
- getNamedItem(name) 返回 nodeName 属性等于 name 的节点;
- removeNamedItem(name) 从列表中移除 nodeName 属性等于 name 的节点;
- setNamedItem(node) 向列表中添加节点,以节点的 nodeName 属性为索引;
- item(pos) 返回位于数字 pos 位置处的节点。
- attribute 属性中包含一系列节点,每个节点的 nodeName 就是特性的名称,而节点的 nodeValue 就是特性的值。要取得元素的 id 特性,可以使用如下代码:
var id = element.attributes.getNamedItem('id').nodeValue; var id_1 = element.attributes['id'].nodeValue;
- 在 IE7- 中,所有被指定的特性或者通过 setAttribute() 设置的特性,其 specified 属性值都会变成 true。
-
创建元素
- document.createElement() 接收一个参数:要创建元素的标签名或完整标签。
var div = document.createElement('div'); var newDiv = document.createElement('<div id=\"myNewDiv\" class=\"box\"></div>');
-
元素的子节点
- 元素可以有任意数目的子节点和后代节点。
- 元素支持 getElementsByTagName() 方法。在通过元素调用这个方法时,除了搜索起点是当前元素之外,其他方面都跟通过 document 调用这个方法相同,因此结果只会返回当前元素的后代。
-
-
Text 类型
-
文本节点由 Text 类型表示,包含的是可以照字面解释的纯文本内容。纯文本中可以包含转义后的 HTML 字符,但不能包含 HTML 代码。Text 节点具有以下特征:
- nodeType 3
- nodeName '#text'
- nodeValue 节点所包含的文本
- parentNode Element
- 不支持(没有)子节点
- 可以通过 nodeValue 或 data 属性访问 Text 节点中包含的文本,这两个属性中包含的值相同。对 nodeValue 的修改也会通过 data 反映出来,反之亦然。
- 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 位置处的字符串。
- length 保存节点中字符的数目。(nodeValue.length == data.length)
- 每个可以包含内容的元素最多只能有一个文本节点,而且必须确实有内容存在。
-
创建文本节点
- document.createTextNode() 接收一个参数:要插入节点中的文本。
- 在创建文本节点时,也会为其设置 ownerDocument 属性。不过,除非把新节点添加到文档树中已经存在的节点中,否则不会在浏览器窗口中看到新的节点。
var element = document.createElement('div'); element.className = 'message'; var textNode = document.createTextNode('<strong>Hello</strong> world!'); element.appendChild(textNode); document.body.appendChild(element);
-
规范化文本节点
- 合并相邻文本节点,由 Node 类型定义,名叫 normalize()。如果在一个包含两个或者多个文本节点的父元素上调用 normalize() 方法,则会将所有文本节点合并成一个节点,结果节点的 nodeValue 值等于将合并前每个文本节点的 nodeValue 值拼接起来的值。
var element = document.createElement('div'); element.className = 'message'; var textNode = document.createTextNode('Hello'); var anotherTextNode = document.createTextNode('World'); element.appendChild(textNode); element.appendChild(anotherTextNode); element.normalize();
-
分割文本节点
- splitText() 将一个文本节点分成两个文本节点,按照指定的位置分割 nodeValue 值。原文本节点将包含从头开始到指定位置之前的内容,新的文本节点包含剩下的文本。该方法返回一个新的文本节点,该节点与原节点的 parentNode 相同。
var element = document.createElement('div'); element.className = 'message'; var textNode = document.createTextNode('Hello World'); element.appendChild(textNode); document.body.appendChild(element); var newNode = element.firstChild.splitText(5);
-
-
Comment 类型
- 注释在 DOM 中是通过 Comment 类型来表示的。Comment 节点具有下列特征:
- nodeType 8
- nodeName '#comment'
- nodeValue 注释内容
- parentNode Document / Element
- 不支持子节点
- 与 Text 类型继承自相同的基类,拥有除 splitText 外所有字符串的操作方法。与 Text 类型相似,也可以通过 nodeValue 或 data 属性取得注释内容。
- 使用 document.createComment() 并为其传递注释文本可以创建注释节点。
- 注释在 DOM 中是通过 Comment 类型来表示的。Comment 节点具有下列特征:
-
CDATASection 类型
- CDATASection 类型值针对基于 XML 的文档,表示 CDATA 区域。CDATASection 节点具有下列特征:
- nodeType 4
- nodeName "#cdata-section"
- nodeValue CDATA 区域中的内容
- parentNode Document / Element
- 与 Text 类型吧继承自相同的基类,拥有除 splitText 外所有字符串的操作方法。
- CDATA 区域只会出现在 XML 文档中,因此多数浏览器会把 CDATA 区域错误的解析为 Comment 或 Element。在真正的 XML 文档中,可以使用 document.createCDataSection() 来创建 CDATA 区域。
- CDATASection 类型值针对基于 XML 的文档,表示 CDATA 区域。CDATASection 节点具有下列特征:
-
DocumentType 类型
- 仅有 Firefox、Safari、Opera 支持。包含于文档的 doctype 有关的所有信息。具有如下特征:
- nodeType 10
- nodeName doctype 的名称
- nodeValue null
- parentNode Document
- 在 DOM1 中,DocumentType 对象智能通过解析文档代码的方式来创建。支持它的浏览器会把 DocumentType 对象保存在 document.doctype 中。DOM1 描述了 DocumentType 的三个属性:name entities notations。
- 仅有 Firefox、Safari、Opera 支持。包含于文档的 doctype 有关的所有信息。具有如下特征:
-
DocumentFragment 类型
- DocumentFragment 在文档中没有对应的标记。DOM 规定文档片段 (document fragment) 是一种”轻量级“的文档,可以包含和控制节点,但不会像完整的文档那样占用额外的资源。DocumentFragment 节点具有下列特征:
- nodeType 11
- nodeName "#document-fragment"
- nodeValue null
- parentNode null
- 子节点可以是 Element ProcessingInstruction Comment Text CDATASection 或 EntityReference
- 创建 DocumentFragment 对象可以使用 document.createDocumentFragment() 方法。
- DocumentFragment 继承了 Node 的所有方法。可以通过 appendChild() 或 insertChild() 将文档片段中的内容添加到文档中。将文档片段作为参数传递给这两个方法时,实际上只会将文档片段的所有子节点添加到相应位置上;文档片段本身永远不会称为文档树的一部分。
- DocumentFragment 在文档中没有对应的标记。DOM 规定文档片段 (document fragment) 是一种”轻量级“的文档,可以包含和控制节点,但不会像完整的文档那样占用额外的资源。DocumentFragment 节点具有下列特征:
-
Attr 类型
- 元素的特性在 DOM 中以 Attr 类型来表示。在所有浏览器中都可以访问 Attr 类型的构造函数和原型。从技术角度来讲,特性就是存在于元素的 attributes 属性中的节点。特性节点具有下列特征:
- nodeType 2
- nodeName 特性名称
- nodeValue 特性的值
- parentNode null
- Attr 对象有三个属性,name value specified。其中,name 是特性名称,value 是特性的值,specified 是布尔值,用以区别特性是代码中指定的还是默认的。
- 使用 document.createAttribute() 并传入特性的名称可以创建新的特性节点。
var attr = document.createAttribute('align'); attr.value = 'left'; element.setAttributeNode(attr);
- 元素的特性在 DOM 中以 Attr 类型来表示。在所有浏览器中都可以访问 Attr 类型的构造函数和原型。从技术角度来讲,特性就是存在于元素的 attributes 属性中的节点。特性节点具有下列特征:
DOM 操作技术
-
动态脚本
动态脚本指的是在页面加载时不存在,但是将来的某个时刻通过修改 DOM 动态添加的脚本。
-
创建动态脚本可以插入外部文件,也可以直接插入 JavaScript 代码。
<script type="text/javascript" src="client.js"></script>
动态创建这个节点的 DOM
var script = document.createElement("script"); script.type = "text/javascript"; script.src = "client.js"; document.body.appendChild(script);
行内 JavaScript 代码也可以使用类似的方式添加。
使用这种方式要考虑兼容性问题。
以这种方式加载的代码会在全局作用域中执行,而且当脚本执行后立即可用。实际上,这样执行的代码与在全局作用域中把相同的字符串传递给 eval() 是一样的。
-
动态样式
- 方法类似于动态脚本。
- 必须将 <link> 元素添加到 <head> 而不是 <body> 中,才能保证在所有浏览器中的行为一致。
- 加载样式与执行 JavaScript 代码是异步的,后边的章节给出了利用时间检测这个过程是否完成的方法。
- IE 将 <style> 视为一个特殊的、与 <script> 类似的节点,不允许访问其子节点。通过访问元素的 styleSheet 属性中的 cssText 属性可以使其接受 CSS 代码。
var style = document.createElement("style"); style.type = "text/css"; try { style.appendChild(document.createTextNode("body {background-color: red}")); } catch (ex) { style.styleSheet.cssText = "body {background-color: red}"; var head = document.getElementByTagName("head")[0]; head.appendChild(style);
-
表格操作
- DOM 为 <table> <tbody> <tr>元素添加了一些属性和方法,以方便表格的创建。(查手册)
-
使用 NodeList
- 每当文档结构发生变化时,NodeList NamedNodeMap 和 HTMLCollection 都会得到更新。从本质上说,所有的 NodeList 对象都是在访问 DOM 文档时实时运行的查询。
- 使用迭代方法对 NodeList 进行操作时,最好使用第二个变量保存 NodeList 的 length。