DOM是语言中立的API,用于访问和操作HTML和XML文档。DOM1级将HTML和XML文档形象地看做一个层次化的节点数,
可以使用JavaScript来操作这个节点树,进而改变底层文档的外观和结构。
DOM由各种节点构成,简要总结如下:
1)最基本的节点类型是Node,用于抽象地表示文档中一个独立的部分;所有其他类型都继承自Node.
2)Document类型表示整个文档,是一组分层节点的根节点。在JavaScript中,document对象是Document的一个实例。
使用document对象,有很多种方式可以查询和取得节点。
3)Element节点表示文档中的所有HTML或XML元素,可以用来操作这些元素的内容和特性。
4)另外还有一些节点类型,分别表示文本内容、注释、文档类型、CDATA区域和文档片段。
访问DOM的操作在多数情况下都很直观,不过在处理<script>和<style>元素时还是存在一些复杂性。
由于这两个元素分别包含脚本和样式信息,因此浏览器通常会将它们与其他元素区别对待。
这些区别导致了在针对这些元素适用innerHTML时,以及在创建新元素时的一些问题。
理解DOM的关键,就是理解DOM对性能的影响。DOM操作往往是JavaScript程序中开销最大的部分,
而因当问NodeList导致的问题为最多。NodeList对象都是“动态的”,这就意味着每次访问NodeList对象,
都会运行一次查询。有鉴于此,最好的办法就是尽量减少DOM操作。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>文档对象模型(DOM)</title>
</head>
<body>
<div id="div1">
<p name="p1">段落1</p>
<p>段落2</p>
<p>段落3</p>
<img src="" name="myImage"/>
<img src="" name="myImage"/>
</div>
<div id="myDiv"><!--a comment --></div>
<ul id="myList"></ul>
<script src="l10.js"></script>
</body>
</html>
/*
* 文档对象模型(DOM)
*/
function cl(x){
console.log(x);
}
/**
* 10.1 节点层次
*/
//10.1.1 Node类型 nodeType
Node.ELEMENT_NODE;//1
Node.ATTRIBUTE_NODE;//2
Node.TEXT_NODE;//3
Node.CDATA_SECTION_NODE;//4
Node.ENTITY_SECTION_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
//判断节点类型
var div1Element=document.getElementById("div1");
if(div1Element.nodeType==1){
cl("div1是一个元素");
}
//10.1.1.1 nodeName和nodeValue属性
//对于元素节点,nodeName中保存的始终是元素的标签名,而nodeValue的值则始终为null
cl(div1Element.nodeName); //=>DIV
cl(div1Element.nodeValue); //=>null
//10.1.1.2 节点关系
//childNodes——子节点集合(NodeList)
var firstChild=div1Element.childNodes[0];
var secondChild=div1Element.childNodes.item(1);
var count=div1Element.childNodes.length;
cl(count);//=>7 所有子节点数量
//将NodeList对象转换为数组
function convertToArray(nodes){
var array=null;
try{
array=Array.prototype.slice.call(nodes,0);//针对IE9及以上浏览器
}catch(ex){//针对IE8及以下浏览器
array=new Array();
for(var i= 0,len=nodes.length;i<len;i++){
array.push(nodes[i]);
}
}
return array;
}
//parentNode——父节点
//previousSibling——上一个兄弟节点
//nextSibling——下一个兄弟节点
//firstChild——第一个子节点
//lastChild——最后一个子节点
//hasChildNodes()——是否包含子节点
//ownerDocument——整个文档的文档节点
cl(div1Element.ownerDocument); //=>Document
//10.1.1.3 操作节点:操作某个节点的子节点
//appendChild(newNode)——向childNodes列表的末尾添加一个节点
var returnedNode=div1Element.appendChild(div1Element.firstChild);
cl(returnedNode==div1Element.lastChild);//=>true
//insertBefore(newNode,someNode)——插入一个兄弟节点
//replaceChild(newNode,oldNode)——替换原来的一个子节点
//removeChild(oldNode)——移除一个子节点
//10.1.1.4 操作节点的其他方法
//cloneNode()——用于创建调用这个方法的节点的一个完全相同的副本
var deepList=div1Element.cloneNode(true);
cl(deepList.childNodes.length); //=>7
var shallowList=div1Element.cloneNode(false);
cl(shallowList.childNodes.length); //=>0
//normalize()——处理文档树中的文本节点
//10.1.2 Document类型
//document对象是HTMLDocument的一个实例,表示整个HTML页面。
//Document节点具有下列特征:
//nodeType的值为9;
//nodeName的值为"#document";
//nodeValue的值为null;
//parentNode的值为null;
//ownerDocument的值为null
//其子节点可能是一个DocumentType(最多一个)、Element(最多一个)、ProcessingInstruction或Comment。
//10.1.2.1 文档的子节点
var html=document.documentElement;
cl(html.nodeName); //=>HTML
var body=document.body;
cl(body.nodeName); //=>BODY
var doctype=document.doctype;
cl(doctype===document.childNodes[0]);//=>true 在IE9+及Firefox
//10.1.2.2 文档信息
//取得文档标题
var originalTitle=document.title;
//设置文档标题
document.title="文档对象模型";
//取得完整的URL
cl(document.URL);
//取得域名
cl(document.domain);
//取得来源页面的URL
cl(document.referrer);//=>null
//10.1.2.3 查找元素
//getElementById()
//getElementsByTagName()
//getElementsByName()
var images=document.getElementsByTagName("img");
//var myImage=images.namedItem("myImage");
var myImage=images["myImage"];
//10.1.2.4 特殊集合
document.anchors;//所有带name特性的<a>元素
document.links;//所有带href特性的<a>元素
document.images;//所有<img>元素
document.forms;//所有<form>元素
//10.1.2.5 DOM一致性检测
var hasXMLDom=document.implementation.hasFeature("XML","1.0");
cl(hasXMLDom);//=>true
//10.1.2.6 文档写入
//write()、writeln()——写入文本到输出流
// open()和close()——打开和关闭网页的输出流
//10.1.3 Element类型
//Element节点具有以下特征:
//nodeType的值为1;
//nodeName的值为元素的标签名
//nodeValue的值为null;
//parentNode可能是Document或Element
//其子节点可能是Element、Text、Comment、processingInstruction、CDATASection或EntityReference
cl(div1Element.tagName==div1Element.nodeName);//=>true
//10.1.3.1 HTML元素
//每个HTML元素中存在的标准特性:id,title,className,lang(很少用),dir(很少用)
//10.1.3.2 取得特性 getAttribute()
//10.1.3.3 设置特性 setAttribute()、removeAttribute()
//10.1.3.4 attributes属性
//迭代元素的每一个特性,然后将它们构造成name="value"这样的字符串格式
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){//针对IE及以下
pairs.push(attrName+"=\""+attrValue+"\"");
}
}
return pairs.join(" ");
}
//10.1.3.5 创建元素
//document.createElement()
var div=document.createElement("div");
div.id="myNewDiv";
div.className="box";
document.body.appendChild(div);
//10.1.3.6 元素的子节点
//10.1.4 Text类型
//Text节点具有以下特征:nodeType的值为3,nodeName的值为"#text",nodeValue或data的值为节点所包含的文本,parentNode是一个Element,没有子节点
//使用下列方法可以操作节点中的文本:
//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为止处的字符串。
//10.1.4.1 创建文本节点
//document.createTextNode()
var element=document.createElement("div");
element.className="message";
var textNode=document.createTextNode("Hello world!");
element.appendChild(textNode);
var anotherTextNode=document.createTextNode("Jason,see you later.");
element.appendChild(anotherTextNode);
document.body.appendChild(element);
//10.1.4.2 规范化文本节点 normalize():合并文本节点
cl(element.childNodes.length); //=>2
element.normalize();
cl(element.childNodes.length); //=>1
cl(element.firstChild.nodeValue);//=>Hello world!Jason,see you later.
//10.1.4.3 分割文本节点 splitText()
var newNode=element.firstChild.splitText(12);
cl(element.firstChild.nodeValue); //=>"Hello world!"
cl(newNode.nodeValue); //=>"Jason,see you later."
cl(element.childNodes.length);//=>2
//10.1.5 Comment类型
//注释在DOM中是通过Comment类型来标示的。Comment节点具有下列特征:
//nodeType的值为8,nodeName的值为"#comment",nodeValue的值是注释的内容;parentNode可能是Document或Element;没有子节点
var commentDiv=document.getElementById("myDiv");
var comment=commentDiv.firstChild;
cl(comment.data);//=>a comment
//10.1.6 CDATASection类型
//CDATASection类型只针对基于XML的文档,标示的是CDATA区域。CDATASection节点具有下列特征:
//nodeType的值为4;nodeName的值为"#cdata-section";nodeValue的值是CDATA区域中的内容;parentNode可能是Document或Element;没有子节点
//10.1.7 DocumentType类型
//DocumentType包含着与文档doctype有关的所有信息,具有下列特征:
//nodeType的值为10;nodeName的值为doctype的名称;nodeValue的值为null;parentNode是Document;没有子节点
//10.1.8 DocumentFragment类型
//DOM规定文档片段(document fragment)是一种"轻量级"的文档,可以包含和控制节点,但不会像完整的文档那样占用额外的资源
//DocumentFragment节点具有下列特征:
//nodeType的值为11;
//nodeName的值为"#document-fragment";
//nodeValue的值为null;
//parentNode的值为null;
//子节点可以是Element、ProcessingInstruction、Comment、Text、CDATASection或EntityReference
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);
//10.1.9 Attr类型
//特性节点具有下列特征:
//nodeType的值为11;nodeName的值是特性的名称;nodeValue的值是特性的值;parentNode的值为null;在HTML中没有子节点,在XML中子节点可以是Text或EntityReference
//Attr对象有3个属性:name、value和specified
/**
* 10.2 DOM操作技术
*/
//10.2.1 动态脚本
//创建动态脚本有两种方法:插入外部文件和直接插入JavaScript代码
//<script type="text/javascript" src="client.js"></script>
function loadScript(url){
var script=document.createElement("script");
script.type="text/javascript";
script.src=url;
document.body.appendChild(script);
}
//loadScript("client.js");
//10.2.2 动态样式
//动态样式实在页面加载完成后动态添加到页面中的。
//<link rel="stylesheet" type="text/css" href="styles.css">
function loadStyles(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);
}
//loadStyles("style.css");
//10.2.3 操作表格
/**
* 创建下列表格
* <table border="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>
*/
//创建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);
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 2,1"));
//创建第二行
tbody.insertRow(1);
tbody.rows[1].insertCell(0);
tbody.rows[1].cells[0].appendChild(document.createTextNode("Cell 1,2"));
tbody.rows[1].insertCell(1);
tbody.rows[1].cells[1].appendChild(document.createTextNode("Cell 2,2"));
//将表格添加到文档主体中
document.body.appendChild(table);
//10.2.4 使用NodeList
//从本质上说,所有Nodelist对象都是在访问DOM文档时实时运行的查询
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);
}
//一般来说,应该尽量减少访问NodeList的次数。因为每次访问NodeList,都会运行一次基于文档的查询。