JavaScript高级程序设计之DOM之节点层次之Element类型第10.1.3讲

 
除了Document 类型之外,Element 类型就要算是Web 编程中最常用的类型了。Element 类型用
于表现XML 或HTML 元素,提供了对元素标签名、子节点及特性的访问。Element 节点具有以下特征:
nodeType 的值为1;
nodeName 的值为元素的标签名;
nodeValue 的值为null;
parentNode 可能是Document 或Element;
其子节点可能是Element、Text、Comment、ProcessingInstruction、CDATASection 或
EntityReference。
要访问元素的标签名,可以使用nodeName 属性,也可以使用tagName 属性;这两个属性会返回
相同的值(使用后者主要是为了清晰起见)。以下面的元素为例:
<div id="myDiv"></div>
可以像下面这样取得这个元素及其标签名:
var div = document.getElementById("myDiv");
alert(div.tagName); //"DIV"
alert(div.tagName == div.nodeName); //true
这里的元素标签名是div,它拥有一个值为"myDiv"的ID。可是,div.tagName 实际上输出的是
"DIV"而非"div"。在HTML 中,标签名始终都以全部大写表示;而在XML(有时候也包括XHTML)
中,标签名则始终会与源代码中的保持一致。假如你不确定自己的脚本将会在HTML 还是XML 文档中

执行,最好是在比较之前将标签名转换为相同的大小写形式,如下面的例子所示:

if (element.tagName == "div"){ //不能这样比较,很容易出错!
//在此执行某些操作
}
if (element.tagName.toLowerCase() == "div"){ //这样最好(适用于任何文档)
//在此执行某些操作
}
这个例子展示了围绕tagName 属性的两次比较操作。第一次比较非常容易出错,因为其代码在
HTML 文档中不管用。第二次比较将标签名转换成了全部小写,是我们推荐的做法,因为这种做法适用
于HTML 文档,也适用于XML 文档。
可以在任何浏览器中通过脚本访问Element 类型的构造函数及原型,包括IE8 及
之前版本。在Safari 2 之前版本和Opera 8 之前的版本中,不能访问Element 类型的构
造函数。
1. HTML
元素
所有HTML 元素都由HTMLElement 类型表示,不是直接通过这个类型,也是通过它的子类型来表
示。HTMLElement 类型直接继承自Element 并添加了一些属性。添加的这些属性分别对应于每个HTML
元素中都存在的下列标准特性。
id,元素在文档中的唯一标识符。
title,有关元素的附加说明信息,一般通过工具提示条显示出来。
lang,元素内容的语言代码,很少使用。
dir,语言的方向,值为"ltr"(left-to-right,从左至右)或"rtl"(right-to-left,从右至左),
也很少使用。
className,与元素的class 特性对应,即为元素指定的CSS 类。没有将这个属性命名为class,
是因为class 是ECMAScript 的保留字(有关保留字的信息,请参见第1 章)。
上述这些属性都可以用来取得或修改相应的特性值。以下面的HTML 元素为例:
<div id="myDiv" class="bd" title="Body text" lang="en" dir="ltr"></div>
元素中指定的所有信息,都可以通过下列JavaScript 代码取得:

var div = document.getElementById("myDiv");
alert(div.id); //"myDiv""
alert(div.className); //"bd"
alert(div.title); //"Body text"
alert(div.lang); //"en"
alert(div.dir); //"ltr"
当然,像下面这样通过为每个属性赋予新的值,也可以修改对应的每个特性:

div.id = "someOtherId";
div.className = "ft";
div.title = "Some other text";
div.lang = "fr";
div.dir ="rtl";

并不是对所有属性的修改都会在页面中直观地表现出来。对id 或lang 的修改对用户而言是透明
不可见的(假设没有基于它们的值设置的CSS 样式),而对title 的修改则只会在鼠标移动到这个元素
之上时才会显示出来。对dir 的修改会在属性被重写的那一刻,立即影响页面中文本的左、右对齐方式。
修改className 时,如果新类关联了与此前不同的CSS 样式,那么就会立即应用新的样式。
前面提到过,所有HTML 元素都是由HTMLElement 或者其更具体的子类型来表示的。下表列出了
所有HTML 元素以及与之关联的类型(以斜体印刷的元素表示已经不推荐使用了)。注意,表中的这些
类型在Opera、Safari、Chrome 和Firefox 中都可以通过JavaScript 访问,但在IE8 之前的版本中不能通
过JavaScript 访问。



表中的每一种类型都有与之相关的特性和方法。本书将会讨论其中很多类型。
2.
取得特性
每个元素都有一或多个特性,这些特性的用途是给出相应元素或其内容的附加信息。操作特性的
DOM 方法主要有三个,分别是getAttribute()、setAttribute()和removeAttribute()。这三
个方法可以针对任何特性使用,包括那些以HTMLElement 类型属性的形式定义的特性。来看下面的例子:

var div = document.getElementById("myDiv");
alert(div.getAttribute("id")); //"myDiv"
alert(div.getAttribute("class")); //"bd"
alert(div.getAttribute("title")); //"Body text"
alert(div.getAttribute("lang")); //"en"
alert(div.getAttribute("dir")); //"ltr"
注意,传递给getAttribute()的特性名与实际的特性名相同。因此要想得到class 特性值,应
该传入"class"而不是"className",后者只有在通过对象属性访问特性时才用。如果给定名称的特性
不存在,getAttribute()返回null。
通过getAttribute()方法也可以取得自定义特性(即标准HTML 语言中没有的特性)的值,以
下面的元素为例:

<div id="myDiv" my_special_attribute="hello!"></div>
这个元素包含一个名为my_special_attribute 的自定义特性,它的值是"hello!"。可以像取
得其他特性一样取得这个值,如下所示:

var value = div.getAttribute("my_special_attribute");
过,特性的名称是不区分大小写的,即"ID"和"id"代表的都是同一个特性。另外也要注意,根
据HTML5 规范,自定义特性应该加上data-前缀以便验证。

任何元素的所有特性,也都可以通过DOM 元素本身的属性来访问。当然,HTMLElement 也会有5
个属性与相应的特性一一对应。不过,只有公认的(非自定义的)特性才会以属性的形式添加到DOM
对象中。以下面的元素为例:

<div id="myDiv" align="left" my_special_attribute="hello!"></div>
因为id 和align 在HTML 中是<div>的公认特性,因此该元素的DOM 对象中也将存在对应的属
性。不过,自定义特性my_special_attribute 在Safari、Opera、Chrome 及Firefox 中是不存在的;
但IE 却会为自定义特性也创建属性,如下面的例子所示:

alert(div.id); //"myDiv"
alert(div.my_special_attribute); //undefined(IE 除外)
alert(div.align); //"left"
有两类特殊的特性,它们虽然有对应的属性名,但属性的值与通过getAttribute()返回的值并不
相同。第一类特性就是style,用于通过CSS 为元素指定样式。在通过getAttribute()访问时,返
回的style 特性值中包含的是CSS 文本,而通过属性来访问它则会返回一个对象。由于style 属性是
用于以编程方式访问元素样式的(本章后面讨论),因此并没有直接映射到style 特性。
第二类与众不同的特性是onclick 这样的事件处理程序。当在元素上使用时,onclick 特性中包
含的是JavaScript 代码,如果通过getAttribute()访问,则会返回相应代码的字符串。而在访问
onclick 属性时,则会返回一个JavaScript 函数(如果未在元素中指定相应特性,则返回null)。这是
因为onclick 及其他事件处理程序属性本身就应该被赋予函数值。
由于存在这些差别,在通过JavaScript 以编程方式操作DOM 时,开发人员经常不使用getAttri-
bute(),而是只使用对象的属性。只有在取得自定义特性值的情况下,才会使用getAttribute()方法。

在IE7 及以前版本中,通过getAttribute()方法访问style 特性或onclick 这样
的事件处理特性时,返回的值与属性的值相同。换句话说,getAttribute("style")返
回一个对象,而getAttribute("onclick")返回一个函数。虽然IE8 已经修复了这个
bug,但不同IE版本间的不一致性,也是导致开发人员不使用getAttribute()访问HTML
特性的一个原因。

3.
设置特性
与getAttribute()对应的方法是setAttribute(),这个方法接受两个参数:要设置的特性名和
值。如果特性已经存在,setAttribute()会以指定的值替换现有的值;如果特性不存在,setAttribute()
则创建该属性并设置相应的值。来看下面的例子:

div.setAttribute("id", "someOtherId");
div.setAttribute("class", "ft");
div.setAttribute("title", "Some other text");
div.setAttribute("lang","fr");
div.setAttribute("dir", "rtl");
通过setAttribute()方法既可以操作HTML 特性也可以操作自定义特性。通过这个方法设置的
特性名会被统一转换为小写形式,即"ID"最终会变成"id"。
因为所有特性都是属性,所以直接给属性赋值可以设置特性的值,如下所示。

div.id = "someOtherId";
div.align = "left";
不过,像下面这样为DOM 元素添加一个自定义的属性,该属性不会自动成为元素的特性。

div.mycolor = "red";
alert(div.getAttribute("mycolor")); //null(IE 除外)

这个例子添加了一个名为mycolor 的属性并将它的值设置为"red"。在大多数浏览器中,这个属
性都不会自动变成元素的特性,因此想通过getAttribute()取得同名特性的值,结果会返回null。
可是,自定义属性在IE 中会被当作元素的特性,反之亦然。


在IE7 及以前版本中,setAttribute()存在一些异常行为。通过这个方法设置
class 和style 特性,没有任何效果,而使用这个方法设置事件处理程序特性时也
一样。尽管到了IE8 才解决这些问题,但我们还是推荐通过属性来设置特性。

要介绍的最后一个方法是removeAttribute(),这个方法用于彻底删除元素的特性。调用这个方
法不仅会清除特性的值,而且也会从元素中完全删除特性,如下所示:

div.removeAttribute("class");
这个方法并不常用,但在序列化DOM 元素时,可以通过它来确切地指定要包含哪些特性。
IE6 及以前版本不支持removeAttribute()。
4. attributes 属性
Element 类型是使用attributes 属性的唯一一个DOM 节点类型。attributes 属性中包含一个
NamedNodeMap,与NodeList 类似,也是一个“动态”的集合。元素的每一个特性都由一个Attr 节
点表示,每个节点都保存在NamedNodeMap 对象中。NamedNodeMap 对象拥有下列方法。
getNamedItem(name):返回nodeName 属性等于name 的节点;
removeNamedItem(name):从列表中移除nodeName 属性等于name 的节点;
setNamedItem(node):向列表中添加节点,以节点的nodeName 属性为索引;
item(pos):返回位于数字pos 位置处的节点。
attributes 属性中包含一系列节点,每个节点的nodeName 就是特性的名称,而节点的nodeValue
就是特性的值。要取得元素的id 特性,可以使用以下代码。
var id = element.attributes.getNamedItem("id").nodeValue;
以下是使用方括号语法通过特性名称访问节点的简写方式。
var id = element.attributes["id"].nodeValue;
也可以使用这种语法来设置特性的值,即先取得特性节点,然后再将其nodeValue 设置为新值,
如下所示。
element.attributes["id"].nodeValue = "someOtherId";
调用removeNamedItem()方法与在元素上调用removeAttribute()方法的效果相同——直接删
除具有给定名称的特性。下面的例子展示了两个方法间唯一的区别,即removeNamedItem()返回表示
被删除特性的Attr 节点。
var oldAttr = element.attributes.removeNamedItem("id");
最后,setNamedItem()是一个很不常用的方法,通过这个方法可以为元素添加一个新特性,为此
需要为它传入一个特性节点,如下所示。

element.attributes.setNamedItem(newAttr);
一般来说,由于前面介绍的attributes 的方法不够方便,因此开发人员更多的会使用
getAttribute()、removeAttribute()和setAttribute()方法。
不过,如果想要遍历元素的特性,attributes 属性倒是可以派上用场。在需要将DOM 结构序列
化为XML 或HTML 字符串时,多数都会涉及遍历元素特性。以下代码展示了如何迭代元素的每一个特
性,然后将它们构造成name="value" name="value"这样的字符串格式。

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(" ");
}
这个函数使用了一个数组来保存名值对,最后再以空格为分隔符将它们拼接起来(这是序列化长字
符串时的一种常用技巧)。通过attributes.length 属性,for 循环会遍历每个特性,将特性的名称
和值输出为字符串。关于以上代码的运行结果,以下是两点必要的说明。
针对attributes 对象中的特性,不同浏览器返回的顺序不同。这些特性在XML 或HTML 代
码中出现的先后顺序,不一定与它们出现在attributes 对象中的顺序一致。
IE7 及更早的版本会返回HTML 元素中所有可能的特性,包括没有指定的特性。换句话说,返
回100 多个特性的情况会很常见。
针对IE7 及更早版本中存在的问题,可以对上面的函数加以改进,让它只返回指定的特性。每个特
性节点都有一个名为specified 的属性,这个属性的值如果为true,则意味着要么是在HTML 中指
定了相应特性,要么是通过setAttribute()方法设置了该特性。在IE 中,所有未设置过的特性的该
属性值都为false,而在其他浏览器中根本不会为这类特性生成对应的特性节点(因此,在这些浏览器
中,任何特性节点的specified 值始终为true)。改进后的代码如下所示。
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(" ");
}

这个经过改进的函数可以确保即使在IE7 及更早的版本中,也会只返回指定的特性。
5.
创建元素
使用document.createElement()方法可以创建新元素。这个方法只接受一个参数,即要创建元
素的标签名。这个标签名在HTML 文档中不区分大小写,而在XML(包括XHTML)文档中,则是区
分大小写的。例如,使用下面的代码可以创建一个<div>元素。

var div = document.createElement("div");

在使用createElement()方法创建新元素的同时,也为新元素设置了ownerDocuemnt 属性。此
时,还可以操作元素的特性,为它添加更多子节点,以及执行其他操作。来看下面的例子。

div.id = "myNewDiv";
div.className = "box";
在新元素上设置这些特性只是给它们赋予了相应的信息。由于新元素尚未被添加到文档树中,因此
设置这些特性不会影响浏览器的显示。要把新元素添加到文档树,可以使用appendChild()、inser-
tBefore()或replaceChild()方法。下面的代码会把新创建的元素添加到文档的<body>元素中。

document.body.appendChild(div);
一旦将元素添加到文档树中,浏览器就会立即呈现该元素。此后,对这个元素所作的任何修改都会
实时反映在浏览器中。
在IE 中可以以另一种方式使用createElement(),即为这个方法传入完整的元素标签,也可以包
含属性,如下面的例子所示。

var div = document.createElement("<div id=\"myNewDiv\" class=\"box\"></div >");
这种方式有助于避开在IE7 及更早版本中动态创建元素的某些问题。下面是已知的一些这类问题。
不能设置动态创建的<iframe>元素的name 特性。
不能通过表单的reset()方法重设动态创建的<input>元素(第13 章将讨论reset()方法)。

动态创建的type 特性值为"reset"的<buttou>元素重设不了表单。
动态创建的一批name 相同的单选按钮彼此毫无关系。name 值相同的一组单选按钮本来应该用
于表示同一选项的不同值,但动态创建的一批这种单选按钮之间却没有这种关系。
上述所有问题都可以通过在createElement()中指定完整的HTML 标签来解决,如下面的例子所示。

if (client.browser.ie && client.browser.ie <=7){
//创建一个带name 特性的iframe 元素
var iframe = document.createElement("<iframe name=\"myframe\"></iframe>");
//创建input 元素
var input = document.createElement("<input type=\"checkbox\">");
//创建button 元素
var button = document.createElement("<button type=\"reset\"></button>");
//创建单选按钮
var radio1 = document.createElement("<input type=\"radio\" name=\"choice\" "+
"value=\"1\">");
var radio2 = document.createElement("<input type=\"radio\" name=\"choice\" "+
"value=\"2\">");
}
与使用createElement()的惯常方式一样,这样的用法也会返回一个DOM 元素的引用。可以将
这个引用添加到文档中,也可以对其加以增强。但是,由于这样的用法要求使用浏览器检测,因此我们
建议只在需要避开IE 及更早版本中上述某个问题的情况下使用。其他浏览器都不支持这种用法。
6.
元素的子节点
元素可以有任意数目的子节点和后代节点,因为元素可以是其他元素的子节点。元素的
childNodes 属性中包含了它的所有子节点,这些子节点有可能是元素、文本节点、注释或处理指令。
不同浏览器在看待这些节点方面存在显著的不同,以下面的代码为例。

<ul id="myList">
<li>Item 1</li>
<li>Item 2</li>
<li>Item 3</li>
</ul>
如果是IE 来解析这些代码,那么<ul>元素会有3 个子节点,分别是3 个<li>元素。但如果是在其
他浏览器中,<ul>元素都会有7 个元素,包括3 个<li>元素和4 个文本节点(表示<li>元素之间的空
白符)。如果像下面这样将元素间的空白符删除,那么所有浏览器都会返回相同数目的子节点。

<ul id="myList"><li>Item 1</li><li>Item 2</li><li>Item 3</li></ul>
对于这段代码,<ul>元素在任何浏览器中都会包含3 个子节点。如果需要通过childNodes 属性
遍历子节点,那么一定不要忘记浏览器间的这一差别。这意味着在执行某项操作以前,通常都要先检查
一下nodeTpye 属性,如下面的例子所示。
for (var i=0, len=element.childNodes.length; i < len; i++){
if (element.childNodes[i].nodeType == 1){
//执行某些操作
}
}
这个例子会循环遍历特定元素的每一个子节点,然后只在子节点的nodeType 等于1(表示是元素
节点)的情况下,才会执行某些操作。
如果想通过某个特定的标签名取得子节点或后代节点该怎么办呢?实际上,元素也支持
getElementsByTagName()方法。在通过元素调用这个方法时,除了搜索起点是当前元素之外,其他
方面都跟通过document 调用这个方法相同,因此结果只会返回当前元素的后代。例如,要想取得前面
<ul>元素中包含的所有<li>元素,可以使用下列代码。

var ul = document.getElementById("myList");
var items = ul.getElementsByTagName("li");

要注意的是,这里<ul>的后代中只包含直接子元素。不过,如果它包含更多层次的后代元素,那
么各个层次中包含的<li>元素也都会返回。













  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值