10.1 节点层次
DOM可以将任何HTML和XML文档描绘成一个由多层节点构成的结构,文档节点只有一个子节点,即<html>元素,称之为文档元素,文档元素是最外层元素,文档中的其他所有元素都包含在文档元素中,每个文档只能有一个文档元素。
10.1.1 Node类型
所有节点类型都继承自Node类型,因此所有节点类型都共享相同的基本属性和方法。总共有12种节点类型,每个节点都有一个nodeType属性,用于表明节点类型,是一个常量。nodeName和nodeValue两个属性中保存着节点的信息。
每个节点都有一个childNodes属性。其中保存着一个NodeList对象,用于保存一组有序的节点,它实际上是基于DOM结构动态执行查询的结果,因此DOM结构变化能够自动反应在NodeList对象中。
// 将NodeList对象转为数组
function toArray(nodes) {
var arr = [];
try {
arr = Array.prototype.slice.call(nodes,0);
}catch(ex){
// IE8以前需要枚举所有成员
for(var i=0,len=nodes.length;i<len;i++){
arr.push(nodes[i]);
}
}
return arr;
}
每个节点都有parentNode属性,指向文档树中的父节点。同一个childNodes列表中的所有节点都具有相同的父节点。因此它们的parentNode属性都指向同一个节点。包含在childNodes列表中的每个节点相互之间都是同胞节点,previousSibling表示前一节点,nextSibling表示后一节点。父节点的firstChild表示childNodes列表中的第一个节点,lastChild表示childNodes列表中的最后一个节点。也可以用方括号表示。即firstChild等于childNodes[0],lastChild等于childNodes[childNodes.length-1]。节点的hasChildNodes()方法在节点包含一个或多个子节点时返回true。所有节点都有ownerDocument属性,指向表示整个文档的文档节点。
appendChild()方法用于向childNodes列表末尾添加一个节点,并返回新添加的节点。如果传给该方法的节点已经是文档中的一部分,那结果就是将该节点从原来的位置转移到新位置。
insertBefore()方法用于将节点插入childNodes列表中特定的位置,这个方法接收两个参数,要插入的节点和作为参照的节点,插入后返回被插入的节点,参照节点为null时与appendChild()方法相同。
replaceChild()方法用于替换childNodes列表中的节点,这个方法接收两个参数,要插入的节点和要替换的节点,要替换的节点会从文档树中被移除,并被这个方法返回。
removeChild()方法用于移除childNodes列表中的节点,接收一个参数,即被移除的节点,被移除的节点将作为返回值返回。
cloneNode()方法用于创建调用和这个方法的节点的一个完全相同的副本,始终接收一个布尔值参数,表示是否执行深度复制,即复制节点及其整个子节点树,在参数为false时,只执行浅复制,即只复制节点本身。
10.1.2 Doucment类型
最常见的Doucment类型就是作为HTMLDocument实例的document对象。
document.documentElement属性指向<html>元素;
document.body属性指向<body>元素;
document.title属性包含<title>元素中的文本,可以通过这个取得当前页面的标题,也可以修改并反映在浏览器的标题中,但修改其值不会改变<title>元素;
document.URL属性保存页面完整的URL,即地址栏中显示的URL。
document.domain属性中包含当前页面的域名。
document.referrer属性中包含页面来源的URL。
查找元素的方法:
document.getElementById(),按照元素ID进行查找;
document.getElementByTagName(),按照元素标签名进行查找,返回HTMLCollection对象;
document.getElementByName(),按照元素标name特性进行查找,返回HTMLCollection对象;
HTMLCollection对象有一个namedItem()方法,可以根据元素的name特性取得HTMLCollection对象中对应的对象。
特殊合集:
document.anchors,包含文档中所有带有name特性的<a>元素;
document.forms,包含文档中所有<form>元素,与document.getElementByTagName("form")相同;
document.images,包含文档中所有<img>元素,与document.getElementByTagName("img")相同;
document.links,包含文档中所有带有href特性的<a>元素;
特殊合集中的内容会随当前文档的更新而更新。
10.1.3 Element类型
Element节点即元素节点,提供了对元素标签名,子节点及特性的访问。
nodeType值为1;
noadeName值为元素标签名,与tagName值相同,输出的元素标签名为大写;
<p id="a">asd</p>
console.log(document.getElementById("a").nodeType); // 1
console.log(document.getElementById("a").tagName); // P
console.log(document.getElementById("a").nodeName); // P
所有HTML元素都由HTMLElement类型表示,每个HTML元素都具有以下标准特性,下列特性均可以修改。
id,元素在文档中的唯一标识符;
title,有关元素附加说明信息;
lang,元素内容语言代码;
dir,语言的方向;
className,元素的class特性。
特性修改方法:
getAttribute(),接收一个字符串参数,为需要获取的特性名称,直接获取传入特性的值,也可以获取自定义特性的值,并且不区分大小写,只有公认的(非自定义)特性才会以属性的形式添加到DOM对象当中,获取style特性值得到的是CSS文本,通过属性访问style得到的是一个对象,获取onclick之类事件处理程序特性值返回的是相应代码的字符串,通过属性访问onclick之类事件处理程序得到的是一个JavaScript函数(未指定事件处理程序则返回null)。
<p id="a" myID="b">asd</p>
var p = document.getElementById("a");
console.log(p.id); // a
console.log(p.myID); // undefined
setAttribute(),接收两个参数,一个为需要设置的特性名称,一个为需要设置的特性的值,如果需要设置的特性已经存在,则替换原有的特性值。
removeAttribute(),接收一个字符串参数,彻底删除元素的特性。
Element类型是使用attributes属性的唯一一个DOM节点类型。attributes属性中包含一个NamedNodeMap,与NodeList类型,是一个动态集合。每一个特性节点都保存在NamedNodeMap中,attributes属性仅在需要遍历元素特性的时候才用得到。每个特性节点都有一个specified属性,如果这个属性为true,则表示该特性要么是在HTML中指定的,要么是通过setAttribute()方法设置的。来自继承的特性和方法都为false。
<p id="a" class="b" title="c" data-myID="x">asd</p>
var p = document.getElementById("a");
var obj = {};
for(var i = 0 ;i<p.attributes.length; i++){
if(p.attributes[i].specified){
obj[p.attributes[i].nodeName]=p.attributes[i].nodeValue;
}
}
console.log(obj); // {id: "a", class: "b", title: "c", data-myid: "x"}
使用document.createElement()可以创建新元素,传入需要创建的新元素名称,创建后就可以对其进行各种DOM操作,包括设置各种特性。但最后需要appendChild()、insertBefore()、replaceChild()等方法将其添加到DOM当中。
浏览器会将元素之间的空白符解释为文本节点,如果需要通过childNodes属性遍历子节点,则需要对节点类型进行判断后再执行操作。
for(var i = 0 ;i<ele.childNodes.length; i++){
if(ele.childNodes[i].nodeType == 1){
// 执行操作
}
}
每个元素应该只有一个文本子节点,使用normalize()方法可以将元素中的自文本节点合并成一个节点。
10.2 DOM操作技术
10.2.1 动态脚本
在页面加载完成后动态加载外部javascript文件的通用方法。
function loadScript(url) {
var script = document.createElement("script");
script.type = "text/javascript";
script.src = url;
document.body.appendChild(script);
}
在页面加载完成后动态加载外部javascript代码片段的通用方法。
function loadScriptCode(code) {
var script = document.createElement("script");
script.type = "text/javascript";
// IE不允许访问script元素的子节点,所以需要用script元素的text属性来添加代码片段。
try{
script.appendChild(document.createTextNode(code));
}catch(ex) {
script.text = code;
}
document.body.appendChild(script);
}
10.2.2 动态样式
在页面加载完成后动态加载外部CSS文件的通用方法。注意CSS代码应该添加到head元素中,不是body元素中。
function loadStyle(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);
}
在页面加载完成后动态加载外部CSS代码片段的通用方法。
function loadStyleCode(code) {
var style = document.createElement("style");
style.type = "text/css";
// IE不允许访问style元素的子节点,所以需要用style元素的styleSheet属性的cssText属性来添加代码片段。
try {
style.appendChild(document.createTextNode(code));
}catch (ex){
style.styleSheet.cssText = code;
}
var head = document.getElementsByTagName("head")[0];
head.appendChild(style);
}
10.2.3 操作表格
<table>元素具有以下属性和方法:
caption属性,保存着对<caption>元素的指针;
tBodies属性,<tbody>元素的HTMLCollection;
tFoot属性,保存着对<tfoot>元素的指针;
tHead属性,保存着对<thead>元素的指针;
rows属性,表格中所有行的HTMLCollection;
creatTHead(),创建<thead>元素,将其放到表格中,返回引用;
creatTFoot(),创建<tfoot>元素,将其放到表格中,返回引用;
creatCaption(),创建<caption>元素,将其放到表格中,返回引用;
deleteTHead(),删除<thead>元素;
deleteTFoot(),删除<tfoot>元素;
deleteCaption(),删除<caption>元素;
deleteRow(),删除指定参数的行;
insertRow(),向rows集合中指定位置插入行。
<tbody>元素具有以下属性和方法:
rows属性,<tbody>元素中所有行的HTMLCollection;
deleteRow(),删除指定参数的行;
insertRow(),向rows集合中指定位置插入行,返回对新插入行的引用。
<tr>元素具有以下属性和方法:
cells属性,<tr>元素中所有单元格的HTMLCollection;
deleteCells(),删除指定参数的行;
insertCells(),向cells集合中指定位置插入单元格,返回对新插入单元格的引用。
10.2.4 使用NodeList
与NodeList类似的集合,NameNodeMap、HTMLCollection之类的,都是动态合集,文档结构发生变化时,它们也会得到更新。如果要迭代一个NodeList,最好使用length属性初始化第二个变量,以保证迭代的数量不会随着迭代而发生变化。应尽量减少对NodeList的访问,因为每次访问NodeList,都会运行一次基于文档的查询,所以应当将NodeList中取得的值缓存起来,方便多次使用。