六. W3C DOM之旅的第一步,文档访问

JavaScript DOM Tutorials

 

文档对象模型:对HTML的映射

基础知识回顾:

DOM 即(Document Object Model),文档对象模型,DOM实际是把HTML当作XML文件来进行处理,用对象的眼光来打量HTML,可以说DOM是继HTML后Web的最有用的发明。

文档对象模型(DOM)是表示文档(比如HTML和XML)和访问、操作构成文档的各种元素的应用程序接口(API)。一般的,支持Javascript的所有浏览器都支持DOM。本文所涉及的DOM,是指W3C定义的标准的文档对象模型,它以树形结构表示HTML和XML文档,定义了遍历这个树和检查、修改树的节点的方法和属性。

在DOM中,HTML文档的每个元素都是一个对象,属性和文本也都是对象。JavaScript能够独立访问每个对象,通过使用内建函数也能轻松地发现或改变所需的对象。

DOM眼中的HTML文档:树

在DOM眼中,HTML跟XML一样是一种树形结构的文档,对于W3C DOM而言,HMTL文档中的任何一样东西都是一个节点。整篇文档是一个文档节点(document node);在每一个HTML中的标签都是一个元素节点。比如:<html>是根(root)节点,<head>、<title>、<body>是<html>的子(children)节点,互相之间是兄弟(sibling)节点。

按照HMTL语言这种嵌套的层次结构,所有的元素就映射成了一个棵DOM树。这棵树从文档节点自身开始扩展分支,直到所有位于末端的文本节点。

以这个简单的HTML页面为例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
 
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
    <head>
        <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
        <title>JavaScript DOM 学习</title>
    </head>
 
    <body>
    <h1>Hello world!</h1>
    <p>
    我是一个WEB前端技术爱好者!
    我的WEB前端技术交流网站是
    <a href="http://blog.moocss.com/" title="关注WEB前端技术;重视WEB标准 ">CssRainBow.cn</a>    
    </p>
 
</body>
</html>

元素节点是DOM节点的一种类型,DOM结构中的大部分节点都是元素节点;但在实际的文档中,还包括另外两种节点:文本节点属性节点

节点类型

元素节点

即表示HTML元素的节点。有那些请看 第四章.HTML 4.01/XHTML 1.0 元素列表

文本节点

在HMTL代码中,在成对的尖括号(<>)外的所有内容都会被视为DOM的文本节点。从结构上说,文本节点和元素节点是基本相同的,它们和元素节点同处于树型结构中,也像元素节点那样被访问;但文本节点是没有子节点的。

注意:

文本节点不仅可以包含可见字符,还可以包含一些不可见的字符,例如换行符,tab等符号。

正如我们在例子中看到的代码一样,要使代码更容易与阅读,就需要换行符或TAB符号来分离标签或文本,这样都将被视为一个文本节点对待的

这就意味着相邻的元素之间要用文本节点分隔的,或者在文本节点的开始或结尾处用空白来分离的。不同的浏览器对空白节点的处理方法是不同的,鉴于DOM解析中的这种可变性,处理DOM节点的数目及顺序应格外谨慎。

举例说明:

换行符,tab等符号格式化的代码

1
2
3
4
 <div id="test">
    <h1>Hello world!</h1>
    <p>我是一个WEB前端技术爱好者</p>
 </div>

为了看出这种区别,我们可以遍历test的子节点,并将其节点个数及节点类型都打印出来:

1
2
3
4
5
var x = document.getElementById("test");
var xc = x.childNodes;//查找子节点
var xcl = xc.length;
for(var i=0;i<br />");
}

结果:

IE中:

1
2
nodeName = H1; nodeType = 1
nodeName = P; nodeType = 1

非IE中:

1
2
3
4
5
nodeName = #text; nodeType = 3
nodeName = H1; nodeType = 1
nodeName = #text; nodeType = 3
nodeName = P; nodeType = 1
nodeName = #text; nodeType = 3

两种不同的结果这就是我上面说的原因导致的。

在这种情况下,唯一有可能的原因就是在HTML的书写上,因为这段HTML并不是连续的书写,而是每个节点间都用回车换行了,非IE把换行也当成了一个节点。

连续的书写的HTML

1
 <div id="test"><h1>Hello world!</h1><p>我是一个WEB前端技术爱好者</p></div>

在IE和非IE中得到了一样的结果

1
2
nodeName = H1; nodeType = 1
nodeName = P; nodeType = 1

我还要说的就是“鉴于DOM解析中的这种可变性,处理DOM节点的数目及顺序应格外谨慎。”

属性节点

元素节点和文本节点分别用来表示标签和文本,除此之外,DOM中还需要说明的信息就是属性了。

简单的地说,属性应该是元素的一部分,从某种角度上看也是的确如此。但是属性也是一种节点类型,称作属性节点

正如图片上的示例DOM结构,任何一个anchor元素都可以加上href和title属性节点。

属性节点通常是依附于元素节点的,但和元素节点及文本节点有着本质的不同,DOM结构对于它们并不适用,属性节点并没有被看做它们所依附节点的子节点。

因此,操作属性节点通过特殊的函数实现,稍后我们将讨论这类函数。

获取节点(寻找元素)

正如前面所看到的DOM结构一样,即使是对于一个简单的文档,其DOM结构也会很快变得相当复杂。

因此需要一种有效的方法来识别和操作DOM中的节点。

在很多方面,利用DOM来操作元素都和应用CSS中的元素样式相似,都采用了如下的所示的常规模式:

  • 指定需要影响的元素或元素组;
  • 说明希望对应该元素或元素实现的效果。

同时,它们查找元素的过程也十分相似。

长途旅行

我看到的下面,说的这两个方法,如果你能给它们正确的指令并正确书写函数(javascript是区分大小写的),它们能找到整个文档中的任何一个HTML元素。

getElementById()
该方法将返回一个与那个有着给定id属性值的元素节点相对应的对象。
如果您需要查找文档中的一个特定的元素,最有效的方法是 getElementById()。
getElementsByTagName()
该方法返回一个对象数组,每个对象分别对应着文档里有着给定标签的一个元素。
即,该方法可返回带有指定标签名的对象的集合。
getElementsByTagName() 方法返回元素的顺序是它们在文档中的顺序。
如果把特殊字符串 “*” 传递给 getElementsByTagName() 方法,它将返回文档中所有元素的列表,元素排列的顺序就是它们在文档中的顺序。

1. getElementById() # 通过ID属性查找元素

实战:

1
2
3
4
5
6
7
8
<h1 id="tit">我是标题一</h1>
<p title="一个段落">段落内容放在这里儿!!!!!!!!!!!!</p>
<strong>我是无序列表</strong>
<ul id="test_ul">
    <li>列表内容1</li>
    <li>列表内容2</li>
    <li>列表内容3</li>
</ul>
1
2
3
4
var target=document.getElementById("tit");
alert(typeof(target));//typeof()判断返回值的类型
alert(target.innerHTML);//可以获得id为tit对象的内容
alert(target.nodeName);//返回该节点的标签名称


注意:

我在网页设计时尽量保持元素ID的唯一性。

如果指定的ID值的元素找不到,getElementById()将不返回任何节点,而是返回一个空值null , null 表示期望的对象并不存在。

那么,如果我们不能确定文档中是否包含了所需的具有特点ID值的元素,最安全的方法是检测getElementById()返回的是否个节点对象,因为对null值进行操作会导致程序报错或停止运行。

如果你在使用javascript时妨碍了”内容的有效性可访问性“,那么你的做法就是错误的。

给我们的代码加一些验证或检测。

1
2
3
4
5
6
7
var target=document.getElementById("tit");
//判断对象是否存在
if (target!=null){
    alert(typeof(target));//typeof()判断返回值的类型
    alert(target.innerHTML);//可以获得id为tit的对象内容
    alert(target.nodeName);//返回该节点的标签名称
}

2. getElementsByTagName() # 通过标签名称查找元素

如果每次只修改 一个元素,那么使用ID属性查找元素的方法很方便。但是如果需要一次查找一组元素,则getElementsByTagName() 更为合适。

实战:

臃肿的代码:

1
2
3
4
5
6
7
alert(document.getElementsByTagName("li").length);//获得文档中所有的li元素对象的节点数目
//遍历对象
for(var i=0;i<document.getElementsByTagName("li").length;i++){
	alert(typeof(document.getElementsByTagName("li")[i]));//每一个对象的类型
	alert(document.getElementsByTagName("li")[i].innerHTML);//获得每一个对象的值
    alert(document.getElementsByTagName("li")[i].nodeName);// 返回该节点的标签名称
}

我们需要简化代码:

1
2
3
4
5
6
7
var items=document.getElementsByTagName("li");//获得文档中所有的li元素对象
//遍历对象
for(var i=0;i<items.length;i++){
   alert(typeof items[i]);//每一个对象的类型
   alert(items[i].innerHTML);//获得每一个对象的值
   alert(items[i].nodeName);// 返回该节点的标签名称
}

给 getElementsByTagName() 方法的 “ * ”星号参数。
如果把特殊字符串 “*”, 星号通配符 传递给 getElementsByTagName() 方法,它将返回文档中所有元素的列表,元素排列的顺序就是它们在文档中的顺序。

1
2
3
4
5
6
7
8
var mylist=document.getElementById("test_ul");
var items=mylist.getElementsByTagName("*");//获得ID为“test_ul”的所有子元素对象
//遍历对象。
for(var i=0;i<items.length;i++){
    alert(typeof items[i]);//每一个对象的类型
    alert(items[i].innerHTML);//获得每一个对象的值
    alert(items[i].nodeName);// 返回该节点的标签名称
}

注意:

如果你在使用javascript时妨碍了”内容的有效性可访问性“,那么你的做法就是错误的。

我们遇到了浏览器版本问题,那就是IE5.5及更早的IE版本是不支持getElementsByTagName(“*”)。我们现在不排除有些人还在使用古董级的电脑。
你千万别内心里嘀咕,我们开发的外文的网站,是有许多老外还在使用古董级的电脑。不过在IE5.x版本中,Microsoft提供了一个特殊的document.all对象,该对象包含了文档中的所有元素。因此调用它与调用document.getElementsByTagName(“*”)的结果是相同的。

范例:

1
2
3
4
5
6
7
8
9
10
11
12
13
var elementArray=[];
if(typeof document.all!="undefined"){
 
   elementArray=document.all;//返回文档中的所有元素
 
}else{
 
   elementArray=document.getElementsByTagName("*");//返回文档中的所有元素
 
}
 
//以上代码还可以简化成一下这样
var allArray=document.all?document.all:document.getElementsByTagName("*");

根据以上原理,针对上面的例子,写一些兼容性的代码。

范例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
var mylist=document.getElementById("test_ul");
var li_all=mylist.all;
var elementArray=[];
 
if(typeof li_all!="undefined"){//检测对象是否存在
   // alert("IE5.x的document.all");
   elementArray=mylist.all;
}else{
  // alert("非IE5.x的document.all");
   elementArray=mylist.getElementsByTagName("*");
}
//遍历对象
for(var i=0;i<elementArray.length;i++){
   alert(typeof elementArray[i]);//每一个对象的类型
   alert(elementArray[i].innerHTML);//获得每一个对象的内容
   alert(elementArray[i].nodeName);// 返回该节点的标签名称
}

注意:

我们在上面的范例中用for 语句 循环输出getElementsByTagName() 方法返回的相同标签名的元素。与getElementById()方法不同的是,即使在文档中没有和指定标签名称相匹配的元素,getElementsByTagName() 方法也会返回一个节点列表。不过节点列表的长度将为0,这就意味着上面范例中用for 语句来检测节点列表长度的语句是可靠地。

3. getElementsByClass() # 通过类名查找元素

注意: getElementsByClass()方法,内建的DOM函数中并没有提供用来实现按类名查找元素的方法,因此我们需要自己动手生产一个函数来完成这一功能。

通过类名查找元素的getElementsByClass()方法,我们在这一章的第二节中,作为一个专题来讨论。

站点统计