DOM之访问
今天无意中看到一篇文章觉得可以很好的回顾一下关于DOM基础知识,故翻译了一下~原文链接是here
所有的起源都来源于document这个对象。这个对象提供了提供了很多方法来搜索和修改元素。
根元素:documentElement和body
DOM的根元素总是document.documentElement, 它是一个可以引用最开始的HTML标签的特殊属性。另一个起点的属性是document.body,它表示了BODY标签。
这两个进入点都是有效的,但是document.body可以为null,比如这种情况,在HEAD标签里试图访问body就会是null,因为此时还没有解析到body标签。
<!DOCTYPE HTML>
<html>
<head>
<script>
alert("Body from HEAD: "+document.body) // null
</script>
</head>
<body>
<div>The document</div>
<script>
// different browsers output different text here,
// because of different implementations of toString
alert("Body from inside body: " + document.body)
</script>
</body>
</html>
相反,document.documentElement总是可以被访问到的。还需要注意的是document.body不能是undefined。因为在DOM的世界里,没有这个元素或者这个元素找不到就总会被表示为null。在访问脚本执行时是访问不到还没有被渲染的元素的。
子元素
childNodes
一个元素拥有一个类数组属性childNodes指向自己的所有直接子元素们。所有的节点都会被引用起来,包括空白节点(处理IE<9).
<!DOCTYPE HTML>
<html>
<body>
<div>Allowed readers:</div>
<ul>
<li>Bob</li>
<li>Alice</li>
</ul>
<!-- a comment node -->
<script>
function go() {
var childNodes = document.body.childNodes
for(var i=0; i<childNodes.length; i++) {
alert(childNodes[i])
}
}
</script>
<button onclick="go()" style="width:100px">Go!</button>
</body>
</html>
注意SCRIPT节点也会被遍历出来。还有就是document.body.childNodes[1]是DIV,但是在IE<9的情况下,由于没有空白节点,所有document.body.childNodes是UL。
children
有时候我们只需要元素节点,而不需要其他类型的节点(比如text节点,注释节点等),此时使用children属性来访问再方便不过了。
<!DOCTYPE HTML>
<html>
<body>
<div>Allowed readers:</div>
<ul>
<li>Bob</li>
<li>Alice</li>
</ul>
<!-- a comment node -->
<script>
function go() {
var children = document.body.children
for(var i=0; i<children.length; i++) {
alert(children[i])
}
}
</script>
<button onclick="go()" style="width:100px">Go!</button>
</body>
</html>
注意IE<9的话,注释节点也会存在于children中。
Children links
只是得到节点的子元素还不足以方便的遍历DOM,所有我们还需要一些其他属性,例如siblings和parent等。
firstChild 和 lastChild
用来快速访问一个节点的第一个或者最后一个子元素
所以以下代码是相等的:
var body = document.body
alert(body.firstChild === body.childNodes[0])
alert(body.lastChild === body.childNodes[body.childNodes.length-1])
parentNode, previousSibling 和 nextSibling
- parentNode属性指向节点的父节点。对于document.documentElement.parentNode为null.(这里作者说错了,document.documentElement.parentNode 是document, 而document.parentNode是null)
- previousSibling和nextSibling访问节点的左和右的邻居节点。
<!DOCTYPE HTML>
<html>
<head>
<title>My page</title>
</head>
<body>
<div>The header</div>
<ul><li>A list</li></ul>
<div>The footer</div>
</body>
</body>
</html>
浏览器总是会维护这些属性正确的值,通过这些属性可以修改,添加和删除元素,而不用担心重新赋值给这些属性的问题。
如何判断一个节点是空的?
if (elem.childNodes.length) { ... }
if (elem.firstChild) { ... }
if (elem.lastChild) { ... }
document.body.lastChild.nextSibling总是为null是正确的,因为body的最后一个子节点一定没有右兄弟节点了。document.body.children[0].previousSibling则有可能是null也有可能不是,因为也许body节点的第一个元素的左节点可能是一个text节点。
总结
- 上:parentNode
- 下:children/childNodes, firstChild, lastChild
- 左右:previousSibling/nextSibling
浏览器总是会保证他们的指向是正确值,他们都是只读的,如果他们不存在则为null。
那么怎么遍历整个文档呢?
深度遍历:
function goNodes(node) {
console.log(node.tagName);
if(node.hasChildNodes()) {
for(var i=0;i<node.childNodes.length; i++) {
goNodes(node.childNodes[i]);
}
} else {
return;
}
}
goNodes(document.documentElement);
广度遍历呢?可以思考一下。。。~
function goNodes(node) {
console.log(node.tagName);
var nodesOnSameLevel = [node];
for(var i = 0; i<nodesOnSameLevel.length; i++) {
if(nodesOnSameLevel[i].hasChildNodes()) {
for(var j = 0; j<nodesOnSameLevel[i].children.length; j++){
console.log(nodesOnSameLevel[i].children[j].tagName);
nodesOnSameLevel.push(nodesOnSameLevel[i].children[j]);
}
}
}
}
goNodes(document.documentElement);