DOM(文档对象模型)是针对HTML和XML文档的一个API。DOM描绘了一个层次化的节点树。允许开发人员添加、移除、修改页面的某一部分。
节点层次
文档节点是每个文档的根节点,文档元素是文档的最外层元素,文档中其他说有的元素都包含在文档元素中,每个文档只能有一个文档元素,在HTML 中,文档元素始终是<html> 元素,在XML 中没有预定义的元素,因此任何元素都可能成为文档元素。
每一段标记都可以通过树中的一个节点来表示:HTML 元素通过元素节点表示,特性(attribute)通过特性节点表示,文档类型通过文档类型节点表示,而注释则通过注释节点表示,总共有12 种节点类型。
1.Node类型
DOM1 级定义了一个Node 接口,该接口将由DOM中所有节点类型实现。这个Node在JavaScript中是作为Node 类型实现的;JavaScript 中所有节点都继承自Node 类型,因此所有节点类型都共享相同的基本属性和方法。
属性
每个节点都有一个nodeType 属性,用于表明节点的类型。节点类型由在Node类型中定义的下列 12 个数值常量来表示,任何节点类型必居其一。
- Node.ELEMENT_NODE(1)
- Node.ATTRIBUTE_NODE(2)
- Node.TEXT_NODE(3);
- Node.CDATA_SECTION_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属性和上面的常量比较,如果相等,则说明节点是 该类型的元素。由于IE没有公开Node类型的构造函数,所以为了确保跨浏览器兼容,最好还是将nodeType属性与数字值进行比较。
if(someNode.nodeType == 1){ //Node.ELEMENT_NODE alert("Node is an element"); }
可以使用nodeName 和 nodeValue 者两个属性来了解节点的具体信息,对于元素节点,nodeName中保存的始终都是元素的表签名,而nodeValue 则始终为null。
每一个节点都保存者一个childNodes 属性,其中保存着一个NodeList对象。NodeList是一种类似于数组的对象,用于保存一组有序的节点。NodeList 对象的独特之处在于,它实际上是基于DOM结构动态执行查询的结果,因此DOM结构的变化能够自动反应在NodeList 对象中。
可以使用方括号或者item方法来访问NodeList中的对象。
var firstChild = someNode.childNodes[0]; var secondChild = someNode.childNodes.item(1); var count = someNode.childNodes.length;
可以使用Array.prototype.slice()方法将NodeList转换成普通的ArrayList对象。
var arrayOfNodes = Array.prototype.slice.call(someNode.childNodes, 0);
每个节点还有 prentNode、previousSibling、nextSibling、firstChild、lastChild等属性。顾名思义,不再一一介绍。
方法
节点的关系指针都是只读的,所以DOM提供了一些操作节点的方法。其中最常用的是appendChild(),用于向childNodes 列表的末尾添加一个节点。返回新增的节点。
使用insertBefore()方法可以把节点方法childNodes 列表中某个特定的位置上。接收两个参数:要插入的节点和作为参照的节点。插入节点后,被插入的节点会变成参照节点的前一个兄弟节点(previousSibling),同时被方法返回。如果参照节点是null, 则insertBefore() 与 appendChild() 执行相同的操作。
replaceChild()方法接收两个参数:要插入的节点和要替换的节点。要替换的节点将由这个方法返回并从文档树中被移除,同时由要插入的节点占据其位置。
removeChild()方法接收一个参数,即要移除的节点。被移除的节点将成为方法的返回值。
cloneNode() 用于创建调用这个方法的节点的一个完全相同的副本。该方法接收一个布尔值参数,表示是否执行深复制。
2.Document类型
JavaScript 通过Document 类型表示文档,在浏览器中,document 对象是HTMLDocument的一个实例,表示整个HTML页面。而且document对象是window对象的一个属性,因此可以作为全局对象来访问。
document 的 nodeType 为9, nodeName 为“#document”,nodeValue 为null parentNode 为null,ownerDocument为null。
Document 节点的子节点可以是DocumentType、Element、ProcessingInstruction 或 Comment,但还是有两个内置的访问其子节点的快捷方式。
第一个就是documentElement 属性,该属性始终指向 HTML 页面中的<html> 元素。另一个就是body 属性,直接指向<body> 元素。
document 对象有一些标准的Document 对象所没有的属性。这些属性提供了document 对象所表现的页面的一些信息。
第一个属性是 title,包含者<title> 元素中的文本----显示在浏览器窗口的标题栏或标签页上。通过这个属性可以取得当前页面的标题,也可以修改当前页面的标题并反应在浏览器的标题栏中。
URL属性中包含页面完整的URL,domain 属性中包含页面的域名,而referrer 属性中则保存着联街道当前页面的哪个页面的URL。在没有来源页面的情况下,referrer 属性中可能会包含空字符串。所有这些信息都存在于请求的HTTP 头部,只不过是通过这些属性让我们能够在JavaScript中访问他们而已。
document提供了两个方法用于查找某个或某组元素的引用:getElementById() 和 getElementsByTagName()。
getElementsByTagName() 函数返回的是包含0或多个元素的 NodeList。在HTML中,这个方法会返回一个HTMLCollection 对象,作为一个动态集合,该对象与NodeList 非常类似。
getElementsByName() 方法是HTMLDocument 特有的方法,返回带有给定name 特性的所有元素。
document对象还有一些特殊的集合,这些集合都是HTMLCollection对象,为访问文档常用部分提供了快捷方式。
- document.anchors, 包含文档中所有带name特性的<a> 元素。
- document.forms,包含文档中所有的<form> 元素。与 document.getElementsByTagName("form") 得到的结果相同。
- document.images,包含文档中所有的<img> 元素,与 document.getElementsByTagName("img") 得到的结果相同。
- document.links,包含文档中所有带href特性的<a>元素。
3.Element类型
- nodeType 值为 1
- nodeName 值为元素表签名
- nodeValue 值为 null
- parentNode 可能是 Document或 Element
- 其子节点可能是 Element、Text、Comment、ProcessingInstruction、CDATASection 或 EntityReference。
要访问元素的标签名,可以使用nodeName属性,也可以使用tagName 属性;这两个属性会返回相同的值。
所有的HTML元素都由HTMLElement 类型表示,不是直接通过这个类型也是通过它的子类型来表示。HTMLElement 类型直接继承自Element 并添加了一些属性。添加的这些属性分别对应于每个HTML都存在的下列标准特性:
- id,元素在文档中的唯一标识符
- title,有关元素的附加说明信息,一般通过工具提示条显示出来
- lang,元素内容的语言代码,很少使用
- dir,语言的方向,值为“ltr”(left-to-right,从左向右)或“rtl”(right-to-left,从右向左),也甚少使用。
- className,与元素class特性对应,即为元素制定的CSS 类。没有将这个属性命名为class,是因为class 是ECMAScript的保留字。
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title>study</title> </head> <body> <div id="myDiv" class="bd" title="Body text" lang="en" dir="rtl">good morning</div> <script type="text/javascript"> var div = document.getElementById("myDiv"); alert("id=" + div.id + ", class=" + div.className + ", title=" + div.title + ", lang=" + div.title + ", dir=" + div.dir); //id=myDiv, class=bd, title=Body text, lang=Body text, dir=rtl </script> </body> </html>
getAttribute()方法也可以取得属性内容,也可以取得自定义的特性。参数传递属性名称,不去分大小写。
在通过JavaScript 以编程方式操作DOM 时,开发人员经常不使用getAttribute(),而是只使用对象的属性。只有在取得自定义特性值的情况下,才会使用getAttribute() 方法。
setAttribute() 方法可以为对象设置属性,也可以操作自定义特性,通过这个方法设置的特性名会被统一转换成小写形式。
removeAttribute() 方法用于彻底删除元素的特性。调用这个方法不仅会清除特性的值,而且也会从元素中完全删除特性。
Element 类型是使用attributes 属性的唯一一个DOM 节点类型。attributes 属性中包含一个NamedNodeMap,是一个动态集合。元素的每一个特性都是一个Attr节点表示,每个节点都保存在NamedNodeMap对象中。NamedNodeMap对象拥有下列方法:
- getNamedItem(name):返回nodeName属性等于name的节点;
- removeNamedItem(name):从列表中移除nodeName属性等于name的节点;
- setNamedItem(node):向列表中添加节点,以节点的nodeName 属性为索引;
- item(pos):返回位于数字pos位置处的节点。
attributes属性中包含一系列节点,每个节点的nodeName 就是特性的名称,而节点的 nodeValue 就是特性的值。要取得元素的id特性,可以使用以下代码。
var id = element.attributes.getNamedItem("id").nodeValue; var id = element.attributes["id"].nodeValue; element.attributes["id"].nodeValue = "newId";
一般来说,因为attributes 的方法不够方便,因此开发人员更多的会使用 getAttribute()、setAttributes()、removeAttributes()方法。
创建元素,使用document.creatElement()可以创建新元素,这个方法只接收一个参数,即要创建元素的标签名,
var div = document.createElement("div"); div.id = "myNewDiv"; div.className = "box";
4.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 属性,保存着节点中字符的数目。
创建文本节点,可以使用 document.createTextNode()创建新文本节点,这个方法接受一个参数----要插入的文本。
DOM操作技术
操作表格
<table> 元素是 HTML中最复杂的结构之一,要想创建表格,一般都必须设计表示表格行、单元格、表头等方面的标签。由于涉及的标签多,因此使用核心的DOM方法创建和修改表格往往都免不了要编写大量的代码。为了方便构建表格,HTML DOM 还为<table>、<tbody>和<tr>元素添加了一些属性和方法。
为<table>元素添加的属性和方法:
- caption:保存着对<caption>元素的指针;
- tBodies:是一个<tbody> 元素的HTMLCollection。
- tFoot:保存着对 <tfoot>元素的指针;
- tHead:保存着对<thead>元素的指针;
- 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集合中的指定位置插入一个单元格返回对新单元格的引用。
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title>study</title> </head> <body> <script type="text/javascript"> var table = document.createElement("table"); table.border = 1; table.width = "100%"; //create tbody var tbody = document.createElement("tbody"); table.appendChild(tbody); //first row tbody.insertRow(0); tbody.rows[0].insertCell(0); tbody.rows[0].cells[0].appendChild(document.createTextNode("Cell 1, 1")); tbody.rows[0].insertCell(1); tbody.rows[0].cells[1].appendChild(document.createTextNode("Cell 1, 2")); //second row tbody.insertRow(1); tbody.rows[1].insertCell(0); tbody.rows[1].cells[0].appendChild(document.createTextNode("Cell 2, 1")); tbody.rows[1].insertCell(1); tbody.rows[1].cells[1].appendChild(document.createTextNode("Cell 2, 2")); document.body.appendChild(table); </script> </body> </html>
NodeList
NodeList、NamedNodeMap 和 HTMLCollection 三个集合都是动态的集合,每当文档结构发生变化是,他们都会更新。因此他们始终都会保存着最新、最准确的信息。从本质上说,所有NodeList对象都是在访问DOM文档时实时运行查询。
var divs = document.getElementsByTegName("div"), i, div; for(i = 0; i < divs.length; i++){ div = document.createElement("div"); document.body.appendChild(div); }
由于divs是动态集合,所以上面代码会导致无限循环,解决方法是把divs.length保存到另一个变量中进行缓存。
一般来说,应该尽量减少访问NodeList的次数。
小结
DOM是语言中立的 API,用于访问HTML 和XML 文档。DOM1级将HTML和XML 文档形象地看做一个层次化的节点树,可以使用JavaScript来操作这个节点树,进而改变底层文档的外观和结构。
- 最基本的节点类型是Node, 用于抽象地表示文档中一个独立的部分;所有其他类型都继承自Node。
- Document 类型表示整个文档,是一组分层节点的根节点,在JavaScript 中, document对象是Document的一个实例。使用document 对象,有很多中方式可以查询和取得节点。
- Element 节点表示文档中所有HTML 或 XML 元素,可以用来操作这些元素的内容和特性。
- 另外还有一些节点类型,分别表示文本内容、注释、文档类型、CDATA区域和文档片段。