JavaScript高级程序设计:学习笔记5--DOM及DOM扩展

1. 节点层次

    DOM可以将任何HTML或XML文档描绘成一个由多层节点构成的结构.

1. Node类型

    DOM1级定义了一个Node接口,该接口将由DOM中的所有节点类型实现.javascript中的所有节点类型都继承自Node类型,因此所有节点类型都共享着相同的基本属性和方法.

    每个节点都有一个nodeType属性,用于表明节点的类型:

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);
    而我们可以这样比较:
print(document.nodeType);	//9

1. nodeName和nodeValue属性

    nodeName保存元素的标签名,而nodeValue始终为null.由于这两个属性完全取决于节点的类型,所以使用之前最好进行一次比较:

if (someNode.nodeType == 1) { /*do something*/}
    因为nodeType==1说明它为一个元素节点(Node.ELEMENT_NODE),所以可以进行对nodeName和nodeValue的操作.
<html>
<script type="text/javascript" src="./print.js"></script>
<script type="text/javascript" src="./EventUtil.js"></script>
<Form method="post" id="myForm">	
	<ul>
    	<li><input type="radio" name="color" value="red">Red</li>
        <li><input type="radio" name="color" value="green">green</li>
        <li><input type="radio" name="color" value="blue">blue</li>
    </ul>
</form>
<body>
<script type="text/javascript">
var form = document.getElementById("myForm");
var red = document.getElementsByName("color");
</script>
</body>
</html>
firebug上显示如下:

JavaScript高级程序设计:学习笔记5--DOM及DOM扩展_第1张图片

2. 节点关系

    每个节点都有一个childNodes属性,其中保存着一个NodeList对象.NodeList是一种类数组对象,用于保存一组有序的节点,可以通过位置来访问这些节点.NodeList对象的独特之处在于:它实时反映DOM结构的变化.

JavaScript高级程序设计:学习笔记5--DOM及DOM扩展_第2张图片

我们可以通过方括号或者item来访问NodeList中的节点:

var firstChild = someNode.childNodes[0];
var secondChild = someNode.childNodes.item(1);
var count = someNode.childNodes.length;
    而我们可以遍历childNodes来枚举所有的成员:
function convertToArray(nodes) {
	var array = null;
	try {
		array = Array.prototype.slice.call(nodes, 0);
	} catch (ex) {
		array = new Array();
		for (var i = 0, len = nodes.length; i < len; i++) {
			array.push(nodes[i]);
		}
	}
	
	return array;
}

    而每个节点都有一个parentNode属性,该属性指向文档树中的父节点.我们可以通过列表中的每个节点的previousSibling和nextSibling属性,访问同一列表中的其他节点.列表中第一个节点的previousSibling属性值为null,而最后一个节点的nextSibling也为null:

对于上面的代码,我们也可以firebug如下:

JavaScript高级程序设计:学习笔记5--DOM及DOM扩展_第3张图片

    而父节点也有firstChild和lastChild属性,指向第一个节点和最后一个节点:


JavaScript高级程序设计:学习笔记5--DOM及DOM扩展_第4张图片

3. 操作节点

appendChild: 向childNodes列表的末尾添加一个节点.如果新插入的节点已经存在childNodes中,则进行转移操作(DOM节点不能同时出现在文档中的多个位置).

//someNode有多个子节点
var returnedNode = someNode.appendChild(someNode.firstChild);
print(returnedNode == someNode.firstChild);//false
print(returnedNode == someNode.lastChild);//true
insertBefore:接收两个参数,要插入的节点和作为参照的节点
//插入后成为最后一个子节点
returnedNode = someNode.insertBefore(newNode, null);
print(newNode == someNode.lastChild);	//true

//插入后成为第一个字节点
var returnedNode = someNode.insertBefore(newNode, someNode.firstChild);
print(newNode == someNode.firstChild);	//true

//插入到最后一个字节点前面
returnedNode = someNode.insertBefore(newNode, someNode.lastChild);
print(newNode == someNode.childNodes[someNode.childNodes.length - 2]);	//true
replaceChild:接收两个参数,要插入的节点和替换的节点
//替换第一个字节点
var returnedNode = someNode.replaceChild(newNode, someNode.firstChild);

//替换最后一个字节点
returnedNode = someNode.replaceChild(newNode, someNode.lastChild);
removeChild:接收一个参数,移除节点.
//移除第一个字节点
var returnedNode = someNode.removeChild(someNode.firstChild);

//移除最后一个字节点
returnedNode = someNode.removeChild(someNode.lastChild);

    实际的例子如下:

<html>
<script type="text/javascript" src="./print.js"></script>
<script type="text/javascript" src="./EventUtil.js"></script>
<body id="myBody">
<div id="myDiv1" value="hello1">hello1</div>
<div id="myDiv2" value="hello2">hello2</div>
<div id="myDiv3" value="hello3">hello3</div>
<script type="text/javascript">
var body = document.getElementById("myBody");
var myDiv1 = document.getElementById("myDiv1");
//插入后,界面显示hello2 hello3  hello1
var newDiv1 = body.appendChild(myDiv1);
print(newDiv1 == myDiv1);	//true

//插入后,界面显示hello1 hello2 hello3
myDiv1 = document.getElementById("myDiv1");
var myDiv2 = document.getElementById("myDiv2");
body.insertBefore(myDiv1, myDiv2);

//myDiv2被myDiv1代替,界面上显示hello1 hello3
myDiv1 = document.getElementById("myDiv1");
myDiv2 = document.getElementById("myDiv2");
body.replaceChild(myDiv1, myDiv2);

//移除myDiv1,界面上显示hello3
myDiv1 = document.getElementById("myDiv1");
body.removeChild(myDiv1);

</script>
</body>
</html>

cloneNode:用于创建调用这个方法的节点的一个完全相同的副本.接收一个参数:如果为true则表示进行深复制,即复制节点及其整个子节点树,为false则单单复制节点本身.

<Form method="post" id="myForm"> 
    <ul>
        <li><input type="radio" name="color" value="red">Red</li>
        <li><input type="radio" name="color" value="green">green</li>
        <li><input type="radio" name="color" value="blue">blue</li>
    </ul>
</form>
<body>
<script type="text/javascript">
var form = document.getElementById("myForm");
var deepList = form.cloneNode(true);
print(deepList.childNodes.length);	//3
var shallowList = form.cloneNode(false);
print(shallowList.childNodes.length);	//0
</script>
normalize():处理文档树中的文本节点.

    在处理DOM操作中,可能会出现文本节点不包含文本,或者接连出现两个文本节点的情况.当在某个节点上调用这个方法时,就会在该节点的后代节点中查找上述情况.如果找到了空文本节点,则删除它;如果找到相邻的文本节点,则将它们合并为一个文本节点.

2. Document类型

    javascript通过Document类型表示文档.在浏览器中,document对象是HTMLDocument(继承自Document类型)的一个实例,表示整个HTML页面.而且,document对象是window对象的一个属性,因此可以将其作为全局对象来访问(Document并不表示具体的节点,而是表示整个HTML页面).Document节点具有下列特征:

1. nodeType的值为9

2. nodeName的值为#document

3. nodeValue的值为null

4. parentNode的值为null

5. ownerDocument的值为null

6. 其子节点可能是一个DocumentType(最多一个),Element(最多一个),ProcessingInstruction或Comment.

1. 文档的子节点

    快速访问节点:documentElement和ChildNodes:

<html id="hello">
<body id="world">
<script type="text/javascript">
function print(str) {
	document.write(str + "<br />");
}

var html = document.documentElement;
print(html === document.childNodes[0]);	//true
print(html === document.firstChild);	//true

</script>
</body>
</html>
    而我们在firebug里面输入:

JavaScript高级程序设计:学习笔记5--DOM及DOM扩展_第5张图片

    这实际上并不正确,因为document.childNodes表示页面中第一个层次的节点,一般来说是<html>,但是存在两个第一层次的节点:

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
	"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html>
    这时候document.childNodes[0]并不等于document.documentElement了.所以我们一般使用document.documentElement来调用<html>,用document.doctype来调用<!DOCTYPE>标签:

多数情况下,我们都用不着在document对象上调用appendChild(),removeChild()和replaceChild()方法,因为文档类型是只读的,而且它只能有一个元素子节点(html).

2. 文档信息

    document通常有以下属性:title(包含<title>的内容),URL(地址栏显示的URL),domain(页面的域名)和referrer(链接到当前页面的那个页面的URL):

<html id="hello">
<title>this is a title</title>
<body id="world">
<script type="text/javascript">
function print(str) {
	document.write(str + "<br />");
}

setTimeout(function(){
	location.replace("http://www.wrox.com/");
}, 1000);

</script>
</body>
</html>
    在还没有跳转的情况下,在页面输入输出为:

JavaScript高级程序设计:学习笔记5--DOM及DOM扩展_第6张图片

    但是,跳转之后为:

JavaScript高级程序设计:学习笔记5--DOM及DOM扩展_第7张图片

3. 查找元素

getElementById():接收一个参数为要取得元素的ID.(但是如果多个元素的ID相同,则只返回第一次出现的元素,但是在DOM情况下,一般ID都是唯一的)

getElementsByTagName():接收一个参数为要取得元素的标签名.而返回的是包含零个或多个元素的NodeList.

namedItem():通过元素的name来取得集合中的项(多个name相同情况下,只会取第一个name的值)

getElementsByName():根据元素的name来取得集合中的所有项.

    输入代码如下:

<html id="hello">
<title>this is a title</title>
<body id="world">
<script type="text/javascript">
function print(str) {
	document.write(str + "<br />");
}

</script>

<fieldset>
	<legend>Which color do you prefer?</legend>
	<ul>
		<li><input type="radio" value="red" name="color" id="colorRed">
			<label for="colorRed">Red</label></li>
		<li><input type="radio" value="green" name="color" id="colorGreen">
			<label for="colorGreen">Green</label></li>
		<li><input type="radio" value="blue" name="color" id="colorBlue">
			<label for="colorBlue">Blue</label></li>
	</ul>
</fieldset>
</body>
</html>
    浏览器显示如下:

JavaScript高级程序设计:学习笔记5--DOM及DOM扩展_第8张图片

    而我们在firebug里面的调试信息如下:

JavaScript高级程序设计:学习笔记5--DOM及DOM扩展_第9张图片

4. 文档写入

write:要写入的输出流的文本

writeln:要写入的输出流的文本,但是字符串末尾会加换行符\n

<html>
<head>
	<title>document.write() example</title>
</head>
<body>
	<p>The current date and time is:
	<script type="text/javascript">
		document.write("<strong>" + (new Date()).toString() + "</strong>");
	</script>
	</p>
</body>
</html>

3. Element类型

    Element类型用于表现XML或HTML元素,提供了对元素标签名,子节点及特性的访问.Element节点具有以下特征:

1. nodeType的值为1

2. nodeName的值为元素的标签名

3. nodeValue的值为null

4. parentNode可能是Document或Element

5. 其子节点可能是Element,Text,Comment,ProcessingInstruction,CDATASection或EntityReference.

    要访问元素的标签名,可以使用nodeName属性,也可以使用tagName属性;这两个属性会返回相同的值.

<html>
<body>
	<div id="myDiv"></div>
</body>
</html>
而firebug里面显示如下:

JavaScript高级程序设计:学习笔记5--DOM及DOM扩展_第10张图片

    由于显示的DIV为大写,但可能存在情况是nodeName为小写.所以我们进行比较的时候,最好要用toLowerCase函数,把它转换为小写.

1. HTML元素

    所有HTML元素都由HTMLElement类型表示,不是直接通过这个类型,也是通过它的子类型来表示.下列是HTML元素中都存在的标准特性:

1. id,元素在文档中的唯一标识符.

2. title,有关元素的附加说明,一般通过工具提示条显示出来.

3. lang,元素内容的语言代码,很少使用

4. dir,语言方向.ltr(left-to-right)或者rtl(right-to-left)

5. className,元素指定的CSS类

<html>
<body>
	<div id="myDiv" class="bd" title="Body text" lang="en" dir="ltr">hello world</div>
</body>
</html>
firebug调试信息如下:

JavaScript高级程序设计:学习笔记5--DOM及DOM扩展_第11张图片

    这里特别有用的一点是title属性,当我们把鼠标放在内容上时候,就会显示出来.

2. 取得特性

    每个元素都有一个或多个特性.这些特性的内容是给出相应元素或其他内容的附加信息.getAttribute()用于取得特性,它区别于直接使用对象的方法是:它可以调用自定义的属性:

<html>
<meta charset="utf-8" />
<head>
</head>
<body>
<div id="myDiv1" title="this is a div" myValue="value">hello world</div>
</body>
<script type="text/javascript" >
var div = document.getElementById("myDiv1");
</script>

</html>
firebug调试信息如下:

JavaScript高级程序设计:学习笔记5--DOM及DOM扩展_第12张图片

    有两类特殊的特性,它们虽然有对应的属性名,但是属性的值与通过getAttribute()返回的值并不相同.第一类特性就是style,用于通过CSS为元素指定样式.在通过getAttribute访问时,返回的style特性值中包含的是CSS文本,而通过属性来访问它则会返回一个对象.第二类是onclick这样的事件处理程序.当在元素上使用时,onclick特性中包含的是javascript代码,如果通过getAttribute访问时,则会返回相应代码的字符串.而在访问onclick属性时,则会返回一个javascript函数.

    由于存在这些差别,在通过javascript以编程方式操作DOM时,开发人员经常不使用getAttribute,而只是使用对象的属性,除非取得自定义特性才使用getAttribute方法.

3. 设置特性

    我们可以通过setAttribute来设置特性,当然也是可以直接通过设定属性值来达到目标.但是setAttribute的方法可以设定自定义的特性:

<html>
<body>
	<div id="myDiv" class="bd" title="Body text" lang="en" dir="ltr" data-my_special_attribute="my attribute">hello world</div>
	<script type="text/javascript">
		var div = document.getElementById("myDiv");
		div.setAttribute("data-my_special_attribute", "new my attribute");
		document.write(div.getAttribute("data-my_special_attribute"));	//new my attribute 
	</script>
</body>
</html>

    但是我们不能使用div.data-my_special_attribute="new my attribute"来进行自定义属性的设置,这是无效的.

    我们也可以直接通过removeAttribute来删除属性.

4. attributes属性

    Element类型是使用attributes属性的唯一一个DOM节点类型.它包含了一个NamedNodeMap的动态集合,具有下列方法:

1. getNamedItem(name):返回nodeName属性等于name的节点;

2. removeNamedItem(name):从列表中移除nodeName属性等于name的节点.

3. setNamedItem(node):向列表中添加节点,以节点的nodeName属性为索引.

4. item(pos):返回位于数字pos位置处的节点.

    而nodeValue就是特性的值,nodeName就是属性的名称.

<html>
<body>
	<div id="myDiv" class="bd" title="Body text" lang="en" dir="ltr" data-my_special_attribute="my attribute">hello world</div>
	<script type="text/javascript">
		var div = document.getElementById("myDiv");
		var id = div.attributes.getNamedItem("id").nodeValue;
		document.write(id + "<br />");	//myDiv
		div.attributes["id"].nodeValue = "new Div";
		id = div.attributes.getNamedItem("id").nodeValue;
		document.write(id);	//new Div
	</script>
</body>
</html>
    但是,这attributes的方法不够好用,最好使用getAttribute的方法.而attributes的方法可以用于遍历元素的所有特性:
		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(" ");
		}

    但是IE7及更早的版本可能包含所有的特性,所以我们需要一次判断:每个特性节点都有一个名为specified的属性,这个属性的值如果为true,则意味着要么是在HTML中指定了相应特性,要么是通过setAttribute()方法设置了该特性.所以算法修改为:

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) {
			pairs.push(attrName + "=\"" + attrValue + "\"");
		}	
	}
			
	return pairs.join(" ");
}

5. 创建元素

    通过document.createElement()方法来创建新元素.但是新元素的创建还需要appendChild(),insertBefore()或replaceChild()方法添加到文档树中:

<script type="text/javascript">
var div = document.createElement("div");
div.id = "myDiv2";
var body = document.body;
body.appendChild(div);
var myDiv2 = document.getElementById("myDiv2");
var textNode = document.createTextNode("haha");
myDiv2.appendChild(textNode);
</script>

4. Text类型

    文本节点由Text类型表示,包含纯文本内容.纯文本中可以包含转义后的HTML字符,但不能包含HTML代码.Text具有以下的特征:

1. nodeType的值为3;

2. nodeName的值为"#text";

3. nodeValue的值为节点所包含的文本;

4. parentNode是一个Element;

5. 不支持(没有)子节点.

    可以通过nodeValue属性或data属性访问Text节点中包含的文本,这两个属性中包含的值相同.以下方法可操作节点中的文本:

1. appendData(text):将text添加到节点的末尾.

2. deleteData(offset, count):从offset指定的位置开始删除count个字符.

3. insertData(offset, text):在offset指定的位置插入text.

4. replaceData(offset, count, text):用text替换从offset指定的位置开始到offset+count为止的文本.

5. splitText(offset):从offset指定的位置将当前文本节点分成两个文本节点.

6. substringData(offset, count):提取从offset指定的位置开始到offset+count为止处的字符串.

<html>
<body>
	<div id="myDiv1" title="Body text">hello world1</div>
	<div id="myDiv2" title="Body text">hello world2</div>
	<div id="myDiv3" title="Body text">hello world3</div>
	<script type="text/javascript">
	var div = document.getElementById("myDiv1");
	div.firstChild.nodeValue = "new hello world1";	//new hello world1
	div.firstChild.appendData(" ???");		//new hello world1 ???
	div.firstChild.deleteData(0, 5);		//ello world1 ???
	</script>
</body>
</html>

1. 创建文本节点

    可以使用document.createTextNode()创建新文本节点,接受一个参数--要插入节点中的文本.但是我们要把新节点添加到文档树中已经存在的节点.

<html>
<body>
	<div id="myDiv1" title="Body text">hello world1</div>
	<script type="text/javascript">
	var div = document.getElementById("myDiv1");
	
	var textNode = document.createTextNode("____Hello world!");
	//两个节点连接起来
	div.appendChild(textNode);		//hello world1____Hello world!
	</script>
</body>
</html>

    这里必须注意,虽然输出:hello world1____Hello world!,但是这里是两个文本相邻显示而已!


    而我们需要函数normalize()来将所有的文本合并起来:

    当然,我们也可以使用splitText来分割字符串:

<html>
<body>
	<div id="myDiv1" title="Body text">hello world1</div>
	<script type="text/javascript">
	var div = document.getElementById("myDiv1");
	
	var textNode = document.createTextNode("____Hello world!");
	//两个节点连接起来
	div.appendChild(textNode);		//hello world1____Hello world!
	
	div.normalize();
	
	div.firstChild.splitText("hello world1".length);
	</script>
</body>
</html>
firebug调试如下:

5. Comment类型

    注释在DOM中是通过Comment类型来表示的.Comment节点具有下列特征:

1. nodeType的值为8;

2. nodeName的值为"#comment";

3. nodeValue的值为注释的内容.

4. parentNode可能是document或Element;

5. 不支持(没有)子节点.

    Comment类型与Text类型继承自相同的基类,拥有除了splitText之外的所有操作方法.注释节点可以通过父节点来访问.

<html>
<body>
	<!--the comment 1-->
	<div id="myDiv1" title="Body text"><!--the comment 2--></div>
	<script type="text/javascript">
	</script>
</body>
</html>
而firebug调试如下:

6. CDATASection类型    

    CDATASection类型只争对基于XML的文档,表示的是CDATA区域.

1. nodeType的值为4;

2. nodeName的值为"#cdata-section";

3. nodeValue的值是CDATA区域中的内容;

4. parentNode可能是Document或Element;

5. 不支持(没有)子节点.

2. DOM操作技术

1. 动态脚本

    使用<script>元素可以向页面中插入javascript代码,一种方式是通过其src特性包含外部文件,另一种方式就是用这个元素本身来包含代码.

    当我们加载如下脚本的时候:

<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");
    当我们面对另一种加载js代码的方式时候:
	<script type="text/javascript">
	function sayHi() {
		alert("Hi");
	}
	</script>
    我们可以这样编写:
		var script = document.createElement("script");
		script.type = "text/javascript";
		script.appendChild(document.createTextNode("function sayHi() {alert('Hi');}"));
		document.body.appendChild(script);

    为了应付可恶的IE,则代码最终修改如下:

function loadScriptString(code) {
	var script = document.createElement("script");
	script.type = "text/javascript";
	try {
		script.appendChild(document.createTextNode(code));
	} catch(ex) {
		script.text = code;
	}
	
	document.body.appendChild(script);
}
    则我们可以这样调用:
loadScriptString("function sayHi(){alert('Hi');}");

2. 动态样式

    <link>元素用于包含来自外部的文件,而<style>元素用于指定嵌入的样式.所谓动态样式是指页面刚加载时不存在的样式;动态样式是在页面加载完成后动态添加到页面中的.

<link rel="stylesheet" type="text/css" href="styles.css">
    我们可以动态编写如下:
	var link = document.createElement("link");
	link.rel = "stylesheet";
	link.type = "text/css";
	link.href = "style.css";
	var head = document.getElementsByTagName("head")[0];
	head.appendChild(link);
    而对于<style>格式:
	
	<style type="text/css">
	body {
		background-color : red;
	}
	</style>
    我们可以修改如下:
	var style = document.createElement("style");
	style.type = "text/css";
	style.appendChild(document.createTextNode("body{background-color : red}"));
	var head = document.getElementsByTagName("head")[0];
	head.appendChild(style);

    封装的函数如下:

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);
}

loadStyle("styles.css");
function loadStyleString(css) {
	var style = document.createElement("style");
	style.type = "text/css";
	try {
		style.appendChild(document.createTextNode(css));
	} catch(ex) {
		style.styleSheet.cssText = css;
	}
	var head = document.getElementsByTagName("head")[0];
	head.appendChild(style);
}
loadStyleString("body{background-color:red}");

2. DOM扩展

1. 选择符API

querySelector()方法:接收一个CSS选择符,返回与该模式匹配的第一个元素,没有找到匹配则返回null.

querySelectorAll()方法:和querySelector方法类似,只是返回所匹配的所有元素,是一个NodeList的实例.

<body>
<div id="myDiv" title="this is a div">hello world</div>
<div class="selected" title="this is a class">class</div>
<img class="button" id="button1" />
<img class="button" id="button2" />

<script type="text/javascript">
//取得body元素
var body = document.querySelector("body");
//取得ID为"myDiv"的元素
var myDiv = document.querySelector("#myDiv");
//取得类为"selected"的第一个元素
var selected = document.querySelector(".selected");
//取得类为"button"的第一个图像元素
var img = document.body.querySelector("img.button");
</script>
浏览器显示如下:

JavaScript高级程序设计:学习笔记5--DOM及DOM扩展_第13张图片

    而querySelectorAll的方法使用如下:
	//取得某<div>中的所有<em>元素(类似于getElementsByTagName("em"))
	var ems = document.getElementById("myDiv").querySelectorAll("em");
	
	//取得类为"selected"的所有元素
	var selected = document.querySelectorAll(".selected");
	
	//取得所有<p>元素中的所有<strong>元素
	var strongs = document.querySelectorAll("p strong");

    得到NodeList后我们可以通过[]或者item来获取其值.

    但是如果传入了浏览器不支持的选择符或者选择符有语法错误,则querySelector()和querySelectorAll()会抛出错误.

2. 元素遍历

    新定义了DOM元素的5个属性:

1. childElementCount:返回子元素(不包括文本节点和注释)的个数

2. firstElementChild:指向第一个子元素;firstChild的元素版

3. lastElementChild:指向最后一个子元素;lastChild的元素版

4. previousElementSibling:指向前一个同辈元素;previousSibling的元素版

5. nextElementSibling:指向后一个同辈元素;nextSibling的元素版

备注:元素版的意思是:此节点必定为元素,因为递归childNodes的情况下,存在文本节点或者注释或者换行等等.

例子如下:我们遍历某元素的所有子元素,通常需要编写以下代码:

var i,
	len,
	child = element.firstChild;
while (child != element.lastChild) {
	if (child.nodeType == 1) {	//检查是不是元素
		processChild(child);
	}
	child = child.nextSibling;
}
    而如今我们可以这样编写
var i,
	len,
	child = element.firstElementChild;
while (child != element.lastElementChild) {
	processChild(child);
	child = child.nextElementSibling;
}

3. HTML5

1. 与类相关的补充

    class属性被使用过多,所以需要添加一些API来优化其操作:

1. getElementsByClassName()方法

    此方法接收一个参数,即一个包含一或多个类名的字符串,返回带有指定类的所有元素的NodeList.传入多个类名时,类名的先后顺序不重要.

    调用这个方法时,只有位于调用元素子树中的元素才会返回.在document对象上调用getElementsByClassName()始终会返回与类名匹配的所有元素,在元素上调用该方法就只会返回后代元素中匹配的元素:

<body>
	<div class="username" id="myDiv1">hello world</div>
	<div class="current" id="myDiv2">hello world</div>
	<div class="username current" id="myDiv3">hello world</div>
<script type="text/javascript">
var myDiv1 = document.getElementById("myDiv1");
var errorUserName = myDiv1.getElementsByClassName("username");
var userName = document.getElementsByClassName("username");
var allCurrentUserNames = document.getElementsByClassName("username current");
</script>
</body>
firebug调试如下:

JavaScript高级程序设计:学习笔记5--DOM及DOM扩展_第14张图片

2. classList属性

    在操作类名时,需要通过className属性添加,删除和替换类名.但是HTML5之前必须读取出整个类名,然后进行操作.HTML5定义了新集合类型DOMTokenList,此类型定义了如下的方法:

1.add(value):将给定的字符串值添加到列表中.如果值已经存在,就不添加了.

2. contains(value):表示列表中是否存在给定的值,如果存在则返回true,否则返回false.

3. remove(value):从列表中删除给定的字符串.

4. toggle(value):如果列表中已经存在给定的值,删除它;如果列表中没有给定的值,添加它.

    这样有了classList属性,除非需要全部删除所有类名,或者完全重写元素的class属性,否则也就用不到className属性了.

<body>
	<div class="username" id="myDiv1">hello world</div>
	<div class="current" id="myDiv2">hello world</div>
	<div class="bd user disabled" id="myDiv3">hello world</div>
<script type="text/javascript">
var myDiv3 = document.getElementById("myDiv3");
</script>
firebug调试如下:

JavaScript高级程序设计:学习笔记5--DOM及DOM扩展_第15张图片

2. 焦点管理

1. document.activeElement属性

    这个属性始终会引用DOM中当前获得了焦点的元素.元素获得焦点的方法有页面加载,用户输入(通常是通过按Tab键)和在代码中调用focus()方法:

var button = document.getElementById("myButton");
button.focus();
alert(document.activeElement === button);	//true

2. document.hasFocus()方法

    此方法用于确定文档是否获得了焦点:

var button = document.getElementById("myButton");
button.focus();
alert(document.hasFocus());	//true
    通过检查文档是否获得了焦点,可以知道用户是不是正在与页面交互.

3. HTMLDocument的变化

1. readyState属性

    存在两个值:loading,正在加载文档. complete,已经加载完文档.

    使用document.readyState的最恰当方式,就是通过它来实现一个指示文档已经加载完成的指示器:

if (document.readyState == "complete") {
    //执行操作
}

2. 兼容模式

    compatMode属性:在标准模式下,document.compatMode的值等于"CSS1Compat",而在混杂模式下,document.compatMode的值等于"BackCompat":

if (document.compatMode === "CSS1Compat") {
	alert("Standards mode");
} else {
	alert("Quirks mode");
}

3. head属性

var head = document.head || document.getElementsByTagName("head")[0];

4. 字符集属性

    charset属性表示文档中实际使用的字符集,defaultCharset表示默认浏览器及操作系统的设置,当前文档默认的字符集应该是什么:

5. 自定义数据属性

    HTML5规定可以为元素添加非标准的属性,但要添加前缀data-,目的是为元素提供与渲染无关的信息,或者提供语义信息.这些属性可以任意添加,随意命名,只要以data-开头即可(但是一定要以小写字母来命名):

<body>
	<div class="username" id="myDiv1" data-appId="12345" data-myname="Nicholas">hello world</div>
<script type="text/javascript">
var div = document.getElementById("myDiv1");

var appId = div.dataset.appId;	//undefined--只是因为data-appId中包含大写字母
var myName = div.dataset.myname;	//Nicholas
</script>
而实例如下
<body>
	<div class="username" id="myDiv1" data-appid="12345" data-myname="Nicholas">hello world</div>
<script type="text/javascript">
var div = document.getElementById("myDiv1");

//取得自定义属性的值
var appId = div.dataset.appid;	
var myName = div.dataset.myname;	

//设置值
div.dataset.appId = 23456;
div.dataset.myname = "Michael";

if (div.dataset.myname) {
	print("Hello, " + div.dataset.myname);	//Hello, Michael
}
</script>

6. 插入标记

    DOM在操作大量新的HTML时候会很麻烦,因为需要一一创建DOM节点然后一一的插入.而HTML5提供了一些属性用于处理这种情况.

1. innerHTML属性

    在读模式下,innerHTML属性返回与调用元素的所有子节点(包括元素,注释和文本节点)对应的HTML标记.在写模式下,innerHTML会根据指定的值创建新的DOM树,然后用这个DOM树完全替换调用元素原先的所有子节点.

替换调用元素原先的所有子节点.
<body>
	<div id="content">
		<p>This is <strong>paragraph</strong> wit a list following it.</p>
		<ul>
			<li>Item 1</li>
			<li>Item 2</li>
			<li>Item 3</li>
		</ul>
	</div>
<script type="text/javascript">
var div = document.getElementById("content");

//读模式下显示HTML
var innerHtml = div.innerHTML;
print(innerHtml);

//写模式下更改HTML的内容
div.innerHTML = "hello world";
</script>
</body>
    浏览器显示如下:

JavaScript高级程序设计:学习笔记5--DOM及DOM扩展_第16张图片

    使用innerHTML也有一些限制:通过innerHTML插入<script>元素并不会执行其中的脚本(因为<script>是无作用域的元素,不可显示,跟注释一样).所以想插入<script>,我们需要在前面添加一个"有作用域的元素",可以是一个文本节点,也可以是一个没有结束标签的元素如<input>:

div.innerHTML = "_<script defer>alert('hi');</script>";
div.innerHTML = "<div>&nbsp;</div><script defer>alert('hi');</script>";
div.innerHTML = "<input type=\"hidden\"><script defer>alert('hi');</script>";
    第一行代码插入一个文本节点,但是为了界面的显示,我们需要移除这个节点.第二行代码类似,我们也需要移除<div>节点;最后一行代码由于隐藏的<input>不影响页面布局,所以通常情况是首选.
    我们也可以直观的通过innerHTML插入<style>元素:
div.innerHTML = "<style type=\"text/css\">body{background-color: red;}</style>";
    不支持innerHTML的元素有:<col>,<colgroup>,<frameset>,<head>,<html>,<style>,<table>,<tbody>,<thead>,<tfoot>和<tr>.

2. outerHTML属性

    在读模式下,outerHTML返回调用它的元素及所有子节点的HTML标签.在写模式下,outerHTML会根据指定的HTML字符串创建新的DOM子树,然后用这个DOM子树完全替换调用元素.

<body>
	<div id="content">
		<p>This is <strong>paragraph</strong> wit a list following it.</p>
		<p>this is a test.</p>
		<ul>
			<li>Item 1</li>
			<li>Item 2</li>
			<li>Item 3</li>
		</ul>
	</div>
<script type="text/javascript">
var div = document.getElementById("content");

//读模式下显示HTML
var outerHtml = div.outerHTML;
print(outerHtml);

//写模式下更改HTML的内容---这里会替换所有的<p>标签
div.outerHTML = "<p>This is a paragraph.</p>";
</script>
</body>
    浏览器显示如下:

JavaScript高级程序设计:学习笔记5--DOM及DOM扩展_第17张图片

3. insertAdjacentHTML()方法

    insertAdjacentHTML()方法接收两个参数:插入位置和要插入的HTNML文本.第一个参数必须是下列值之一:
beforebegin:在当前元素之前插入一个紧邻的同辈元素.
afterbegin:在当前元素之下插入一个新的子元素或在第一个子元素之前再插入新的子元素.
beforeend:在当前元素之下插入一个新的子元素或在最后一个子元素之后再插入新的子元素.
afterend:在当前元素之后插入一个紧邻的同辈元素.

<body>
	<div id="content">
		<p>This is <strong>paragraph</strong> wit a list following it.</p>
		<p>this is a test.</p>
		<ul>
			<li>Item 1</li>
			<li>Item 2</li>
			<li>Item 3</li>
		</ul>
	</div>
<script type="text/javascript">
var div = document.getElementById("content");

div.insertAdjacentHTML("beforebegin", "<p>beforebegin paragraph</p>");
div.insertAdjacentHTML("afterend", "<p>afterend paragraph</p>");
div.insertAdjacentHTML("afterbegin", "<p>afterbegin paragraph</p>");
div.insertAdjacentHTML("beforeend", "<p>beforeend paragraph</p>");
</script>
</body>
浏览器显示如下:

JavaScript高级程序设计:学习笔记5--DOM及DOM扩展_第18张图片


你可能感兴趣的:(JavaScript,dom)