(DOM(文档对象模型)是针对HTML和XML文档的一个API(应用程序编程接口)。
DOM描绘了一个层次化的节点树,允许开发人员添加、移除和修改页面的某一部分。
1998年10月DOM1级规范成为W3C的推荐标准,为基本的文档结构及查询提供了接口。
IE中的所有DOM对象都是以COM对象的形式实现的。所以IE中的DOM对象与原生的JavaScript对象的行为或活动特点并不一致。
节点层次
节点分为几种不同的类型,每种类型分别表示文档中不同的信息及标记。每个节点都拥有各自的特点、数据和方法,也与其他节点存在某种关系。
eg:
<html>
<head>
<titele>Sample Page</titele>
</head>
<body>
<p>Hello World!</p>
</body>
<html>
每一段标记都可以通过树中的一个节点来表示,总共有12中节点类型,这些类型都继承自一个基类型。
Node类型
DOM1级定义了一个Node接口,该接口由DOM中的所有节点类型实现。
Node接口在JavaScript中是作为Node类型实现的,除IE之外,其他所有浏览器都可以访问到这个类型。
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)。
确定节点类型的代码如下:
if (someNode.nodeType == Node.ELEMENT_NODE) {
//在IE中无效
alert("Node is an element.");
}
为了确保跨浏览器兼容,最好还是将nodeType属性与数字值进行比较:
if(someNode.nodeType == 1) {
//适用于所有浏览器
alert("Node is an element.");
}
- nodeName和nodeValue属性
使用nodeName和nodeValue属性最好先检测节点类型。
if (someNode.nodeType == 1) {
value = someNode.nodeName; //nodeName的值是元素的标签名
}
- 节点关系
在HTML中,可以将元素看成是元素的子元素,相应的元素就是元素的父元素。元素可以看成是元素的同胞元素。
每个节点都有一个childNodes属性,其中保存着一个NodeList对象,用于保存一组有序的节点,可以通过位置来访问这些节点。
NodeList不是Array的实例。
可以通过方括号,也可以使用item()方法来访问NodeList中的节点。
eg:
var firstChild = someNode.childNodes[0];
var secondChild = someNode.childNodes.item(1);
var count = someNode.chldNodes.length;
length属性表示的是访问NodeList那一刻,其中包含的节点数量。
每个节点都有一个parentNode属性,指向文档树中的父节点。
通过使用列表中每个节点的previousSibling和nextSibling属性可以访问同一列表中的其他节点。列表中第一个节点的previousSibling属性为null,最后一个节点的nextSibling属性为null。
hasChildNodess()方法在节点包含一个或多个子节点的情况下返回true。
所有节点都有一个ownerDocument属性,指向表示整个文档的文档节点。
- 操作节点
appendChild()方法用于向childNodes列表的末尾添加一个节点。添加节点后,childNodes的新增节点、父节点及以前的最后一个子节点的关系指针都会相应的得到更新。更新完成后,appendChild()返回新增的节点。
var returnedNode = someNode.appendChild(newNode);
alert(returnedNode == newNode); //true
alert(someNode.lastChild == newNode); //true
如果传入到appendChild()中的节点已经是文档的一部分了,那么就会将该节点从原来的位置转移到新位置。
var returnedNode = someNode.appendChild(someNode.firstChild);
alert(returnedNode == someNode.firstChild); //false
alert(returnedNode == someNode.lastChild); //true
使用insertBefore()方法可以将节点放在childNodes列表中某个特定的位置上,这个方法接收两个参数:要插入的节点和作为参照的节点。
//插入后成为最后一个子节点
returnedNode = someNode.insertBefore('newNode', null);
alert(newNode == someNode.lastChild); //true
//插入后成为第一个子节点
returnedNode = someNode.insertBefore('newNode', someNode.firstChild);
alert(newNode == someNode.firstChild); //true
//插入到最后一个子节点前面
returnedNode = someNode.insertBefore('newNode', someNode.lastChild);
alert(newNode == someNode.childNodes[someNode.childNodes.length - 2]); //true
replaceChild()方法接受两个参数:要插入的节点和要替换的节点。要替换的节点将又这个方法返回并从文档树中被移除,由要插入的节点占据其位置。
//替换第一个子节点
var returnedNode = someNode.replaceChild('newNode', someNode.firstChild);
//替换最后一个子节点
returnedNode = someNode.replaceChild('newNode', someNode.lastChild);
removeChild()方法接收一个参数,即要移除的节点,被移除的节点将成为方法的返回值。
//移除第一个子节点
var returnedNode = someNode.removeChild('newNode', someNode.firstChild);
//移除最后一个子节点
returnedNode = someNode.removeChild('newNode', someNode.lastChild);
通过replaceChild()和removeChild()方法移除的节点,仍然为文档所有,只是在文档中已经没有位置了。
以上4个方法操作的都是某个节点的子节点,要使用这几个方法必须先取得父节点。
并不是所有节点都有子节点,在不支持子节点的节点上调用以上方法将会导致错误。
- 其他方法
cloneNode()方法和normalize()方法是所有类型的节点都有的。
cloneNode()方法,用于创建调用这个方法的节点的一个完全相同的副本,该方法接收一个布尔值参数,表示是否执行深复制(复制节点及其整个子节点树)。
通过cloneNode()方法复制的节点必须通过appendChild()、insertBefore()或replaceChild()添加到文档中。
eg:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<title>Document</title>
</head>
<body>
<ul id="myList">
<li>item 1</li>
<li>item 2</li>
<li>item 3</li>
</ul>
<script>
var myList = document.getElementById("myList");
var deepList = myList.cloneNode(true);
alert(deepList.childNodes.length); //3(ie < 9)或7(其他浏览器)
var shallowList = myList.cloneNode(false);
alert(shallowList.childNodes.length); //0
</script>
</body>
</html>
normalize()方法唯一的作用就是处理文档树中的文本节点。
Document类型
Document类型表示文档,document对象是HTMLDocument(继承自Document类型)的一个实例,表示整个HTML页面。
document对象是window对象的一个属性,具有全局作用域。
Document节点的特征:
- nodeType值为9;
- nodeName的值为“#document”;
- nodeValue的值为null;
- parentNode的值为null;
- ownerDocument的值为null;
- 其子节点可能是一个DocumentType(最多一个)、Element(最多一个)、ProcessingInstruction或Comment。
- 文档的子节点
documentElement属性始终指向HTML页面中的元素。
var html = document.documentElement; //取得对<html>的引用
alert(html === document.childNodes[0]; //true
alert(html === document.firstChild); //true
document对象还有一个body属性,直接指向元素。
var body = document.body; //取得<body>的引用
所有浏览器都支持document.documentElement和document.body属性。
通常将<!DOCTYPE>标签看成一个与文档其他部分不同的实体,可以通过doctype属性来访问它的信息。
var doctype = document.doctype; //取得<!DOCTYPE>引用
浏览器对document.doctype的支持差别很大,总结如下:
- IE8及之前版本:如果存在文档类型说明,会将其错误地解释为一个注释并把它当做Comment节点;而document.doctype的值始终未null。
- IE9+及Firefox:如果存在文档类型声明,则将其作为文档的第一个子节点:document.doctype是一个DocumentType节点,也可以通过document.firstChild或document.childNodes[0]访问同一个节点。
- Safari、Chrome和Opera:如果存在文档类型声明,则将其解析,但不作为文档的子节点。document.doctype是一个DocumentType节点,但该节点不会出现在document.childNodes中。
浏览器在处理标签外部的注释时存在以下差异:
以简单的Html页面为例。
<!-- 第一条注释 -->
<html>
<body>
</body>
</html>
<!-- 第二条注释 -->
- IE8及之前版本、Safari 3.1及更高版本、Opera和Chrome只为第一条注释创建节点,部位第二条注释创建节点。结果,第一条注释就会成为document.childNodes中的第一个子节点。
- IE9及更高版本会将第一条注释创建为document.childNodes中的一个注释节点,也会将第二条注释创建为document.childNodes中的注释节点。
- Firefox以及Safari3.1之前的版本会完全忽略这两条注释。
多数情况下,我们都用不着在document对象上调用appendChild()、removeChild()和replaceChild()方法,因为文档类型(如果存在的话)是只读的,而且它只有一个元素子节点(该节点通常早就已经存在了)。
- 文档信息
title属性包含元素中的文本。
//取得文档标题
var originalTitle = document.title;
//设置文档标题
document.title = "New page title";
URL属性包含页面完成的URL(即地址栏中显示的URL)
domain属性中包含页面的域名
referrer属性中保存着链接到当前页面的那个页面的URL。
//取得完整的URL
var url = document.URL;
var domain = document.domain;
//取得来源页面的URL
var referrer = document.referrer;
URL和referrer属性不可设置,domain可以设置,但是有限制:只能将domain设置为RUL中包含的域,并且如果域名一开始是“松散的”,就不能将它再设置为“紧绷的”。
eg:
//假设页面来自p2p.wrox.com域
document.domain = "wrox.com"; //成功
document.domain = "nczonline.net"; //失败
document.domain = "wrox.com"; //松散的(成功)
document.domain = "p2p.wrox.com"; //紧绷的(出错)
来自不同子域的页面无法通过javascript通信。
- 查找元素
Document类型提供以下方法取得元素引用:
-
getElementById(),接收一个参数:要取得的元素的ID。如果找到相应的元素则返回该元素,如果不存在返回null。
IE8及较低版本不区分ID的大小写。
如果页面中多个元素的ID值相同,则返回第一次出现的元素。 -
getElementsByTagName(),接收一个参数:要取得元素的标签名,返回包含零或多个元素的NodeList。在HTML文档中,返回一个HTMLCollection对象。
-
getElementsByName(),接收一个参数:要取得元素的name。返回带有给定name特性的所有元素。
- 特殊集合
除了属性和方法,document对象还有一些特殊的集合,这些集合都是HTMLCollection对象:
- document.anchors,包含文档中所有带name特性的元素;
- document.applets,包含文档中所有的元素,因为不在推荐使用元素,所以这个集合已经不建议使用了;
- document.forms,包含文档中所有的元素,与document.getElementsByTagName(“form”)得到的结果相同。
- document.images,包含文档中素有的元素,与document.getElementsByTagName(“img”)得到的结果相同;
- document.links,包含文档中所有带href特性的元素。
- DOM一致性检测
document.implementation属性提供了浏览器对DOM的实现相关信息和功能对象。
该属性的hasFeature()方法接收两个参数:要检测的DOM功能的名称及版本号。如果支持给定名称和版本的功能,返回true。
var hasXmlDom = document.implementation.hasFeature("XML", "1.0");
功 能 | 版 本 号 | 说 明 |
---|---|---|
Core | 1.0、2.0、3.0 | 基本的DOM,用于描述表现文档的节点树 |
XML | 1.0、2.0、3.0 | Core的XML扩展,添加了对CDATA、处理指令及实体的支持 |
HTML | 1.0、2.0 | XML的HTML扩展,添加了对HTML特有元素及实体的支持 |
Views | 2.0 | 基于某些样式完成文档的格式化 |
StyleSheets | 2.0 | 将样式表关联到文档 |
CSS | 2.0 | 对层叠样式表1级的支持 |
CSS2 | 2.0 | 对层叠样式表2级的支持 |
Events | 2.0、3.0 | 常规的DOM事件 |
UIEvents | 2.0、3.0 | 用户界面事件 |
MouseEvents | 2.0、3.0 | 由鼠标引发的事件(click、mouseover等) |
MutationEvents | 2.0,3.0 | DOM树变化时引发的事件 |
HTMLEvents | 2.0 | HTML4.01事件 |
Range | 2.0 | 用于操作DOM树中某个范围的对象和方法 |
Traversal | 2.0 | 遍历DOM树的方法 |
LS | 3.0 | 文件与DOM树之间的同步加载和保存 |
LS-Async | 3.0 | 文件与DOM树之间的异步加载和保存 |
Validation | 3.0 | 在确保有效的前提下修改DOM树的方法 |
- 文档写入
四个方法可以将输出流写入到网页中:
- write(),接受衣蛾字符串参数,既要写入到输出流中的文本。
<html>
<head>
<title>document.write() Example</title>
</head>
<body>
<p>The current date and time is:
<script type="text/javascript">
document.write("<strong>" + (new Date()).toString() + "</strong>");
</script>
</p>
</body>
</html>
- writelnm(),接收一个字符串参数,既要写入到输出流中的文本。
<html>
<head>
<title>document.write() Example2</title>
</head>
<body>
<script type="text/javascript">
document.writeln("<script type=\"text/javascript\" src=\"file.js\">" + "<\/script>");
</script>
</body>
</html>
- open(),打开网页输出流。
- close(),关闭网页输出流。
严格型XHTML文档不支持文档写入。
Element类型
Element类型用于表现XML或HTML元素,提供了对元素标签名、子节点及特性的访问。
特征:
- nodeType值为1;
- nodeName值为元素的标签名;
- nodeValue值为null;
- parentNode可能是Document或Element;
- 其子节点可能是Element、Text、Comment、ProcessingInstruction、CDATASection或EntityReference。
访问元素的标签名可以使用nodeName属性,也可以使用tagName属性。
<html>
<head>
<title>Example</title>
</head>
<body>
<div id="myDiv"></div>
<script type="text/javascript">
var div = document.getElementById("myDiv");
alert(div.tagName); //"DIV"
alert(div.tagName == div.nodeName); //true
</script>
</body>
</html>
在HTML中,标签名始终以全部大写表示;在XML(有时候也包括XHTML)中,标签名始终与源代码保持一致。
- HTML元素
所有HTML元素都有HTMLElement类型表示。HTMLElement类型直接继承自Element并添加了一些属性。
每个HTML元素中都存在以下标准特性:
- id,元素在文档中的唯一标识符。
- title,有关元素的附加说明信息,一般通过工具提示条显示。
- lang,元素内容的语言代码,很少使用。
- dir,语言的方向,值为ltr或rtl,也很少使用。
- className,与元素的class特性对应,为元素指定的CSS类。
<html>
<head>
<title>Example</title>
</head>
<body>
<div id="myDiv" class="db" title="Body text" lang="en" dir="ltr"></div>
<script type="text/javascript">
var div = document.getElementById("myDiv");
alert(div.id); //"myDiv"
alert(div.className); //"db"
alert(div.title); //"Body text"
alert(div.lang); //"en"
alert(div.dir); //"ltr"
</script>
</body>
</html>
- 取得特性
每个元素都有一个或多个特性,这些特性的用途是给出相应元素或其内容的附加信息。
操作特性的DOM方法主要由三个:
- getAttribute()
- setAttribute()
- removeAttribute()
传递给getAttribute()的特姓名与实际的特性名相同,如果给定名称的特性不存在,该方法返回null。
通过getAttribute()可以取得自定义特性的值。
根据HTML5规范,自定义特性应该加上data-前缀以便验证。
- 设置特性
setAttribute()方法接收两个参数:要设置的特姓名和值。如果特性已存在,该方法会以指定值替换现有值,如果不存在,该昂发会创建该特性并设置相应的值。
使用removeAttribute()方法用于彻底删除元素的特性,调用这个方法不仅会清除特性的值,也会从元素中完全删除特性。
- attributes属性
Element类型是使用attribute属性的唯一一个DOM节点类型。
元素的每一个特性都由一个Attr节点表示,每个节点都保存在NamedNodeMap对象中。
NamedNodeMap对象拥有下列方法:
- getNamedItem(name):返回nodeName属性等于name的节点;
- removeNamedItem(name):从列表中移除nodeName属性等于name的节点;
- setNamedItem(node):向列表中添加节点,以节点的nodeName属性为索引;
- item(pos):返回位于数字pos位置处的节点。
- 创建元素
document.createElement()方法可以创建新元素,该方法接收一个参数,即要创建元素的标签名,这个签名在HTML文档中不区分大小写,在XML中区分。
在使用createElement()方法创建新元素的同时,也为新元素设置了ownerDocument属性。
在IE中可以以另外一种方式使用createElement(),即为这个方法传入完整的元素标签,也可以包含属性。
eg:
var div = document.createElement("<div id=\"myNewDiv\" class=\"box\"></div>");
通过以上方法可以避开在IE7及更早版本中动态创建元素的某些问题:
- 不能设置动态创建的>元素的name特性。
- 不能通过表单的reset()方法重设动态创建的元素。
- 动态创建的type特性值为"reset"的元素重设不了表单。
- 动态创建的一批name相同的单选按钮彼此毫无关系。name值相同的一组单元按钮本来应该用于表示同一选项的不同值,但动态创建的一批这种单选按钮之间却没有这种关系。
- 元素的子节点
元素可以有任意数目的子节点和后代节点。
元素的childNodes属性中包含了它的所有子节点,这些子节点有可能是元素、文本节点、注释或处理指令。不同浏览器看待这些节点方面存在显著的不同。
如:
<ul id="myList">
<li>Item 1</li>
<li>Item 2</li>
<li>Item 3</li>
</ul>
以上代码在IE中会有3个子节点,分别是3个
- 元素,而在其他浏览器中会有7个元素,包括3个
- 元素和4个文本节点(表示
- 元素之间的空白符)。
如果删除空白符: -
<ul id="myList"><li>Item 1</li><li>Item 2</li><li>Item 3</li></ul>
以上代码在任何浏览器中都包含3个子节点。
如果需要通过childNodes属性遍历子节点,那么一定不要忘记浏览器间的这一差别。在执行某项操作前,先检查一下nodeType属性,当nodeType等于1时,再执行某些操作for (var i = 0, len = element.childNodes.length; i< len; i++) { if (element.childNodes[i].nodeType == 1) { //执行某些操作 } }
元素也支持getElementsByTagName()方法,只是搜索起点是当前元素。
Text类型
文本节点由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也保存着同样的值。
在默认情况下,每个可以包含内容的元素最多只能有一个文本节点,而且必须确实有内容存在。
<!-- 没有内容,也就没有文本节点 --> <div></div> <!-- 有空格,因而有一个文本节点 --> <div> </div> <!-- 有内容,因而有 一个文本节点 --> <div>Hello World!</div>
可以访问和修改文本子节点
var textNode = div.firstChild; //或者div.childNodes[0] div.firstChild.nodeValue = "Some other message";
在修改文本节点时,字符串会经过HTML(或XML,取决于文档类型)编码。
div.firstChild.nodeValue = "Some <strong>other</strong>message"; //输出结果是"Some <strong>other</strong>message"
- 创建文本节点
document.createTextNode()创建新文本节点,该方法接受一个参数——要插入节点中的文本。在新建文本节点的同时,也会为其设置ownerDocument属性。
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8" /> <title>Document</title> </head> <body> <script type="text/javascript"> var element = document.createElement("div"); element.className = "message"; var textNode = document.createTextNode("Hello world!"); element.appendChild(textNode); document.body.appendChild(element); </script> </body> </html>
此处需要注意,不能将JavaScript代码写到内,否则会出现类似“Cannot read property ‘appendChild’ of null”的错误,执行javascript时页面还未完全加载。
如果一个元素包含多个文本子节点,并且两个文本节点是相邻的同胞节点,那么这两个节点中的文本就会连起来显示,中间不会有空格。
- 规范化文本节点
如果在一个包含两个或多个文本节点的父元素上调用normalize()方法,则会将所有文本节点合并成一个节点,结果节点的nodeValue等于将合并前每个文本节点的nodeValue值拼接起来的值。
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8" /> <title>Document</title> </head> <body> <script type="text/javascript"> var element = document.createElement("div"); element.className = "message"; var textNode = document.createTextNode("Hello world!"); element.appendChild(textNode); var otherTextNode = document.createTextNode("Yippee!"); element.appendChild(otherTextNode); document.body.appendChild(element); alert(element.childNodes.length); //2 element.normalize(); alert(element.childNodes.length); //1 alert(element.firstChild.nodeValue); //"Hello world!Yippee!" </script> </body> </html>
- 分割文本节点
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); alert(element.firstChild.nodeValue); //"Hello" alert(newNode.nodeValue); //" world!" alert(element.childNodes.length); //2
Comment类型
DOM中Comment表示注释类型,具有下列特征:
- nodeType值为8;
- nodeName值为"#comment";
- nodeValue值是注释的内容;
- parenNode可能是Document或Element;
- 不支持(没有)子节点。
Comment类型与Text类型继承自相同的基类,因此它拥有除splitText()之外的所有字符串操作方法。与Text类型相似,也可以通过nodeValue或data属性来取得注释内容。
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8" /> <title>Document</title> </head> <body> <div id="myDiv"><!-- A Comment --></div> <script type="text/javascript"> var div = document.getElementById("myDiv"); var comment = div.firstChild; alert(comment.data); //"A Comment" </script> </body> </html>
CDATASection类型
CDATASection类型只针对基于XML的文档,表示的是CDATA区域。CDATASection节点具有下列特征:
- nodeType值Wie4;
- nodeName值为"#cdata-section";
- nodeValue值是CDATA区域中的内容;
- parentNode可能是Document或Element;
- 不支持(没有)子节点。
DocumentType类型
仅有Firefox、Safari和Opera支持DocumentType类型。该类型包含着与文档的doctype有关的所有信息,具有下列特征:
- nodeType的值为10;
- nodeName的值为doctype的名称;
- nodeValue的值为null;
- ParentNode是Document;
- 不支持(没有)子节点。
在DOM1级中,DocumentType对象不能冬天创建,只能通过解析文档代码的方式来创建。
DocumentFragment类型
DocumentFragment类型在文档中没有对应的标记,DOM规定文档片段(document fragment)是一种“轻量级”的文档,可以包含和控制节点,但不会像完整文档那样占用额外的资源。该类型具有以下特征:
- nodeType的值为11;
- nodeName值为“#document-fragment”;
- nodeValue值为null;
- parentNode值为null;
- 子节点可以是Element、ProcessingInstruction、Comment、Text、CDATASection或EntityReference。
可以将文档片段当做一个仓库来使用,字啊里面保存将来可能会添加到文档中的节点。
使用document.createDocumentFragment()方法可以创建文档片段。
文档片段继承了Node的所有方法,通常用于执行那些针对文档的DOM操作。
如果将文档中的节点添加到文档片段中,就会从文档树中移除该节点。
使用文档片段可以避免浏览器反复渲染新信息的问题。
eg:<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8" /> <title>Document</title> </head> <body> <ul id="myList"><!-- A Comment --></ul> <script type="text/javascript"> 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); </script> </body> </html>
Attr类型
元素的特性在DOM中已Attr类型来表示。在所有浏览器中都可以访问Attr类型的构造函数和原型。该类型具有以下特征:
- nodeType值为11;
- nodeName值为特性的名称;
- nodeValue的值为特性的值;
- parentNode值为null;
- 在HTML中不支持(没有)子节点;
- 在XML中子节点可以是Text或EntityReference。
尽管Attr也是节点,但特性不被认为是DOM文档树的一部分。
开发人员最常使用的是getAttribute()、setAttribute()和removeAttribute()方法,很少直接饮用特性节点。
Attr对象有3个属性:name、value和specified。
name:特性名称。
value:特性的值
specified:布尔值,用于区分特性是在代码中指定的还是默认的。var attr = document.createAttribute("align"); attr.value = "left"; element.setAttributeNode(attr); alert(element.attributes["align"].value); //"left" alert(element.getAttributeNode("align").value); //"left" alert(element.getAttribute("align")); //"left"
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);
将以上代码进行封装后:
function loadScript(url) { var script = document.createElement("script"); script.type = "text/javascript"; script.src = url; document.body.appendChild(script); }
可以通过行内方式指定JavaScript代码:
<script type="text/javascript"> function sayHi(){ alert("hi"); } </script>
以上代码也可以通过DOM代码的方式:
var script = document.createElement("script"); script.type = "text/javascript"; script.appendChild(document.createTextNode("function sayHi(){alert('hi');}); document.body.appendChild(script);
以上代码在Firefox、Safari、Chrome和Opera中正常,但在IE中会导致错误,因为IE不允许DOM访问其子节点。但是可以设置script的text属性。
var script = document.createElement("script"); script.type = "text/javascript"; script.text="function sayHi(){alert('hi');}"; document.body.appendChild(script);
一种更具兼容性的实现方式是:
var script = document.createElement("script"); script.type = "text/javascript"; var code = "function sayHi(){alert('hi');}"; try{ script.appendChild(document.createTextNode(code)); } catch (ex) { script.text = code; } document.body.appendChild(script);
进一步封装成函数:
function loadScriptString(code){ var script = document.createElement("script"); script.type = "text/javascript"; try{ script.appendChild(document.createTextNode(code)); } catch (ex) { scrpt.text = code; } document.body.appendChild(script); }
动态样式
元素用于包含来自外部的文件,典型的元素用法:
使用DOM代码创建动态样式:
var link = document.createElement("link"); link.rel = "stylesheet"; link.type = "text/css"; link.href = "style.css"; var head = document.getElementsByTagName("head")[0]; head.appendChild(link);
进一步封装以上代码:
function loadStyle(url) { var link = document.createElement("link"); link.rel = "stylesheet"; link.type = "text/css"; link.href = url; var head = document.getElementsByTagName("head")[0]; head.appendChild(link); }
典型的嵌入式css:
<style type="text/css"> body { background-color: red; } </style>
使用DOM代码创建:
var style = document.createElement("style"); style.type = "text/css"; style.appendChild(document.createTextNode("body{background-color:red}")); var head = document.getElementsByTagName("head")[0]; head.appendChild(style);
以上代码在IE中依然会报错,因为
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.getElementsByTagName("head")[0]; head.appendChild(style);
兼容的解决方案是:
function loadStyleString(css) { var style = document.createElement("style"); style.type = "text/css"; try{ style.appendChild(document.createTextNode(css)); } catch (ex) { style.styleSheet.cssText = css; } var head = document.getElementsByTagName("head")[0]; head.appendChild(style); }
在IE中,重用同一个
操作表格
使用核心DOM方法创建和修改表格通常需要编写大量大码。
eg:<table boreder="1" width="100%"> <tbody> <tr> <td>Cell 1,1</td> <td>cell 2,1</td> </tr> <tr> <td>Cell 1,2</td> <td>cell 2,2</td> </tr> </tbody> </table>
使用核心DOM创建:
//创建table var table = document.createElement("table"); table.border = 1; table.width = "100%"; //创建tbody var tbody = document.createElement("tbody"); table.appendChild(tbody); //创建第一行 var row1 = document.createElement("tr"); tbody.appendChild(row1); var cell1_1 = document.createElement("td"); cell1_1.appendChild(document.appendTextNode("Cell 1,1")); row1.appendChild(cell1_1); var cell2_1 = document.createElement("td"); cell2_1.appendChild(document.appendTextNode("Cell 2,1")); row1.appendChild(cell2_1); //创建第二行 var row2 = document.createElement("tr"); tbody.appendChild(row2); var cell1_2 = document.createElement("td"); cell1_2.appendChild(document.appendTextNode("Cell 1,2")); row2.appendChild(cell1_2); var cell2_2 = document.createElement("td"); cell2_2.appendChild(document.appendTextNode("Cell 2,2")); row2.appendChild(cell2_2); //将表格添加到文档主体中 document.body.appendChild(table);
为了方便构建表格,HTML DOM为
、、和等元素添加了一些属性和方法。
元素: * caption:保存着对元素的HTMLCollection。 * tFoot:保存着对元素(如果有)的指针。 * tHead:保存着对元素(如果有)的指针。 * rows:是一个表格中所有行的HTMLCollection。 * createTHead():创建元素,将其放到表格中,返回引用。 * createTFoot():创建元素,将其放到表格中,返回引用。 * createCaption():创建元素。 * deleteTFoot():删除元素。 * deleteCaption():删除
元素: * rows:保存着元素中行的HTMLCollection。 * deleteRow(pos):删除指定位置的行。 * insertRow(pos):向rows集合中的指定位置插入一行,返回对新插入行的引用。 元素: * cells:保存着元素中单元格的HTMLCollecton。 * deleteCell(pos):删除指定位置的单元格。 * insertCell(pos):向cells集合中的指定位置插入一个单元格,返回对新插入单元格的引用。元素(如果有)的指针。 * tBodies:是一个 元素,将其放到表格中,返回引用。 * deleteTHead():删除 元素。 * deleteRow(pos):删除指定位置的行。 * insertRow(pos):向rows集合中的指定位置插入一行。 使用以上方法重写表格DOM代码:
//创建table var table = document.createElement("table"); table.border = 1; table.width = "100%"; //创建tbody var tbody = document.createElement("tbody"); table.appendChild(tbody); //创建第一行 tbody.insertRow(0); tbody.rows[0].insertCell(0); tboyd.rows[0].cells[0].appendChild(document.createTextNode("cell 1,1")); tbody.rows[0].insertCell(1); tboyd.rows[0].cells[1].appendChild(document.createTextNode("cell 2,1")); //创建第二行 tbody.insertRow(1); tbody.rows[1].insertCell(0); tboyd.rows[1].cells[0].appendChild(document.createTextNode("cell 1,2")); tbody.rows[1].insertCell(1); tboyd.rows[1].cells[1].appendChild(document.createTextNode("cell 2,2")); //将表格添加到文档主体中 document.body.appendChild(table);
使用NodeList
NodeList、NamedNodeMap和HTMLCollection都是动态的,每当文档结构发生变化时,他们都会得到更新。
从本质上说,所有NodeList对象都是在访问DOM文档时实时运行的查询。
以下代码会导致无限循环:var divs = document.getElementsByTagName("div"), i, div; for (i = 0; i < divs.length; i++) { div = document.createElement("div"); document.body.appendChild(div); }
想要迭代一个NodeList,最好使用length属性初始化第二个变量,然后将迭代器与该变量进行比较:
var divs = document.getElementsByTagName("div"), i, len, div; for (i = 0, len = divs.length; i < len; i++) { div = document.createElement("div"); document.body.appendChild(div); }
DOM是语言中立的API,用于访问和操作HTML和XML文档。DOM1级将HTML和XML文档形象的看作一个层次化的节点树,可以使用JavaScript来操作这个节点树,进而改变底层文档的外观和结构。
DOM由各种节点构成,简要总结如下:- 最近本的节点类型是Node,用于抽象地表示文档中的一个独立的部分;所有其他类型都继承自Node。
- Document类型表示整个文档,是一组分层节点的跟节点。在JavaScript中,document对象是Document的一个实例。使用document对象,有很多中方式可以查询和取得节点。
- Element节点表示文档中的所有HTML或XML元素,可以用来操作这些元素的内容和特性。
- 另外还有一些节点类型,分别表示文本内容、注释、文档类型、CDATA区域和文档片段。
访问DOM的操作在多数情况下都很直观,不过在处理