Jul
18
正如[原]《JavaScript DOM编程艺术》的笔记:DOM 一文中描述的,DOM把一份文档表示为一棵树,或称家谱树,并使用parent(父)、child(子)、sibling(兄弟)等记号来表示家族成员之间的关系。DOM文档是由节点构成的集合,节点是文档树上的数字和树叶。DOM里存在许多不同类型的节点,其中最常见的有三种:元素节点、属性节点和文本节点。节点类型和具体的节点决定了其是否能包含其他节点。
例如,元素节点可以包含属性节点,但不一定能包含文本节点。<p>、<span>就可以包含文本节点,<ul>就不行。同时,根据您使用DOM获取节点的方法不同,对节点的处理方式也不一样。特别是涉及关键字this 的情况。
一、示例说明
以一个实际的例子来具体说明。在这个例子中,我们希望通过点击一个链接,然后把链接文本内容(而不是目标)显示在屏幕中。
HTML代码如下:
结果?您预想中的结果是什么呢?实际查看页面,点击链接后,结果是:
可见,得到的不是链接目标,而并不是我们需要的实际链接文本内容。
二、分析
既然结果不正确,我们来逐步分析问题原因。
1、节点类型
我们知道,关键字this在这种情况下应该传递的是点击对象的本身。但这个本身是什么含义呢?
幸亏,DOM提供了一个nodeType的方法,查看某节点的类型(不同于JavaScript的typeof方法)。所以,我们先来看看this传递的是什么对象。把JavaScript函数改动一下:
页面显示结果:
可见,在这里this 传递的对象是一个a元素节点(类型1表示元素节点)。而元素节点本身的nodeValue为null 。这可通过实际代码来看看,把代码改为:
结果:
◎ 这与我们从DOM一文了解到的信息是相同的:
那么,到底链接内容是属于哪个节点呢?
2、子节点
链接内容既然不是a标记的属性,那只可能是一个文本节点。修改代码:
结果:
可见,链接内容文本时作为一个文本节点,连接到a标记属性节点的后面的。文本节点也是节点的类型之一,类型值为3。所以,其拥有nodeValue属性。
但是,如果链接内容不是一个单纯的文本,而是包括其他的标记,例如HTML代码是:
这时,我们得到的结果就完全不同了:
※ 所以,在使用DOM方式时,务必注意操作的元素对象属性。
我们又得到一个新的元素节点。怎么办?
当然,你可以继续往下寻找strong标记的子节点(一个文本节点),肯定可以得到相关的值。但万一链接文本内容再次改变,代码就又要跟着修改。所以,这明显不是一种好办法。
这时,我们可借助span标记和innerHTML,把链接文本内容放在span标记里面,HTML代码改为:
单纯这样还不行,a元素节点下的第一个子节点还是span,我们还需要借助innerHTML的帮助:
结果:
※ 注意,如果不使用innerHTML,那你只能使用DOM 2的办法逐个往下遍历子节点。使用span标记是为了把整个链接文本内容归在一起,以防止再出现独立的子节点。留意,结果中“链接内容节点的值”是个整体(还是粗体)的。
3、不同标记,其值的属性值不同
正如前面所说的,span、div、a等标记,可使用其innerHTML属性获得或设置其文本值。而对于常用于form中的input、select等标记,则可以通过value值获得,它们并没有innerHTML属性。
例如:
在JavaScript中获得其值:
三、关于innerHTML
在修改类似的a、p、span、div等标记时,innerHTML确实很方便,例如,如果一开始我们就使用:
结果:
因为innerHTML忽略了子节点的问题,对于直接处理文字带来了很多方便。但是,因为忽略了子节点处理,对于只修改部分HTML元素时可能会带来问题。
根据前人的建议是:
还有一些具体的问题,如innerHTML自带了语法检查功能,它会自动把不完整的HTML代码补充完整等。可见:Javascript:小心使用innerHTML
四、没有关键字this
上面我们是通过this传递需要操作的对象本身,下面的HTML代码去掉了this:
JavaScript代码:
结果:
注意,getElementsByTagName返回的是一个包含对应标记的元素数组,需从中选择其子元素进行实际操作。这个只有一个a元素,所以可通过node[0]指定。
例如,元素节点可以包含属性节点,但不一定能包含文本节点。<p>、<span>就可以包含文本节点,<ul>就不行。同时,根据您使用DOM获取节点的方法不同,对节点的处理方式也不一样。特别是涉及关键字this 的情况。
一、示例说明
以一个实际的例子来具体说明。在这个例子中,我们希望通过点击一个链接,然后把链接文本内容(而不是目标)显示在屏幕中。
HTML代码如下:
<head>
<script language="javascript" type="text/javascript">
function showhref(obj) {
var content = document.getElementById("content");
content.innerHTML = obj;
}
</script>
</head>
<body>
<a href="#" onclick="showhref(this);return false;">点击</a>
<div id="content">
没有内容
</div>
</body>
<script language="javascript" type="text/javascript">
function showhref(obj) {
var content = document.getElementById("content");
content.innerHTML = obj;
}
</script>
</head>
<body>
<a href="#" onclick="showhref(this);return false;">点击</a>
<div id="content">
没有内容
</div>
</body>
结果?您预想中的结果是什么呢?实际查看页面,点击链接后,结果是:
引用
点击
http://localhost/this_test.html#
http://localhost/this_test.html#
可见,得到的不是链接目标,而并不是我们需要的实际链接文本内容。
二、分析
既然结果不正确,我们来逐步分析问题原因。
1、节点类型
我们知道,关键字this在这种情况下应该传递的是点击对象的本身。但这个本身是什么含义呢?
幸亏,DOM提供了一个nodeType的方法,查看某节点的类型(不同于JavaScript的typeof方法)。所以,我们先来看看this传递的是什么对象。把JavaScript函数改动一下:
function showhref(obj) {
var content = document.getElementById("content");
content.innerHTML = "obj节点名称是:"+obj.nodeName+"<br />";
content.innerHTML += "类型是:"+obj.nodeType;
}
var content = document.getElementById("content");
content.innerHTML = "obj节点名称是:"+obj.nodeName+"<br />";
content.innerHTML += "类型是:"+obj.nodeType;
}
页面显示结果:
引用
点击
obj节点名称是:A
类型是:1
obj节点名称是:A
类型是:1
可见,在这里this 传递的对象是一个a元素节点(类型1表示元素节点)。而元素节点本身的nodeValue为null 。这可通过实际代码来看看,把代码改为:
content.innerHTML += "其nodeValue是:"+obj.nodeValue;
结果:
引用
点击
obj节点名称是:A
其nodeValue是:null
obj节点名称是:A
其nodeValue是:null
◎ 这与我们从DOM一文了解到的信息是相同的:
引用
如果给定节点是一个元素节点,该属性将返回null 。
如果给定节点是一个属性节点,将返回这个属性的值。
如果给定节点是一个文本节点,将返回这个文本节点的内容。
nodeValue属性是一个读/写属性,但不能对已经被定义为null的值进行设置(也就是元素节点没有这个属性)。
如果给定节点是一个属性节点,将返回这个属性的值。
如果给定节点是一个文本节点,将返回这个文本节点的内容。
nodeValue属性是一个读/写属性,但不能对已经被定义为null的值进行设置(也就是元素节点没有这个属性)。
那么,到底链接内容是属于哪个节点呢?
2、子节点
链接内容既然不是a标记的属性,那只可能是一个文本节点。修改代码:
content.innerHTML += "其第一个子节点的类型是:"+obj.firstChild.nodeType+"<br />";
content.innerHTML += "其第一个子节点的名称是:"+obj.firstChild.nodeName+"<br />";
content.innerHTML += "其第一个子节点的值是:"+obj.firstChild.nodeValue;
content.innerHTML += "其第一个子节点的名称是:"+obj.firstChild.nodeName+"<br />";
content.innerHTML += "其第一个子节点的值是:"+obj.firstChild.nodeValue;
结果:
引用
点击
obj节点名称是:A
其第一个子节点的名称是:#text
其第一个子节点的类型是:3
其第一个子节点的值是:点击
obj节点名称是:A
其第一个子节点的名称是:#text
其第一个子节点的类型是:3
其第一个子节点的值是:点击
可见,链接内容文本时作为一个文本节点,连接到a标记属性节点的后面的。文本节点也是节点的类型之一,类型值为3。所以,其拥有nodeValue属性。
但是,如果链接内容不是一个单纯的文本,而是包括其他的标记,例如HTML代码是:
<a href="#" onclick="showhref(this);return false;"><strong>点击</strong></a>
这时,我们得到的结果就完全不同了:
引用
点击
obj节点名称是:A
其第一个子节点的名称是:STRONG
其第一个子节点的类型是:1
其第一个子节点的值是:null
obj节点名称是:A
其第一个子节点的名称是:STRONG
其第一个子节点的类型是:1
其第一个子节点的值是:null
※ 所以,在使用DOM方式时,务必注意操作的元素对象属性。
我们又得到一个新的元素节点。怎么办?
当然,你可以继续往下寻找strong标记的子节点(一个文本节点),肯定可以得到相关的值。但万一链接文本内容再次改变,代码就又要跟着修改。所以,这明显不是一种好办法。
这时,我们可借助span标记和innerHTML,把链接文本内容放在span标记里面,HTML代码改为:
<a href="#" onclick="showhref(this);return false;"><span><strong>点击</strong></span></a>
单纯这样还不行,a元素节点下的第一个子节点还是span,我们还需要借助innerHTML的帮助:
content.innerHTML = "obj节点名称是:"+obj.nodeName+"<br />";
content.innerHTML += "span第一个子节点的名称是:"+obj.firstChild.firstChild.nodeName+"<br />";
content.innerHTML += "span第一个子节点的类型是:"+obj.firstChild.firstChild.nodeType+"<br />";
content.innerHTML += "其链接内容节点的值是:"+obj.firstChild.innerHTML;
content.innerHTML += "span第一个子节点的名称是:"+obj.firstChild.firstChild.nodeName+"<br />";
content.innerHTML += "span第一个子节点的类型是:"+obj.firstChild.firstChild.nodeType+"<br />";
content.innerHTML += "其链接内容节点的值是:"+obj.firstChild.innerHTML;
结果:
引用
点击
obj节点名称是:A
span第一个子节点的名称是:STRONG
span第一个子节点的类型是:1
其链接内容节点的值是:点击
obj节点名称是:A
span第一个子节点的名称是:STRONG
span第一个子节点的类型是:1
其链接内容节点的值是:点击
※ 注意,如果不使用innerHTML,那你只能使用DOM 2的办法逐个往下遍历子节点。使用span标记是为了把整个链接文本内容归在一起,以防止再出现独立的子节点。留意,结果中“链接内容节点的值”是个整体(还是粗体)的。
3、不同标记,其值的属性值不同
正如前面所说的,span、div、a等标记,可使用其innerHTML属性获得或设置其文本值。而对于常用于form中的input、select等标记,则可以通过value值获得,它们并没有innerHTML属性。
例如:
<input name="searchStrings" type="text" id="searchStrings" />
在JavaScript中获得其值:
var searchStrings = document.getElementById("searchStrings").value;
三、关于innerHTML
在修改类似的a、p、span、div等标记时,innerHTML确实很方便,例如,如果一开始我们就使用:
content.innerHTML = "obj节点名称是:"+obj.nodeName+"<br />";
content.innerHTML += "其链接内容节点的值是:"+obj.innerHTML;
content.innerHTML += "其链接内容节点的值是:"+obj.innerHTML;
结果:
引用
点击
obj节点名称是:A
其链接内容节点的值是:点击
obj节点名称是:A
其链接内容节点的值是:点击
因为innerHTML忽略了子节点的问题,对于直接处理文字带来了很多方便。但是,因为忽略了子节点处理,对于只修改部分HTML元素时可能会带来问题。
根据前人的建议是:
引用
innerHTML只适合修改頁面上静态内容。比如div和span里的动态信息。以及在某个可显示容器内一次性显示较复杂的内容对象,比如一张表格。而对现有的对象做修改,还是应该使用DOM来解决。
还有一些具体的问题,如innerHTML自带了语法检查功能,它会自动把不完整的HTML代码补充完整等。可见:Javascript:小心使用innerHTML
四、没有关键字this
上面我们是通过this传递需要操作的对象本身,下面的HTML代码去掉了this:
<a href="#" onclick="showhref();return false;">点击</a>
JavaScript代码:
function showhref() {
var content = document.getElementById("content");
var node = document.getElementsByTagName("a");
content.innerHTML = "其链接内容节点的值是:"+node[0].innerHTML;
}
var content = document.getElementById("content");
var node = document.getElementsByTagName("a");
content.innerHTML = "其链接内容节点的值是:"+node[0].innerHTML;
}
结果:
引用
点击
其链接内容节点的值是:点击
其链接内容节点的值是:点击
注意,getElementsByTagName返回的是一个包含对应标记的元素数组,需从中选择其子元素进行实际操作。这个只有一个a元素,所以可通过node[0]指定。