DOM是针对HTML和XML文档的一个API。DOM描绘了一个层次化的节点树,允许开发人员添加、移除和修改页面的某一部分。
节点层次
<!DOCTYPE html>
<html>
<head>
<title>sample page</title>
</head>
<body>
<p>Hello World!</p>
</body>
</html>
文档节点(Document node)是每个文档的根节点(在浏览器中对应的是document),在这个例子中,文档节点有两个子节点,一个是<!DOCTYPE html>
(DocumentTyep类型),另一个是<html>
(Element类型)。文档元素是文档最外层的元素,文档中的其他所有元素都是包含在文档元素中。每个文档只能有一个文档元素,在HTML页面中,文档元素始终是<html>
元素。
Node类型
JavaScript中,所有节点类型都继承自Node类型,因此所有节点类型都共享着相同的基本属性和方法
每个节点都有一个nodeType属性,用于表明节点的类型.
确定节点类型
if (someNode.nodeType == Node.Element_NODE){
alert("Node is an element"); // 表明该节点是一个HTML元素
}
或者
if (someNode.nodeType == 1){
alert("Node is an element"); // 表明该节点是一个HTML元素
}
nodeName和nodeValue
如果node是HTML标签,则nodeName的值就是元素的标签名,对于非文档节点,返回null
if (someNode.nodeType == 1){
tagName = someNode.nodeName;
}
对于文档节点(HTML标签)来说, nodeValue返回null. 对于text, comment, 和 CDATA 节点来说, nodeValue返回该节点的文本内容. 对于 attribute 节点来说, 返回该属性的属性值.
节点之间的关系(node的一些属性):
node.childNodes
node.parentNode
node.previousSibling
node.nextSibling
node.firstChild
node.lastChild
操作节点(node的一些方法)
node.appendChild():返回新增的节点,这个返回的节点是存在于DOM树中的节点,可以通过上面的属性访问DOM树中的其他节点
var returnNode = parentNode.appendChild(newNode);
insertBefore(newNode, baseNode): 同样会返回一个新节点,同上
replaceChild(newNode, replacedNode): 同样会返回一个新节点,同上
removeChild(node):会返回一个节点,该节点不存在于DOM树中
cloneNode:
// 执行深复制,即复制节点及其整个子树
var returnNode = someNode.cloneNode(true);
// 执行浅复制,只复制节点本身
var returnNode = someNode.cloneNode(false);
Document类型
JavaScript通过Document类型表示文档。在浏览器中,document对象是HTMLDocument(继承自Document类型)的一个实例,表示整个HTML页面。document对象是window对象的一个属性,因此可以作为全局对象来访问
文档子节点
Document节点(浏览器中是document)的子节点可能是DocumentType(最多一个,例如<!DOCTYPE html>
)、Element(最多一个例如 <html>
)、ProcessingInstruction、Comment
document.documentElement 始终指向HTML页面中的文档元素(
<html>
)document.body直接返回对<body>元素的引用
document.doctype会返回对
<! DOCTYPE HTML>
的引用document.title返回文档标题的内容(只读)
document.childNodes会返回[
<! DOCTYPE HTML>
,html
] (如果存在DOCTYPE的话)
文档信息:(可以获取或者设置这些属性)
document.title
document.URL: 包含页面完整的URL(地址栏中的URL)
document.domain:
document.referrer: 链接到当前页面的那个页面的URL
查找元素
document.getElementById()
document.getElementsByTagName():返回HTMLCollection对象
document.getElementsByName():返回带有给定name特性的所有元素,返回HTMLCollection对象
文档写入:
document.write():原样写入传入的字符串
document.writeln(): 在字符串的末尾添加一个换行符
...
<body>
<p>the current date and time is:
<script>
document.write("<strong>" + (new Date().toString() + "</strong>");
</script>
</body.
在 页面呈现过程 中,通过上面的方式可以在指定位置输出内容
这两个方法如果在页面加载结束后被调用,那么输出内容会重写整个页面
Element类型
要访问元素的标签名,可以使用nodeName属性或者tagName属性,这两个属性会返回相同的值。注意在HTML中tagName会返回大写的标签名,在XML中返回值和代码中的标签一致。
<div id="myDiv"></div>
var div = document.getElementById("myDiv");
alert(div.tagName); // DIV
alert(div.tagName == div.nodeName); //true
alert(div.tagName.toLowerCase() == "div"); // true
HTML元素
可以直接访问和修改的属性:
id:
element.id
title:
element.title
className:
element.className
lang
dir: 文字方向
<div id="myDiv" class="bd" title="body text" lang="en" dir="ltr"></div>
var div = document.getElementById("myDiv");
alert(div.id);
alert(div.className);
alert(div.title);
alert(div.lang);
alert(div.dir);
取得特性: getAttribute
<div id="myDiv" class="bd" title="body text" lang="en" dir="ltr"></div>
var div = document.getElementById("myDiv");
alert(div.getAttribute("id"));
alert(div.getAttribute("class");
alert(div.getAttribute("title");
alert(div.getAttribute("lang");
alert(div.getAttribute("dir");
对于style、onclick这种特殊的特性,getAttribute()只能返回相应代码的字符串,因此在通过JavaScript以编程的方式操作DOM时,开发人员经常不使用getAttribute(),而是只使用对象的属性(div.id)
任何元素的所有特性,也都可以通过DOM元素本身的属性来访问。不过只有公认的特性才会以属性的形式添加到DOM对象中。 以下面的元素为例:
<div id="myDiv" align="left" my_special_attribute="hello!"></div>
由于id和align在html中是<div>的公认特性,因此在该元素的DOM对象中也将存在对应的属性。
alert(div.id); // "myDiv"
alert(div.my_special_attribute); // undefined
alert(div.align); //"left"
设置特性--setAttribute()
setAttribute()接受两个参数,要设置的特性名和值。如果特性已经存在,则会替换已有的值,如果特性不存在,则会创建该属性并设置相应的值。
<div id="myDiv" class="bd" title="body text" lang="en" dir="ltr"></div>
var div=document.getElementById("myDiv");
div.setAttribute("id","someOtherId");
移除属性--removeAttribute()
<div id="myDiv" class="bd" title="body text" lang="en" dir="ltr"></div>
var div=document.getElementById("myDiv");
div.removeAttribute("id");
attributes属性:使用不方便不经常使用,但是可以用来遍历元素特性
Element类型是使用attribute属性的唯一一个DOM节点类型。attribute属性中包含一个NamedNodeMap。元素的每个特性都有一个Attr节点表示,每个节点都保存在NamedNodeMap对象中。NamedNodeMap对象拥有下列方法。
getNamedItem(name):返回nodeName属性等于name的节点
removeNamedItem(name): 移除nodeName属性等于name的节点
setNamedItem(node): 向列表中添加节点,以节点的nodeName属性为索引
item(pos): 返回位于数字pos位置处的节点
var id = element.attributes.getNamedItem("id").nodeValue;
var id = element.attributes["id"].nodeValue;
使用不方便不经常使用,但是可以用来遍历元素特性
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;
pairs.push(attrName + " =\"" + attrValue + "\"");
. }
return pairs.join(' ');
}
创建元素
document.createElement()方法可以创建新元素,这个方法只接受一个参数,即要创建元素的标签名
var div = document.createElement("div");
div.id = "myDiv";
div.className = "box";
// 此时新元素还没有添加到文档树中,因此设置这些特性不会影响浏览器的显示
// 把新元素添加到文档树中:appendChild()、insertBefore()、replaceChild()
document.body.appendChild(div);
元素的子节点
如果想通过某个特定的标签名取得子节点或者后代节点,可以在元素上调用getElementsByTagName()方法
Text类型
文本节点由Text类型表示,包含的是可以按照字面解释的纯文本内容,但是不能包含HTML代码
重要的特点:
nodeType的值为3
nodeName为#text
nodeValue的值为节点包含的文本
parentNode是一个Element
没有(不支持)子节点
有一些操作节点中文本的方法:
appendData(text): 将text添加到末尾
deleteData(offset, count): 从offset指定的位置开始,删除count个字符
insertData(offset, text):
replaceData(offset, count, text)
splitText(offset): 从offset指定位置将当前文本节点分成两个 文本节点
...
如果在一个包含两个或多个文本节点的父元素上调用normalize()方法,则会将所有文本节点合并成一个节点,结果节点的nodeValue等于将合并前每个文本节点的nodeValue值拼接起来的值。
DocumentFragment类型:
在所有的节点类型中,只有DocumentFragment在文档中没有对应的标记。DOM规定文档片段是一种“轻量级”的文档,可以包含和控制节点,但不会像完整的文档那样占用额外的资源。
虽然不能把文档片段直接添加到文档中,但是可以将它作为一个仓库来使用,即在里面保存将来可能会添加到文档中的节点。
创建文档片段的方式:document.createDocumentFragment()
var fragment = document.createDocumentFragment();
var ul = document.getElementById('myList');
var li = null;
//位ul添加三个li
for(var i=0; i<3; i++){
li = document.createElement("li");
li.innetText = "Item" + (i+1);
fragment.appendChild(li);
}
ul.appendChild(fragment);
上面的例子是为ul元素添加3个列表项,如果逐个地添加列表项,将会导致浏览器频繁渲染新信息。为避免这个问题,可以使用frament,将所有列表项先添加到fragment中,最后将fragment添加到文档中。通过这种方式减少了浏览器的渲染次数。
DOM操作技术
动态脚本
插入外部文件
function loadScript(url){
var script = document.createElement('script');
script.type = "text/javascript";
script.src = url;
document.body.appendChild(script);
}
行内方式
var script = document.createElement('script');
script.type = "text/javascript";
script.appendChild(document.creatTextNode('function sayHi(){alert('hi');}'));
document.body.appendChild(script);
动态样式
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);
}
操作表格
HTML DOM为<table>
, <tbody>
, <tr>
元素添加了一些属性和方法,通过这些属性和方法,我们可以方便的对表格进行操作
使用NodeList
NodeList,NamedNodeMap和HTMLCollection都是动态的,每当文档结构发生变化时,它们都会得到更新。
一般来说,应该尽量减少访问NodeList的次数,因为每次访问NodeList,都会运行一次基于文档的查询,所以可以考虑将从NodeList中取出的值缓存起来。
总结
DOM操作往往是JavaScript程序中开销最大的部分,而因访问NodeList导致的问题为最多。NodeList对象都是“动态的”,这就意味着每次访问NodeList对象,都会运行一次查询。有鉴于此,最好的办法就是尽量减少DOM操作。
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。