DOM是document object model的缩写,即:文档对象模型。DOM将HTML和XML描述为一个具有层次结构的节点树。DOM作为一个接口,可以对HTML或者XML进行添加,删除和修改。
根据 DOM,HTML 文档中的每个成分都是一个节点。DOM 是这样规定的:
节点类型
JavaScript中总共有12种节点类型,这些节点类型全部继承自Node类型,因此所有节点类型都共享着相同的基本属性和方法。
节点类型有在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属性
每个节点都有一个nodeType属性,可以确定节点的类型。
<div id="test">div</div>
var test = document.getElementById("test");
if(test.nodeType == 1){
console.log("This node is an element");
}
nodeName属性
nodeName属性保存的是元素的标签名
<div id="test">div</div>
var test = document.getElementById("test");
if(test.nodeType == 1){
console.log(test.nodeName); //DIV
}
节点关系
节点的关系类似于家谱。
每个节点都有一个childNodes属性来表示子节点,其中保存着一个NodeList对象。NodeList 是一种类数组对象,用于保存一组有序的节点,可以通过位置或者item()方法来访问这些节点。
var firstChild = someNode.childNodes[0];
var secondChild = someNode.childNodes.item(2);
var length = someNode.childNodes.length;
请注意,虽然可以通过方括号语法来访问 NodeList 的值,而且这个对象也有 length 属性,但它并不是 Array 的实例。可以通过一个函数把它转换为数组
function convert2Array(nodes){
try{
return Array.prototype.slice.call(nodes,0);
}catch(ex){
var array = [];
for(var i=0;i<nodes.length;i++){
array[i] = nodes[i];
}
return array;
}
}
console.log(convert2Array({length:2,0:'first',1:'second'})); //["first", "second"]
还有一些其他关系
- parentNode 父节点
- previousSibling 该节点的前一个兄弟节点
- nextSibling 该节点的后一个兄弟节点
节点操作
appendChild()
该方法用于向childNodes列表末尾添加一个节点。返回值为新增的节点。比如:
<ul id="list">
<li>item1</li>
<li>item2</li>
</ul>
var list = document.getElementById("list");
var item = document.createElement("li");
item.appendChild(document.createTextNode("itme3"));
list.appendChild(item); //向ul中添加了一个li
如果传入到 appendChild() 中的节点已经是文档的一部分了,那结果就是将该节点从原来的位置转移到新位置。比如将item1与item2互换:
<ul id="list">
<li>item1</li>
<li>item2</li>
</ul>
var list = document.getElementById("list");
var item = list.childNodes[1]; //0为空白文本节点
list.appendChild(item);
有时候,空白文本几点会影响操作,可以写一个函数把空白文本节点删除。
function cleanWhitespace(element){
for(var i=0; i<element.childNodes.length; i++)
{
var node = element.childNodes[i];
if(node.nodeType == 3 && !/\S/.test(node.nodeValue))
{
node.parentNode.removeChild(node);
}
}
}
insertBefore()
这个方法接受两个参数:要插入的节点和作为参照的节点。插入节点后,被插入的节点会变成参照节点的前一个同胞节点(previousSibling),同时被方法返回。如果参照节点是null则insertBefore()与appendChild()执行相同的操作。比如在item1前面插入item0:
<ul id="list">
<li>item1</li>
<li>item2</li>
</ul>
var list = document.getElementById("list");
var item = document.createElement("li");
item.appendChild(document.createTextNode("item0"));
list.insertBefore(item,list.firstChild);
replaceChild()
replaceChild()方法接收两个参数:要插入的节点和要替换的节点。要替换的节点将由这个方法返回并从文档树中被移除,同时由要插入的节点占据其位置。
removeChild()
removeChild()方法为移除某个节点,参数为要移除的节点
cloneNode()
该方法用于复制调用该方法的节点,接收两个参数。
- 当参数为true时,复制该节点和该节点下的整个子节点树
- 当参数为false时,只复制当前节点本身
详细介绍可以参考上篇文章
normalize()
这个方法唯一的作用就是处理文档树中的文本节点。由于解析器的实现或DOM操作等原因,可能会出现文本节点不包含文本,或者接连出现两个文本节点的情况。当在某个节点上调用这个方法时,就会在该节点的后代节点中查找上述两种情况。如果找到了空文本节点,则删除它;如果找到相邻的文本节点,则将它们合并为一个文本节点。
Document类型
Document类型表示文档。而我们常用的document对象是HTMLDocument(继承自Document类型)的一个实例。document对象也是window对象的一个属性,因此可以将其作为全局的对象来访问。
document对象的常用属性
- documentElement属性用来获取页面中的
<html>元素 body属性可以直接获取页面中的
body元素var body = document.bodydoctype属性可以获取对
<!DOCTYPE>的引用console.log(document.doctype); //<!DOCTYPE html>title属性可以获取网页的
<title>元素//获取title var title = document.title; //设置title document.title = "new title";URL属性可以获取当前页面完整的url
- domain属性可以获取当前页面的域名
比如google首页:
document对象的常用方法
在开发过程中,经常需要取得摸个元素的引用,再执行某些操作。Document类型为此提供了两个方法。
getElementById()参数为元素的ID名
IE8 及较低版本不区分 ID 的大小写。如果页面中多个元素的 ID 值相同, getElementById() 只返回文档中第一次出现的元素getElementsByTagName()参数为元素的标签名,在HTML文档中该方法返回的是包含零个或多个元素的HTMLCollection对象。<img src="#1" name="user_name"> <img src="#2" name="user_age"> <img src="#3" name="user_sex"> //获取页面中所有的<img>元素 var images = document.getElementsByTagName("img"); //获取长度 console.log(images.length); //通过索引获取src console.log(images[0].src); //通过name获取src console.log(images["user_age"].src);getElementsByName()这个方法会返回带有给定name特性的所有元素。
特殊集合
除了属性和方法,document对象还有一些特殊的集合。这些集合都是 HTMLCollection 对象,为访问文档常用的部分提供了快捷方式。
| 集合 | 描述 |
|---|---|
| document.anchors | 包含文档中所有带 name 特性的 <a> 元素; |
| document.forms | 包含文档中所有的 <form> 元素 |
| document.images | 包含文档中所有的 <img> 元素 |
| document.links | 包含文档中所有带href特性的 <a> 元素 |
文档写入
write()和writeln()
这两个方法都会接受一个字符串参数。在页面被加载的过程中,可以使用这两个方法向页面中动态地加入内容。writeln()方法会在写入末尾添加一个换行符。
current date and time is :
<script type="text/javascript">
document.write("<p>" + (new Date()).toString() +"</p>")
</script>
Element类型
Element类型提供了对元素标签名、子节点及特性的访问。常见的属性为id,title,classname等
元素的创建
可以通过document.createElement()方法创建新元素。比如创建一个div元素
var div = document.createElement("div");
元素特性的相关操作
取得特性
getAttribute()方法来取得属性值。
<div id="my_test_div" class="div_class" title="div_title" align="left"></div>
var div = document.getElementById("my_test_div");
console.log(div.getAttribute("id")); //或者div.id
console.log(div.getAttribute("class"));//或者div.class
console.log(div.getAttribute("title"));//或者div.title
console.log(div.getAttribute("align"));//或者div.align
通过getAttribute()方法也可以取得自定义特性的值,以下面的元素为例:
<div id="my_test_div" my_special_attribute="hello!"></div>
这个元素包含一个名为 my_special_attribute 的自定义特性,它的值是 “hello!” 。可以像取得其他特性一样取得这个值:
var value = div.getAttribute("my_special_attribute");
设置特性
setAttribute()用于设置特性,这个方法接受两个参数:要设置的特性名和
值。如果特性已经存在,setAttribute()会以指定的值替换现有的值;如果特性不存在,setAttribute()则创建该属性并设置相应的值。
<div id="my_test_div" ></div>
var div = document.getElementById("my_test_div");
div.setAttribute("class","div_class");//或者div.class="div_class"
div.setAttribute("title","div_title");//或者div.title="div_title"
div.setAttribute("align","right");//或者div.align="right"
删除特性
removeAttribute(),这个方法用于彻底删除元素的特性。调用这个方法不仅会清除特性的值,而且也会从元素中完全删除特性。
元素子节点
元素子节点部分见节点关系部分。
Text类型
正如之前用到过的例子中,有时候需要向文档树中添加文本节点。
文本节点由Text类型表示,包含的是可以照字面解释的纯文本内容。纯文本中可以包含转义后的HTML字符,但不能包含HTML代码。
在默认情况下,每个可以包含内容的元素最多只能有一个文本节点,而且必须确实有内容存在。
<div></div>//没有内容,没有文本节点
<div> </div>//有空格,有一个文本节点
<div>hello world!</div>//有内容,有一个文本节点
可以获取文本节点的引用并利用nodeValue修改它。
var textNode = div.firstChild;
textNode.nodeValue = "this is a test";
如果没有文本节点,可以创建一个文本节点,并插入到元素中
var element = document.createElement("div");
element.className = "test";
var text = document.createTextNode("hello world");
element.appendChild(text);
document.body.appendChild(element);
DOM文档中存在相邻的同胞文本节点很容易导致混乱,因为分不清哪个文本节点表示哪个字符串。另外,DOM文档中出现相邻文本节点的情况也不在少数,于是就催生了一个能够将相邻文本节点合并的方法normalize()。
这个方法是由 Node类型定义的。如果在一个包含两个或多个文本节点的父元素上调用normalize()方法,则会将所有文本节点合并成一个节点,结果节点的nodeValue 等于将合并前每个文本节点的nodeValue值拼接起来的值。比如:
var element = document.createElement("div");
element.className = "message";
var textNode = document.createTextNode("Hello world!");
element.appendChild(textNode);
var anotherTextNode = document.createTextNode("Nico!");
element.appendChild(anotherTextNode);
document.body.appendChild(element);
console.log(element.childNodes.length); //2
element.normalize();
console.log(element.childNodes.length); //1
console.log(element.firstChild.nodeValue); // "Hello world!Nico!"
Text 类型提供了一个作用与normalize()相反的方法:splitText()。
这个方法会将一个文本节点分成两个文本节点,即按照指定的位置分割nodeValue值。原来的文本节点将包含从开始到指定位置之前的内容,新文本节点将包含剩下的文本。这个方法会返回一个新文本节点,该节点与原节点的parentNode相同。
var element = document.createElement("div");
element.className = "test";
var text = document.createTextNode("Nico&Sora");
element.appendChild(text);
document.body.appendChild(element);
var newNode = element.childNodes[0].splitText(4);
console.log(element.childNodes[0].nodeValue); //Nico
console.log(newNode.nodeValue); //&Sora
console.log(element.childNodes.length); //2