1. 概念
DOM是针对HTML 和XML 文档的一个API(应用程序编程接口)。DOM描绘了一个层次化的节点树,允许开发人员添加、移除和修改页面的某一部分。
DOM 可以将任何HTML 或XML 文档描绘成一个由多层节点构成的结构。节点分为几种不同的类型,每种类型分别表示文档中不同的信息及(或)标记。每个节点都拥有各自的特点、数据和方法,另外也与其他节点存在某种关系。节点之间的关系构成了层次,而所有页面标记则表现为一个以特定节点为根节点的树形结构。
注意:IE 中的所有DOM 对象都是以COM 对象的形式实现的。这意味着IE 中的DOM 对象与原生JavaScript 对象的行为或活动特点并不一致。
2. Node类型
2.1 概念
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 中无效 }
2.2 常用的Node方法
1、childNodes
每个节点都有一个childNodes 属性,其中保存着一个NodeList 对象。NodeList 是一种类数组对象,用于保存一组有序的节点,可以通过位置来访问这些节点。请注意,虽然可以通过方括号语法来访问NodeList 的值,而且这个对象也有length 属性,但它并不是Array 的实例。
var firstChild = someNode.childNodes[0];
var secondChild = someNode.childNodes.item(1);
var count = someNode.childNodes.length;
var next=someNode.nextSibling;
var previous=someNode.previousSibling;//获得同胞节点
2、appendChild
var input=document.createElement('input');
input.appendChild(newNode);// 相当于$("input").append(roomEle);
注意:如果传入到appendChild()中的节点已经是文档的一部分了,那结果就是将该节点从原来的位置转移到新位置。
3、insertBefore
//插入后成为第一个子节点
var returnedNode = someNode.insertBefore(newNode, someNode.firstChild);
注意:如果参照节点是null,则insertBefore()与appendChild()执行相同的操作。
4、replaceChild()
replaceChild()方法接受的两个参数是:要插入的节点和要替换的节点。插入一个节点时,该节点的所有关系指针都会从被它替换的节点复制过来。尽管从技术上讲,被替换的节点仍然还在文档中,但它在文档中已经没有了自己的位置。
//替换第一个子节点
var returnedNode = someNode.replaceChild(newNode, someNode.firstChild);
5、 removeChild()
//移除第一个子节点, 被移除的节点将成为方法的返回值。
var formerFirstChild = someNode.removeChild(someNode.firstChild);
6、cloneNode()
用于创建调用这个方法的节点的一个完全相同的副本。cloneNode()方法接受一个布尔值参数,表示是否执行深复制。在参数为true的情况下,执行深复制,也就是复制节点及其整个子节点树;在参数为false 的情况下,执行浅复制,即只复制节点本身。复制后返回的节点副本属于文档所有,但并没有为它指定父节点。cloneNode()方法不会复制添加到DOM 节点中的JavaScript 属性,例如事件处理程序等。这个方法只复制特性、(在明确指定的情况下也复制)子节点,其他一切都不会复制。相当于
var ele = $("#template").clone(true);
3. Document类型
3.1 常用document对象属性
var html = document.documentElement; //取得对<html>的引用
var body = document.body; //取得对<body>的引用
var originalTitle = document.title; //取得文档标题
var url = document.URL; //取得完整的URL
var domain = document.domain;//取得域名
var referrer = document.referrer; //取得来源页面的URL
3.2 document对象特殊集合
除了属性和方法,document 对象还有一些特殊的集合。这些集合都是HTMLCollection 对象,为访问文档常用的部分提供了快捷方式,包括:
document.anchors,包含文档中所有带name 特性的<a>元素;
document.forms,包含文档中所有的<form>元素,与document.getElementsByTagName("form")得到的结果相同;
document.images,包含文档中所有的<img>元素,与document.getElementsByTagName("img")得到的结果相同;
document.links,包含文档中所有带href 特性的<a>元素。
这些对象都可以通过.length来获得这些集合的长度,通过下标来获得相应的元素,如:
var id=document.links[0].id;//获得第一个<a>标签的id属性
var length=document.images.length;
3.3 document对象方法
getElementById() --返回对拥有指定 id 的第一个对象的引用。
getElementsByName()--返回带有指定名称的对象集合。
getElementsByTagName()--返回带有指定标签名的对象集合。
open()--打开一个流,以收集来自任何 document.write() 或 document.writeln() 方法的输出。
close()--关闭用 document.open() 方法打开的输出流,并显示选定的数据。
write()--向文档写 HTML 表达式 或 JavaScript 代码。
writeln()--等同于 write() 方法,不同的是在每个表达式之后写一个换行符。
3.4 write()和writeln()方法
使用write()和writeln()方法动态地包含外部资源,例如JavaScript文件等。在包含JavaScript文件时,必须注意不能像下面的例子那样直接包含字符串"</script>",因为这会导致该字符串被解释为脚本块的结束,它后面的代码将无法执行。
<html>
<head>
<title>document.write() Example 2</title>
</head>
<body>
<script type="text/javascript">
document.write("<script type=\"text/javascript\" src=\"file.js\">" +"</script>");
</script>
</body>
</html>
即使这个文件看起来没错,但字符串"</script>"将被解释为与外部的<script>标签匹配,结果文本");将会出现在页面中。为避免这个问题,只需加入转义字符\即可;解决方案如下:
<html>
<head>
<title>document.write() Example 3</title>
</head>
<body>
<script type="text/javascript">
document.write("<script type=\"text/javascript\" src=\"file.js\">" +"<\/script>");
</script>
</body>
</html>
4. Element类型
1、getAttribute()
var div = document.getElementById("myDiv");
alert(div.getAttribute("id")); //"myDiv"
alert(div.getAttribute("class")); //"bd"
通过getAttribute()方法也可以取得自定义特性,如
<div id="myDiv" my_special_attribute="hello!"></div>
var value = div.getAttribute("my_special_attribute");
特性的名称是不区分大小写的,即"ID"和"id"代表的都是同一个特性。另外也要注意,根据HTML5 规范,自定义特性应该加上data-前缀以便验证。
注意:公认Attribute(如id,class,name等)在该元素的DOM对象中也将存在对应的属性。不过,自定义特性my_special_attribute在Safari、Opera、Chrome 及Firefox 中是不存在的;但IE却会为自定义特性也创建属性,如下面的例子所示:
alert(div.id); //"myDiv"
alert(div.my_special_attribute); //undefined(IE 除外)
alert(div.align); //"left"
有两类特殊的特性,它们虽然有对应的属性名,但属性的值与通过getAttribute()返回的值并不相同。第一类特性就是style,用于通过CSS 为元素指定样式。在通过getAttribute()访问时,返回的style 特性值中包含的是CSS 文本,而通过属性来访问它则会返回一个对象。由于style 属性是用于以编程方式访问元素样式的,因此并没有直接映射到style 特性。第二类与众不同的特性是onclick 这样的事件处理程序。当在元素上使用时,onclick 特性中包含的是JavaScript 代码,如果通过getAttribute()访问,则会返回相应代码的字符串。而在访问onclick 属性时,则会返回一个JavaScript 函数(如果未在元素中指定相应特性,则返回null)。这是因为onclick 及其他事件处理程序属性本身就应该被赋予函数值。由于存在这些差别,在通过JavaScript 以编程方式操DOM时,开发人员经常不使用getAttribute(),而是只使用对象的属性。只有在取得自定义特性值的情况下,才会使用getAttribute()方法。
2、setAttribute()
div.setAttribute("id", "someOtherId");
div.setAttribute("class", "ft");
等价于
div.id = "someOtherId";
div.class= "left";
等价于
$(“input”).attr("id","list");
注意:不能通过对象的属性来设置自定义的Attribute ,如
div.mycolor = "red";
alert(div.getAttribute("mycolor")); //null(IE 除外)
3、document.createElement()
在IE 中可以以另一种方式使用createElement(),即为这个方法传入完整的元素标签,也可以包含属性,如下面的例子所示。
var div = document.createElement("<div id=\"myNewDiv\" class=\"box\"></div >");
这种方式有助于避开在IE7 及更早版本中动态创建元素的某些问题。
5. Text类型
1、文本节点由Text 类型表示,包含的是可以照字面解释的纯文本内容。纯文本中可以包含转义后的HTML字符,但不能包含HTML 代码。即:小于号、大于号或引号都会像下面的例子一样被转义。
//输出结果是"Some <strong>other</strong> message"
div.firstChild.nodeValue = "Some <strong>other</strong> message";
可以通过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 element = document.createElement("div");
element.className = "message";
var textNode = document.createTextNode("Hello world!");
element.appendChild(textNode);
var anotherTextNode = document.createTextNode("Yippee!");
element.appendChild(anotherTextNode);
document.body.appendChild(element);
如果两个文本节点是相邻的同胞节点,那么这两个节点中的文本就会连起来显示,中间不会有空格。
2、normalize()
如果在一个包含两个或多个文本节点的父元素上调用normalize()方法,则会将所有文本节点合并成一个节点,结果节点的nodeValue 等于将合并前每个文本节点的nodeValue 值拼接起来的值。例如:
var element = document.createElement("div");
element.className = "message";
var textNode = document.createTextNode("Hello world!");
element.appendChild(textNode);
var anotherTextNode = document.createTextNode("Yippee!");
element.appendChild(anotherTextNode);
document.body.appendChild(element);
alert(element.childNodes.length); //2
element.normalize();
alert(element.childNodes.length); //1
alert(element.firstChild.nodeValue); // "Hello world!Yippee!"
3、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); //