DOM1级
DOM
DOM(文档对象模型)是针对HTML和XML文档的一个API。DOM描绘了一个层次化的节点树,允许开发人员添加、移除和修改页面的某一部分。
DOM可以将任何HTML或XML文档描绘成一个由多层节点构成的结构。节点分为几种不同的类型,每种类型分别表示文档中不同的信息及(或)标记。
在HTML页面中,文档元素始终都是元素。在XML中,没有预定义的元素,因此任何元素都可能成为文档元素。
1 Node类型
DOM1级定义了一个node接口,该接口将由DOM中的所有节点类型实现。这个Node接口在JS中是作为Node类型实现的;除了IE之外,其他所有浏览器都可以访问到这个类型。JS中的所有节点类型都继承自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);
- NODEDOCUMENT_NODE(9);
- NODE.DOCUMENT_TYPE_NODE(10);
- NODE.DOCUMENT_FRAGMENT_NODE(11);
- NODE.NOTATION_NODE(12);
为了确保跨浏览器兼容,最好还是将nodeType属性与数字值进行比较,如下所示:
if(someNode.nodeType == 1) {
alert("Node is an element.");
}
1.1 nodeName和nodeValue属性
对于元素节点,nodeName中保存的始终都是元素的标签名,而nodeValue的值则始终未null。
1.2 节点关系
每个节点都有一个childNodes属性,其中保存着一个NodeList对象。NodeList是一种类数组对象,用于保存一组有序的节点,可以通过位置来访问这些节点。请注意,虽然可以通过方括号语法来访问NodeList的值,而且这个对象也有length属性,但它并不是Array的实例。DOM结构的变化能够自动反映在NodeList对象中。
可以通过方括号,也可以使用item()方法。
var firstChild = someNode.childNodes[0];
var secondChild = someNode.item(1);
var count = someNode.childNodes.length;
对arguments对象可以使用Array.prototype.slice()方法可以将其转换为数组。而采用同样地方法,也能将NodeList对象转换为数组。例如:
var arrayOfNodes = Array.prototype.slice.call(NodeList, 0);
为了实现兼容性,可以这样:
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(i);
}
}
return array;
}
每个节点都有一个parentNode属性,该属性指向文档树中的父节点。包含在childNodes列表中的所有节点都具有相同的父节点。因此它们的parentNode属性都指向同一个节点。此外,包含在childNodes列表中的每个节点都是同胞节点。通过使用列表中每个节点的previousSibling和nextSibling属性,可以访问同一列表中的其他节点。
父节点与其第一个和最后一个子节点之间也存在特殊关系。父节点的firstChild和lastChild属性分别指向其childNodes列表中的第一个和最后一个节点。
下图形象地展示了上述关系:
hasChildNodes()也是一个非常有用的方法,这个方法在节点包含一或多个子节点的情况下返回true。
所有节点都有的最后一个属性是ownerDocument,该属性指向表示整个文档的文档节点。
1.3 操作节点
- appendChild(),用于向childNodes列表的末尾添加一个点。
- insertBefore(),用于把节点放在childNodes列表中某个特定的位置上,而不是放在末尾。这个方法接受两个参数:要插入的节点和作为参照的节点。如果参照节点是null,则insertBefore()和appendChild()执行相同的操作。
- replaceChild(),用于替换节点。这个方法接受两个参数,要插入的节点和要替换的节点。要替换的节点将由这个方法返回并从文档树中被移除。同时由要插入的节点占据其位置。
- removeChild(),用于移除节点。
这四个方法必须先取得父节点(使用parent属性)。在不支持子节点的节点上调用这些方法,将会导致错误发生。
1.4 其他方法
- cloneNode(),用于创建调用这个方法的节点的一个完全相同的副本。cloneNode()方法接受一个布尔值参数,表示是否执行深复制。在参数为true的情况下,执行深复制,也就是复制节点及其整个子节点树;在参数为false的情况下,执行浅复制,即只复制节点本身。
- normalize(),用于处理文档树中的文本节点。当在某个节点上调用这个方法时,就会在该节点的后代节点中查找下述两种情况:如果找到了空文本节点,则删除它;如果找到相邻的文本节点,则将它们合并为一个文本节点。
2 Document类型
在浏览器中,document对象是HTMLDocument(继承自Document类型)的一个实例,表示整个HTML页面。而且,document对象是window对象的一个属性,因此可以将其作为全局对象来访问。
其子节点可能是一个DocumentType(最多一个)、Element(最多一个)、ProcessingInstruction或comment。
2.1 文档的子节点
虽然DOM标准规定Document节点的子节点可以是DocumentType、Element、ProcessingInstruction或comment,但还有两个内置的访问其子节点的快捷方式。第一个就是documentElement属性,该属性始终指向HTML页面中的<html>元素。另一个就是通过childNodes列表访问文档元素。例如:
<html>
<body>
</body>
</html>
这个页面在经过浏览器解析后,其文档中只包含一个子节点,即<html>元素。可以通过documentElement或childNodes列表来访问这个元素。例如:
var html = document.documentElement; //取得对<html>的引用
alert(html === document.childNodes[0]); //true
alert(html === document.firstChild); //true
作为HTMLDocument的实例,document对象还有一个body属性,直接指向<body>元素。例如:
var body = document.body
2.2 文档信息
作为HTMLDocument的一个实例,document对象还有一些标准的Document对象所没有的属性。
- document.title,包含着<title>元素中的文本
- document.URL,包含页面完整的URL
- document.domain,包含页面的域名,浏览器对domain属性还有一个限制,即如果域名一开始是“松散的”(loose),那么不能将它再设置为“紧绷的”(tight)。
- document.referer,保存着链接到当前页面的那个页面的URL
2.3 查找元素
取得元素的操作可以使用document对象的几个方法来完成。其中,Document类型为此提供了两个方法:
- getElementById(),接收一个参数:要取得的元素的ID
- getElementsByTagName(),接收一个参数:即要取得元素的标签名,而返回的是包含零或多个元素的NodeList。在HTML文档中,这个方法会返回一个HTMLCollection对象,作为一个“动态”集合,该对象与NodeList非常类似。例如:
var images = document.getElementsByTagName("img");
alert(images.length);
alert(images[0].src);
alert(images.item(0).src);
HTMLCollection对象还有一个方法,叫做namedItem(),使用这个方法可以通过元素的name特性取得集合中的项。例如:
<img src="myImage.gif" name="myImage">
var myImage = images.namedItem("myImage");
在提供索引访问项的基础上,HTMLCollection还支持按名称访问项,这就为我们取得实际想要的元素提供了便利。而且,对命名的项也可以使用方括号语法来访问。例如:
var myImage = images["myImage"];
对HTMLCollection而言,我们可以向方括号中传入数值或字符串形式的索引值。在后台,对数值索引就会调用item(),而对字符串索引就会调用namedItem()。
第三个方法,也是只有HTMLDocumet类型才有的方法,是getElementsByNames()。
2.4 特殊集合
除了属性和方法,document对象还有一些特殊的集合。这些集合都是HTMLCollection对象,为访问文档常用的部分提供了快捷方式。
- document.anchors,包含文档中所有带name特性的<a>元素
- document.forms,包含文档中所有的<form>元素
- document.images,包含文档中所有的<img>元素
- document.links,包含文档中所有带href特性的<a>元素
2.5 文档写入
有一个document对象的功能以及存在很多年了,那就是将输出流写入到网页中的能力。
- write(),接受一个字符串参数,原样写入
document.write("<strong>" + (new Date()).toString() + "</strong>");
- writeIn(),和write()基本类似,只不过会在字符串的末尾添加一个换行符(\n)。
如果在文档加载结束后再调用document.write(),那么输出的内容将会重写整个页面。
方法open()和close()分别用于打开和关闭网页的输出流。
3 Element类型
要访问元素的标签名,可以使用nodeName属性,也可以使用tagName属性;这两个属性会返回相同的值(使用后者主要是为了清晰可见)。
3.1 HTML元素
所有HTML元素都由HTMLElement类型表示,不是直接通过这个类型,也是通过它的子类型来表示。HTMLElement类型直接继承自Element并添加了一些属性。添加的这些属性分别对应于每个HTML元素中都存在的下列标准特性。
- id,元素在文档中的唯一标识符
- title,有关元素的附加说明信息,一般通过工具提示条显示出来
- lang,元素内容的语言代码,很少使用
- dir,语言的方向,值为“ltr”(left to right,从左至右)或“rtl”(right to left,从右至左),也很少使用
- className,与元素的class特性对应,即为元素指定的CSS类。没有将这个属性命名为class,是因为class是ES的保留字。
3.2 取得特性、设置特性和移除特性
操作特性的DOM方法主要有三个,分别是getAttribute(),setAttribute(),removeAttribute()。
通过getAttribute()方法也可以取得自定义特性(即标准HTML中语言中没有的特性)的值,以下面的元素为例:
<div id="myDiv" my_special_attribute="hello!"></div>
var value = div.getAttribute("my_special_attribute");
根据HTML5规范,自定义特性应该加上data-前缀以便验证。
只有在取得自定义特性值的情况下,才会使用getAttribute()方法。
3.3 attributes属性
Element类型是使用attributes属性的唯一一个DOM节点类型。attributes属性中包含一个NamedNodeMap,与NodeList类似,也是一个动态的集合。元素的每一个特性都由一个Attr节点表示,每个节点都保存在NamedNodeMap对象中。NamedNodeMap对象拥有下列方法:
- getNamedItem(name):返回NodeName属性等于name的节点
- removeNamedItem(name):从列表中移除nodeName属性等于name的节点
- setNamedItem(node):向列表中添加节点,以节点的nodeName属性为索引
- item(pos):返回位于数字pos位置处的节点
var div1 = document.getElementById("div1");
var type = document.createAttribute("class");
type.nodeValue = "class1";
div1.attributes.setNamedItem(type);
以上是setNamedItem()的方法。
一般来说,attributes的方法不够方便,更多会使用getAttribute()、removeAttribute()、setAttribute()方法。
不过,如果想要遍历元素的属性,attributes属性倒是可以派上用场。例如:
var div1 = document.getElementById("div1");
function outputAttributes(element) {
var pairs = new Array(),
attrName,
attrValue,
i,
len;
for(i = 0, len = element.attributes.length; i < len; i++) {
attrName = element.attributes[i].nodeName;
attrValue = element.attributes[i].nodeValue;
if(element.attributes[i].specified) {
pairs.push(attrName + "=\"" + attrValue + "\"");
}
}
return pairs.join(" ");
}
alert(outputAttributes(div1)); //'id="div1" class="div2"'
3.4 创建元素
使用document.createElement()方法可以创建新元素。这个方法只接受一个参数,即要创建元素的标签名。
3.5 元素的子节点
元素可以有任意数目的子节点和后代节点,因为元素可以是其他元素的子节点。元素的hildNodes属性中包含了它的所有子节点,这些子节点有可能是元素、文本节点、注释或处理指令。例如:
<ul id="myList">
<li>Item1</li>
<li>Item2</li>
<li>Item3</li>
</ul>
如果是IE来解析这段代码,那么<ul>会有三个子节点,分别是3个<li>元素。但如果是在其他浏览器中,<ul>元素都会有七个元素,包括三个<li>元素和四个文本节点。如果像下面这样将元素间的空白符删除,那么所有浏览器都会返回相同数目的子节点。
<ul id="myList"><li>Item1</li><li>Item2</li><li>Item3</li></ul>
4 文本类型
文本节点由Text类型表示,包含的是可以照字面解释的纯文本内容。纯文本中可以包含转义后的HTML字符,但不能包含HTML代码。
可以通过nodeValue属性或data属性访问Text节点中包含的文本,这两个属性中包含的值相同。
可以通过下列方法操作节点中的文本:
- appendData(text):将text添加到节点的末尾。
- deleteData(offset, count):从offset指定的位置开始删除count个字符。
- insertData(offset, text):在offset指定的位置插入text。
- replaceData(offset, count, text):用text替换从offset指定的位置开始到offset+count为止处的文本。
- splitData(offset):从offset指定的位置将当前文本, 节点分成两个文本节点。
- substringData(offset, count):提取从offset指定的位置开始到offset+count为止处的字符串。
文本节点还有一个length属性,保存着节点中字符的数目。而且,nodeValue.length和data.length中也保存着同样的值。
在默认情况下,每个可以包含内容的元素最多只能有一个文本节点,而且必须确实有内容存在。
4.1 创建文本节点
可以使用document.createTextNode()创建新文本节点,这个方法接受一个参数——要插入节点中的文本。
4.2 规范化文本节点
如果在一个包含两个或多个文本节点的父元素上调用normalize()方法,则会将所有文本节点合并成一个节点,结果节点的nodeValue等于将合并前每个文本节点的nodeValue值拼接起来的值。
4.3 分割文本节点
Text类型提供了一个作用与normalize()相反的方法:splitText()。这个方法将一个文本节点分成两个文本节点,即按照指定的位置分割nodeValue值。原来的文本节点将包含从开始到指定位置之前的内容,新文本节点将包含剩下的文本。
5 Comment类型
Comment类型与Text类型继承自相同的基类,因此它拥有除splitText()之外的所有字符串操作方法。与Text类型相似,也可以通过nodeValue或data属性来取得注释的内容。
可以使用document.createComment()并为其传递注释文本创建注释节点。
6 DocumentType类型
DocumentType包含着与文档的doctype有关的所有信息。
在DOM1级中,DocumentType对象不能动态创建,而只能通过解析文档代码的方式来创建。支持它的浏览器会把DocumentType对象保存在document.doctype中。DOM1级描述了DocumentType对象的三个属性:name、entities和notations。其中,name属性表示文档类型的名称;entities是由文档类型描述的实体的NamedNodeMap对象;notations是由文档类型描述的符号的NamedNodeMap对象;只有name属性是有用的。这个属性保存的是文档类型的名称,也就是出现在<DOCTYPE之后的文本。
7 DocumentFragment类型
在所有节点类型中,只有DocumentFragment在文档中没有对应的标记。DOM规定文档片段(document fragment)是一种“轻量级”的文档,可以包含和控制节点,但不会像完整的文档那样占用额外的资源。
虽然不能把文档片段直接添加到文档中,但可以将它作为一个“仓库”来使用,即可以在里面保存将来可能会添加到文档中的节点。要创建文档片段,可以使用document.createDocumentFragment()方法,如下所示:
var fragment = document.createDocumentFragment();
如果将文档中的节点添加到文档片段中,就会从文档树中移除该节点,也不会从浏览器中再看到该节点。添加到文档片段中的新节点同样也不属于文档树。可以通过appendChild()或insertBefore()将文档片段中内容添加到文档中。在将文档片段作为参数传递给这两个方法时,实际上只会将文档片段的所有子节点添加到相应位置上;文档片段本身永远不会成为文档树中的一部分。例如:
<ul id="myList"></ul>
假设我们想为这个<ul>元素添加3个列表项。如果逐个地添加列表项,将会导致浏览器反复渲染(呈现)新信息。为避免这个问题,可以使用一个文档片段来保存创建的列表项,然后再一次性将它们添加到文档中。
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);
8 Attr类型
元素的特性在DOM中以Attr类型来表示。
Attr对象有三个属性:name、value和specified。其中,name是特性名称(与nodeName的值相同),value是特性的值(与nodeValue的值相同),而specified是一个布尔值,用以区别特性是在代码中指定的,还是默认的。
使用document.createAttribute()并传入特性的名称可以创建新的特性节点。例如:
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"
9 DOM操作技术
9.1 动态脚本
跟操作HTML元素一样,创建动态脚本也有两种方式:插入外部文件和直接插入Javascript代码。
9.2 动态样式
与动态脚本类似,所谓动态样式是指在页面加载时不存在的样式;动态样式是在页面加载完成后动态添加到页面中的。
9.3 操作表格
9.4 使用nodeList
上一篇: JavaScript和DOM
下一篇: 文档对象模型(DOM)