DOM能够为JavaScript引擎公开文档(网页)。通过使用DOM,可以采用编程方式操作文档的结构,如图2-3所示。当编写Ajax应用时,这是一种特别有用的能力。在传统Web应用中,我们通常使用来自服务器的新的HTML流来刷新整个页面,并通过提供新的HTML来重新定义用户界面。而在Ajax应用中,用户界面的更新主要是使用DOM来完成的。Web页面中的HTML标签被组织成一个树状结构。树的根节点是标签,它代表这个文档。在它内部的标签代表文档的主体部分,是可见的文档结构的根节点。在文档主体之内,有表格、段落、列表以及其他的标签类型,每个标签之中还可能有其他标签。
Web页面的DOM表示也是一个树状结构,由元素或节点组成,节点还可能包含很多的子节点。JavaScript引擎通过全局变量document公开当前Web页面的根节点,这个变量是所有DOM操作的起点。DOM元素通过W3C规约来定义。它有一个父元素,没有或者有多个子元素,有任意多个作为关联数组来存储的属性(也就是说,使用width或style这样的文本形式的键,而不是使用数字索引)。图2-3展示了代码清单2-2中文档一个抽象结构,这个图是用Mozilla DOM检查器工具生成的(详见附录A)。
DOM中元素之间的关系可以看作是HTML清单的镜像。这种关系是双向的,修改DOM将会改变HTML标记,随之会反映在页面的显示上。
这提供了一种高层视图,使得我们对于DOM看起来像什么有一个直观的了解。在后续的小节中,我们将考察DOM为JavaScript解释器公开了哪些接口,并且学会如何使用它。
2.4.1 使用JavaScript操作DOM
在任何应用中,我们都需要在用户的使用过程中改变用户界面,为用户执行的操作和完成的进度提供反馈。这些反馈包括修改单个元素的标签或颜色、弹出临时的对话框、使用一组全新的UI组件替换大部分的屏幕内容等等。到目前为止,最常见的方式就是,通过提供给浏览器一段声明式的HTML来构造DOM树(换句话说,也就是编写HTML页面)。
我们在代码清单2-2和图2-3中显示的文档有点太大和太复杂了,还是从小的步骤开始做DOM操作吧。假设我们要向用户显示友好的问候。当页面第一次加载的时候,我们并不知道他的名字,因此想要在稍后修改页面的结构,以添加上用户的名字,通过以编程方式操作DOM节点可以做这件事。代码清单2-3展示了这个简单页面初始的HTML标记。
代码清单2-3 Ajax的“hello”页面
我们来看一下链接到的资源。通过修改字体和颜色,样式表为区分清单中不同类别的条目定义了一些简单的样式(代码清单2-4)。 代码清单2-4 hello.css 我们定义了两个样式来描述最初的DOM节点(样式的名称可以是任意的,我们取这两个名字是为了使例子容易理解,当然也可以给它们取名为fred和jim)。这些样式类都没有在HTML中用到,但是我们将以编程方式将它们应用在元素上。代码清单2-5显示了伴随代码清单2-4的Web页面的JavaScript。当加载文档时,我们将以编程方式为一个已有的节点设置样式,并且以编程方式创建一些新的DOM元素。 代码清单2-5 hello.js JavaScript代码比HTML或样式表更加复杂。代码的入口点是window.onload()函数,一旦整个页面加载完毕,浏览器就会调用这个函数。这个时候DOM树已经建造好,可以对它进行操作。代码清单2-5利用一些DOM操作方法来改变DOM节点的属性,显示和隐藏某些节点,甚至在运行时创建一些新节点。我们这里没有覆盖所有的DOM操作方法(可以查看“资源”一节获得所有的DOM操作方法)。在下面几个小节中,我们将深入讨论一些最有用的方法。 2.4.2 寻找DOM节点 用JavaScript操作DOM的第一件事就是找到要修改的元素。前面已经提到,我们开始只能得到根节点的一个引用,它保存在全局变量document中。DOM中的每一个节点都是document的子节点(或孙节点、曾孙节点等等),但是要在大型的复杂文档中,一步一步地缓慢搜寻是件体力活。幸运的是,我们可以走一些捷径。最常用的方法就是给元素附加唯一的ID。在代码清单2-5的onload()函数中,我们想要寻找两个元素:段落元素,我们为它设置样式;空的
和
任何一个DOM节点都可以分配一个ID,用来在程序中通过一个函数调用获得这个节点的引用,无论它在文档中的什么位置: var hello=document.getElementById('hello'); 注意,这是Document对象的一个方法。在一个如上所述的简单情况中(以及在很多复杂的情况中),可以通过document访问当前的Document对象。如果你使用了IFrame(我们将会在后面讨论),那么可能需要跟踪多个Document对象,并确定正在查询的是哪个Document对象。 在一些情况下,我们确实需要一步一步地搜索DOM树。因为DOM节点是以树形结构来组织的,每一个DOM节点都只有不多于一个的父节点,但是可以有任意多个子节点。可以通过parentNode和childNodes来访问它们。parentNode返回另外一个DOM节点,而childNodes返回一个JavaScript节点数组,可以对其遍历,即: var children=empty.childNodes; for (var i=0;i ... } 即便在文档中的某个节点上没有附加唯一ID,我们仍有第三种方法可以方便地定位节点。那就是,使用getElementsByTagName()方法,基于HTML标签的类型搜索DOM节点。例如,document.getElementsByTagName("UL")会返回一个包含文档中所有
这些方法对于操作那些我们几乎无法控制的文档[2]来说是很有用的。作为通常的规则,使用getElementById()要比使用getElementsByTagName()更加安全,因为前者对于文档结构和顺序的假设更少一些,这样文档结构可以独立于代码而变化。 2.4.3 创建DOM节点 除了重组已有的DOM节点,一些情况下还需要创建新的节点并把它们添加到文档中(例如,在运行时创建消息框)。DOM的JavaScript实现也为我们提供了相应的方法。 我们再次考察一下代码清单2-5的例子代码。ID为'empty'的DOM节点实际上是从空节点开始的。当加载这个页面的时候,我们为它动态创建了一些内容。addNode()函数使用了标准的document.createElement()和document.createTextNode()方法。createElement()可以用来创建任何HTML元素,带有一个标签类型参数,例如: var childEl=document.createElement("div"); createTextNode()创建了一个代表一段文本的DOM节点,这样的节点通常嵌入在标题、div、段落以及列表条目等标签之中。 var txtNode=document.createTextNode("some text"); DOM标准将文本节点和代表HTML元素的节点区别开来,分别对待。不能直接对文本节点应用样式,因此也就少占用一些内存。不过,我们仍然可以通过包含这个文本节点的DOM元素来设置该节点所表示的文本的样式。 无论何种类型的节点一旦被创建,都必须将它附加到文档之上,然后才能在浏览器窗口中见到它。DOM节点的appendChild()方法就是用来完成这个工作的: el.appendChild(childEl); createElement()、createTextNode()和appendChild()这三个方法为我们提供了向一个文档中添加新的结构所需要的一切。完成了这个工作,我们通常还想要以一种适当的方式为新的结构设置样式。我们来考察一下如何做这件事。 2.4.4 为文档增加样式 到目前为止,我们已经考察了使用DOM来操作文档的结构(一个元素如何被另外一个元素所包含,诸如此类)。这使得我们可以有效地改造在静态HTML中声明的结构。DOM还提供了另外一类方法,允许以编程方式修改元素的样式和改造定义在样式表中的结构。 通过DOM操作,Web页面上的每一个元素都可以拥有多种视觉样式,例如位置、高度和宽度、颜色、边框和空白。尽管分别修改每一个属性可以更加精细地控制元素的外观,但是这样做是很单调乏味的。幸运的是,Web浏览器为我们所提供的JavaScript绑定除了提供底层接口以便精确操作之外,也允许通过CSS类来为元素设置一致的样式。我们来逐个考察一下。 1. className属性 CSS提供了一种简明的方式来将预先定义的、可重用的样式应用到文档中。当我们为在代码中创建的元素设置样式的时候,也可以通过设置DOM节点的className属性来利用CSS。例如,下面一行为一个节点设置了通过declared类定义的显示规则: hello.className='declared'; 其中hello是到一个DOM节点的引用。这提供了一种容易和紧凑的方法,来为一个节点同时分配很多的CSS规则,并且可以通过样式表来管理复杂的样式。 2. style属性 在其他一些情况下,我们想要细粒度地改变特定元素的样式,也许仅仅是作为已经通过CSS应用的样式的补充。 DOM节点还包含了一个名为style的关联数组,其中包含了节点样式的全部细节。正如图2-4所示,DOM节点的样式中通常包含有大量的条目。鲜为人知的是,为节点分配className也会改变style数组的值。 图2-4 在DOM检查器中检查DOM节点的style属性。大部分的值都不是由用户显式设置的,而是由呈现引擎自己设置的。注意旁边的滚动条:我们只看到了全部已计算样式列表的大约四分之一的内容 style数组也可以直接操作。在为empty节点中的条目设置样式后,我们可以给它加一个边框: empty.style.border="solid green 2px"; empty.style.width="200px"; 我们也可以很容易地仅仅声明一个box类,然后通过修改className属性来应用样式,但是这种方法在某些特定环境下用起来更加便捷,并且它还允许以编程方式构造字符串。例如,我们想要以像素的精度自由地改变元素的大小,如果为此预先定义从1到800像素每个宽度的样式,那显然是低效和笨拙的。 使用上面的方法,我们可以创建新的DOM元素,并且为它们设置样式。在内容处理技术的工具箱中,还有另外一个很有用的工具,它使用一种略微不同的方法来通过程序编写Web页面,这就是InnerHTML属性,我们通过考察这个属性来结束本节。 2.4.5 捷径:使用innerHTML属性 到目前为止,我们所描述的方法通过使用DOM API提供了对页面结构进行低层次控制的能力。然而,对于创建一个文档来说,createElement()和appendChild()提供的API相当冗长,它最适合于以下场合:文档遵循一种有规律的结构来创建,这种结构可以编码为一种算法。所有流行的Web浏览器的DOM元素都支持称作innerHTML的属性,允许以一种非常简单的方式来为元素分配任意的内容。innerHTML是一个字符串,以HTML标记的形式表示一个节点中的内容。例如,我们可以使用innerHTML来重写addNode()函数,就像这样: function addListItemUsingInnerHTML(el,text){ el.innerHTML+="
}
到这里我们已经讲述了JavaScript、CSS和DOM。它们在动态HTML这个名字首次出现时就在一起使用了。正如在本章前面的介绍中提到的,Ajax使用了很多动态HTML的技术,但是Ajax令人耳目一新之处在于它向这个组合中加入了新的成分。在下面小节中,我们来考察一下Ajax与DHTML的区别所在——在用户使用的同时与服务器通信的能力。 
我们添加了两个到外部资源的引用:CSS
和包含JavaScript代码的文件
。我们还声明了带ID的空
,以后可以通过编程方式向其中添加更多的元素。
![]()
![]()


标签的数组。








