利用DOM进行Web响应

日期: 2008-07-07 作者:Brett McLaughlin 来源:TechTarget中国

  程序员(使用后端应用程序)和Web程序员(编写HTML、CSS和JavaScript上)之间的分水岭是长久存在的。但是,Document Object Model (DOM)弥补了这个裂缝,使得在后端使用XML同时在前端使用HML切实可行,并成为极其有效的工具。在本文中,Brett McLaughlin介绍了Document Object Model,解释它在Web页面中的应用,并开始挖掘其在JavaScript中的用途。


  与许多Web程序员一样,您可能使用过HTML。HTML是程序员开始与Web页面打交道的方式;HTML通常是他们完成应用程序或站点前的最后一步——调整一些布局、颜色或样式。不过,虽然经常使用HTML,但对于HTML转到浏览器呈现在屏幕上时到底发生了什么,人们普遍存在误解。在我分析您认为可能发生的事情及其可能错误的原因之前,我希望您对设计和服务Web页面时涉及的过程一清二楚:


  ·一些人(通常是您!)在文本编辑器或IDE中创建HTML。
  ·然后您将HTML上载到Web服务器,例如Apache HTTPD,并将其公开在Internet或intranet 上。
  ·用户用Firefox或SafariA等浏览器请求您的Web页面。
  ·用户的浏览器向您的服务器请求HTML。
  ·浏览器将从服务器接收到的页面以图形和文本方式呈现;用户看到并激活Web页面。


  这看起来非常基础,但事情很快会变得有趣起来。事实上,步骤4和步骤5之间发生的巨大数量的“填充物(stuff)”就是本文的焦点。术语 “填充物” 也十分适用,因为多数程序员从来没有真正考虑过当用户浏览器请求显示标记时到底在标记身上发生了什么。


  ·是否浏览器只是读取HTML中的文本并将其显示?
  ·CSS呢?尤其是当CSS位于外部文件时。
  ·JavaScript呢?它也通常位于外部文件中。
  ·浏览器如何处理这些项,如果将事件处理程序、函数和样式映射到该文本标记?


  实践证明,所有这些问题的答案都是Document Object Model。因此,废话少说,直接研究DOM。


  Web程序员和标记


  对于多数程序员,当Web浏览器开始时他们的工作就结束了。也就是说,将一个HTML文件放入Web浏览器的目录上后,您通常就认为它已经“完成”,而且(满怀希望地)认为再也不会考虑它!说到编写干净、组织良好的页面时,这也是一个伟大的目标;希望您的标记跨浏览器、用各种版本的CSS和JavaScript显示它应该显示的内容,一点错都没有。


  问题是这种方法限制了程序员对浏览器中真正发生的事情的理解。更重要的是,它限制了您用客户端JavaScript动态更新、更改和重构Web页面的能力。摆脱这种限制,让您的Web站点拥有更大的交互性和创造性。


  程序员做什么


  作为典型的Web程序员,您可能启动文本编辑和IDE后就开始输入HTML、CSS甚至JavaScript。很容易认为这些标记、选择器和属性只是使站点正确显示而做的小小的任务。但是,在这一点上您需要拓展您的思路,要意识到您是在组织您的内容。不要担心;我保证这不会变成关于标记美观、您必须如何认识到Web页面的真正潜力或其他任何元物质的讲座。您需要了解的是您在Web开发中到底是什么角色。


  说到页面的外观,顶多您只能提提建议。您提供CSS样式表时,用户可以覆盖您的样式选择。您提供字体大小时,用户浏览器可以为视障者更改这些大小,或者在大显示器(具有同等大的分辨率)上按比例缩小。甚至您选择的颜色和字体也受制于用户显示器和用户在其系统上安装的字体。虽然尽您所能来设计页面样式很不错,但这绝不是 您对Web页面的最大影响。


  您绝对控制的是Web页面的结构。您的标记不可更改,用户就不能乱弄;他们的浏览器只能从您的Web服务器检索标记并显示它(虽然样式更符合用户的品味而不是您自己的品味)。但页面组织,不管是在该段落内还是在其他分区,都只由您单独决定。要是想实际更改您的页面(这是大多数Ajax应用程序所关注的),您操作的是页面的结构。尽管很容易更改一段文本的颜色,但在现有页面上添加文本或整个区段要难得多。不管用户如何设计该区段的样式,都是由您控制页面本身的组织。


  标记做什么


  一旦意识到您的标记是真正与组织相关的,您就会对它另眼相看了。不会认为h1导致文本是大字号、黑色、粗体的,而会认为h1是标题。用户如何看待这个问题以及他们是使用您的CSS、他们自己的CSS还是这两者的组合,这是次要的考虑事项。相反,要意识到只有标记才能提供这种级别的组织;p指明文本在段落内,img表示图像,div将页面分成区段,等等。


  还应该清楚,样式和行为(事件处理程序和JavaScript)是在事后 应用于该组织的。标记就绪以后才能对其进行操作或设计样式。所以,正如您可以将CSS保存在HTML的外部文件中一样,标记的组织与其样式、格式和行为是分离的。虽然您肯定可以用JavaScript更改元素或文本的样式,但实际更改您的标记所布置的组织却更加有趣。


  只要牢记您的标记只为您的页面提供组织、框架,您就能立于不败之地。再前进一小步,您就会明白浏览器是如何接受所有的文本组织并将其转变为超级有趣的一些东西的,即一组对象,其中每个对象都可被更改、添加或删除。


  文本标记的优点


  在讨论Web浏览器之前,值得考虑一下为什么纯文本绝对 是存储HTML的最佳选择(有关详细信息,请参阅 有关标记的一些其他想法)。不考虑优缺点,只是回忆一下在每次查看页面时HTML是通过网络发送到Web浏览器的(为了简洁,不考虑高速缓存等)。真是再没有比传递文本再有效的方法了。二进制对象、页面图形表示、重新组织的标记块等等,所有这一切都比纯文本文件通过网络传递要更困难。


  此外,浏览器也为此增光添彩。今天的浏览器允许用户更改文本大小、按比例伸缩图像、下载页面的CSS或JavaScript(大多数情况),甚至更多,这完全排除了将任何类型的页面图形表示发送到浏览器上。但是,浏览器需要原HTML,这样它才能在浏览器中对页面应用任何处理,而不是信任浏览器去处理该任务。同样地,将CSS从JavaScript分离和将CSS从HTML标记分离要求一种容易分离的格式。文本文件又一次成为该任务的最好方法。


  最后但同样重要的一点是,记住,新标准(比如HTML 4.01与XHTML 1.0和1.1)承诺将内容(页面中的数据)与表示和样式(通常由CSS应用)分离。如果程序员要将HTML与CSS分离,然后强制浏览器检索粘结页面各部分的一些页面表示,这会失去这些标准的多数优点。保持这些部分到达浏览器时都一直分离使得浏览器在从服务器获取HTML时有了前所未有的灵活性。


  进一步了解Web浏览器


  对于一些Web程序员来说,在前文阅读到的所有内容可能是对您在Web开发过程中角色的滑稽讲述。但说到浏览器的行为,许多最能干的Web设计人员和开发人员通常都没有意识到“内幕”的实际状况。我将在本节重点进行讲述。不要担心,代码很快就到,但是要克制您编码的急躁心情,因为真正了解Web浏览器的确切行为对于您的代码正确工作是非常关键的。


  文本标记的缺点


  正如文本标记对于设计人员和页面创建者具有惊人的优点之外,它对于浏览器也具有相当出奇的缺点。具体来说,浏览器很难直接将文本标记可视地表示给用户(详细信息请参阅 有关标记的一些其他想法)。考虑下列常见的浏览器任务:


  ·基于元素类型、类、ID及其在HTML文档中的位置,将CSS样式(通常来自外部文件中的多个样式表)应用于标记。
  ·基于JavaScript代码(通常位于外部文件)将样式和格式应用于HTML文档的不同部分。
  ·基于JavaScript代码更改表单字段的值。
  ·基于JavaScript代码,支持可视效果,比如图像翻转和图像交换。


  复杂性并不在于编码这些任务;其中每件事都是相当容易的。复杂性来自实际实现请求动作的浏览器。如果标记存储为文本,比如,想要在center-text类的p元素中输入文本(text-align: center),如何实现呢?


  ·将内联样式添加到文本吗?
  ·将样式应用到浏览器中的HTML文本,并只保持内容居中或不居中?
  ·应用无样式的HTML,然后事后应用格式?


  这些非常困难的问题是如今很少有人编写浏览器的原因。(编写浏览器的人应该接受最由衷的感谢)


  无疑,纯文本不是存储浏览器HTML的好办法,尽管文本是获取页面标记最好的解决方案。如果加上JavaScript更改 页面结构的能力,事情就变得有些微妙了。浏览器应该将修改过的结构重新写入磁盘吗?如何才能保持文档的最新版本呢?


  无疑,文本不是答案。它难以修改,为其应用样式和行为很困难,与今天Web页面的动态本质在根本上相去甚远。


  求助于树视图


  这个问题的答案(至少是由当今Web浏览器选择的答案)是使用树结构来表示HTML。参见清单1,这是一个表示为本文标记的相当简单又无聊的HTML页面。


  清单1. 文本标记中的简单HTML页面


  <html>
   <head>
    <title>Trees, trees, everywhere</title>
   </head>
   <body>
    <h1>Trees, trees, everywhere</h1>
    <p>Welcome to a <em>really</em> boring page.</p>
    <div>
      Come again soon.
      <img src=”come-again.gif” />
    </div>
   </body>
  </html>
 
  浏览器接受该页面并将之转换为树形结构,如图1所示。



  图1. 清单1的树视图
 
  为了保持本文的进度,我做了少许简化。DOM或XML方面的专家会意识到空白对于文档文本在Web浏览器树结构中表示和分解方式的影响。肤浅的了解只会使事情变得模棱两可,所以如果想弄清空白的影响,那最好不过了;如果不想的话,那可以继续读下去,不要考虑它。当它成为问题时,那时您就会明白您需要的一切。


  除了实际的树背景之外,可能会首先注意到树中的一切是以最外层的HTML包含元素,即html元素开始的。使用树的比喻,这叫做根元素。所以即使这是树的底层,当您查看并分析树的时候,我也通常以此开始。如果它确实奏效,您可以将整个树颠倒一下,但这确实有些拓展了树的比喻。


  从根流出的线表示不同标记部分之间的关系。head和body元素是html根元素的孩子;title是head的孩子,而文本“Trees, trees, everywhere”是title的孩子。整个树就这样组织下去,直到浏览器获得与图1类似的结构。


  一些附加术语


  为了沿用树的比喻,head和body被叫做html的分支(branches)。叫分支是因为它们有自己的孩子。当到达树的末端时,您将进入主要的文本,比如“Trees, trees, everywhere”和“really”;这些通常称为叶子,因为它们没有自己的孩子。您不需要记住所有这些术语,当您试图弄清楚特定术语的意思时,只要想像一下树结构就容易多了。


  对象的值


  既然了解了一些基本的术语,现在应该关注一下其中包含元素名称和文本的小矩形了(图1)。每个矩形是一个对象;浏览器在其中解决一些文本问题。通过使用对象来表示HTML文档的每一部分,可以很容易地更改组织、应用样式、允许JavaScript访问文档,等等。


  对象类型和属性


  标记的每个可能类型都有自己的对象类型。例如,HTML中的元素用Element对象类型表示。文档中的文本用Text类型表示,属性用Attribute类型表示,以此类推。


  所以Web浏览器不仅可以使用对象模型来表示文档(从而避免了处理静态文本),还可以用对象类型立即辨别出某事物是什么。HTML文档被解析并转换为对象集合,如图1所示,然后尖括号和转义序列(例如,使用<表示<,使用 >表示 >)等事物不再是问题了。这就使得浏览器的工作(至少在解析输入HTML之后)变得更容易。弄清某事物究竟是元素还是属性并确定如何处理该类型的对象,这些操作都十分简单了。


  通过使用对象,Web浏览器可以更改这些对象的属性。例如,每个元素对象具有一个父元素和一系列子元素。所以添加新的子元素或文本只需要向元素的子元素列表中添加一个新的子元素。这些对象还具有style属性,所以快速更改元素或文本段的样式非常简单。例如,要使用JavaScript更改div的高度,如下所示:


  someDiv.style.height = “300px”;
 
  换句话说,Web浏览器使用对象属性可以非常容易地更改树的外观和结构。将之比作浏览器在内部将页面表示为文本时必须进行的复杂事情,每次更改属性或结构都需要浏览器重新编写静态文件、重新解析并在屏幕上重新显示。有了对象,所有这一切都解决了。


  现在,花点时间展开一些HTML文档并用树将其勾画出来。尽管这看起来是个不寻常的请求(尤其是在包含极少代码的这样一篇文章中),如果您希望能够操纵这些树,那么需要熟悉它们的结构。


  在这个过程中,可能会发现一些古怪的事情。比如,考虑下列情况:


  ·属性发生了什么?
  ·分解为元素(比如em和b)的文本呢?
  ·结构不正确(比如当缺少结束p标记时)的HTML呢?


  一旦熟悉这些问题之后,就能更好地理解下面几节了。


  严格有时是好事


  如果尝试刚提到的练习I,您可能会发现标记的树视图中存在一些潜在问题(如果不练习的话,那就听我说吧!)。事实上,在清单1和图1中就会发现一些问题,首先看p元素是如何分解的。如果您问通常的 Web 开发人员 “p元素的文本内容是什么”,最常见的答案将是“Welcome to a really boring Web page.”。如果将之与图1做比较,将会发现这个答案(虽然合乎逻辑)是根本不正确的。


  实际上,p元素具有三个 不同的子对象,其中没有一个包含完整的“Welcome to a really boring Web page.”文本。您会发现文本的一部分,比如“Welcome to a ”和“ boring Web page”,但不是全部。为了理解这一点,记住标记中的任何内容都必须转换为某种类型的对象。


  此外,顺序无关紧要!如果浏览器显示正确的对象,但显示顺序与您在HTML中提供的顺序不同,那么您能想像出用户将如何响应Web浏览器吗?段落夹在页面标题和文章标题中间,而这不是您自己组织文档时的样式呢?很显然,浏览器必须保持元素和文本的顺序。


  在本例中,p元素有三个不同部分:


  ·em元素之前的文本
  ·em元素本身
  ·em元素之后的文本


  如果将该顺序打乱,可能会把重点放在文本的错误部分。为了保持一切正常,p元素有三个子对象,其顺序是在 清单1的HTML中显示的顺序。而且,重点文本“really”不是p的子元素;而是p的子元素em的子元素。


  理解这一概念非常重要。尽管“really”文本将可能与其他p元素文本一起显示,但它仍是em元素的直接子元素。它可以具有与其他p文本不同的格式,而且可以独立于其他文本到处移动。


  要将之牢记在心,试着用图表示清单2和3中的HTML,确保文本具有正确的父元素(而不管文本最终会如何显示在屏幕上)。


  清单2. 带有巧妙元素嵌套的标记


  <html>
   <head>
    <title>This is a little tricky</title>
   </head>
   <body>
    <h1>Pay <u>close</u> attention, OK?</h1>
    <div>
     <p>This p really isn’t <em>necessary</em>, but it makes the
        <span id=”bold-text”>structure <i>and</i> the organization</span>
        of the page easier to keep up with.</p>
     </div>
   </body>
  </html>
 
  清单3. 更巧妙的元素嵌套


  <html>
   <head>
    <title>Trickier nesting, still</title>
   </head>
   <body>
    <div id=”main-body”>
     <div id=”contents”>
      <table>
       <tr><th>Steps</th><th>Process</th></tr>
       <tr><td>1</td><td>Figure out the <em>root element</em>.</td></tr>
       <tr><td>2</td><td>Deal with the <span id=”code”>head</span> first,
           as it’s usually easy.</td></tr>
       <tr><td>3</td><td>Work through the <span id=”code”>body</span>.
           Just <em>take your time</em>.</td></tr>
      </table>
     </div>
     <div id=”closing”>
      This link is <em>not</em> active, but if it were, the answers
      to this <a href=”answers.html”><img src=”exercise.gif” /></a> would
      be there. But <em>do the exercise anyway!</em>
     </div>
    </div>
   </body>
  </html>
 
  在本文末的GIF文件图2中的tricky-solution.gif和图3中的trickier-solution.gif中将会找到这些练习的答案。不要偷看,先花些时间自动解答一下。这样能帮助您理解组织树时应用的规则有多么严格,并真正帮助您掌握HTML及其树结构。


  属性呢?


  当您试图弄清楚如何处理属性时,是否遇到一些问题呢?前已提及,属性确实具有自己的对象类型,但属性确实不是显示它的元素的子元素,嵌套元素和文本不在同一属性 “级别”,您将注意到,清单2和3中练习的答案没有显示属性。


  属性事实上存储在浏览器使用的对象模型中,但它们有一些特殊情况。每个元素都有可用属性的列表,且与子对象列表是分离的。所以div元素可能有一个包含属性“id”和另一个属性“class”的列表。


  记住,元素的属性必须具有惟一的名称,也就是说,一个元素不能有两个“id”或两个“class”属性。这使得列表易于维护和访问。在下一篇文章将会看到,您可以简单调用诸如getAttribute(“id”)的方法来按名称获取属性的值。还可以用相似的方法调用来添加属性或设置(重置)现有属性的值。


  值得指出的是,属性名的惟一性使得该列表不同于子对象列表。p元素可以有多个em元素,所以子对象列表可以包含多个重复项。尽管子项列表和属性列表的操作方式相似,但一个可以包含重复项(对象的子项),而一个不能(元素对象的属性)。最后,只有元素具有属性,所以文本对象没有用于存储属性的附加列表。


  凌乱的HTML


  在继续之前,谈到浏览器如何将标记转换为树表示,还有一个主题值得探讨,即浏览器如何处理不是格式良好的标记。格式良好是XML广泛使用的一个术语,有两个基本意思:


  ·每个开始标记都有一个与之匹配的结束标记。所以每个<p>在文档中与</p>匹配,每个<div>与</div>匹配,等等。
  ·最里面的开始标记与最里面的结束标记相匹配,然后次里面的开始标记与次里面的结束标记相匹配,依此类推。所以<b><i>bold and italics</b></i>是不合法的,因为最里面的开始标记<i>与最里面的结束标记<b>匹配不当。要使之格式良好,要么切换开始标记顺序,要么切换结束标记顺序。(如果两者都切换,则仍会出现问题)。


  深入研究这两条规则。这两条规则不仅简化了文档的组织,还消除了不定性。是否应先应用粗体后应用斜体?或恰恰相反?如果觉得这种顺序和不定性不是大问题,那么请记住,CSS允许规则覆盖其他规则,所以,例如,如果b元素中文本的字体不同于i元素中的字体,则格式的应用顺序将变得非常重要。因此,HTML的格式良好性有着举足轻重的作用。


  如果浏览器收到了不是格式良好的文档,它只会尽力而为。得到的树结构在最好情况下将是作者希望的原始页面的近似,最坏情况下将面目全非。如果您曾将页面加载到浏览器中后看到完全出乎意料的结果,您可能在看到浏览器结果时会猜想您的结构应该如何,并沮丧地继续工作。当然,搞定这个问题相当简单:确保文档是格式良好的!如果不清楚如何编写标准化的HTML,请咨询参考资料获得帮助。


  DOM简介


  到目前为止,您已经知道浏览器将Web页面转换为对象表示,可能您甚至会猜想,对象表示是DOM树。DOM表示Document Object Model,是一个规范,可从World Wide Web Consortium (W3C)获得(您可以参阅参考资料中的一些DOM相关链接)。


  但更重要的是,DOM定义了对象的类型和属性,从而允许浏览器表示标记。(本系列下一篇文章将专门讲述在JavaScript和Ajax代码中使用DOM的规范。)


  文档对象


  首先,需要访问对象模型本身。这非常容易;要在运行于Web页面上的任何JavaScript代码中使用内置document变量,可以编写如下代码:


  var domTree=document;
 
  当然,该代码本身没什么用,但它演示了每个Web浏览器使得document对象可用于JavaScript代码,并演示了对象表示标记的完整树(图1)。


  每项都是一个节点


  显然,document对象很重要,但这只是开始。在进一步深入之前,需要学习另一个术语:节点。您已经知道标记的每个部分都由一个对象表示,但它不只是一个任意的对象,它是特定类型的对象,一个DOM节点。更特定的类型,比如文本、元素和属性,都继承自这个基本的节点类型。所以可以有文本节点、元素节点和属性节点。


  如果已经有很多JavaScript编程经验,那您可能已经在使用DOM代码了。如果到目前为止您一直在跟踪本Ajax系列,那么现在您一定使用DOM代码有一段时间了。例如,代码行var number=document.getElementById(“phone”).value; 使用DOM查找特定元素,然后检索该元素的值(在本例中是一个表单字段)。所以即使您没有意识到这一点,但您每次将document键入JavaScript代码时都会使用DOM。


  详细解释已经学过的术语,DOM树是对象的树,但更具体地说,它是节点 对象的树。在Ajax应用程序中或任何其他JavaScript中,可以使用这些节点产生下列效果,比如移除元素及其内容,突出显示特定文本,或添加新图像元素。因为都发生在客户端(运行在Web浏览器中的代码),所以这些效果立即发生,而不与服务器通信。最终结果通常是应用程序感觉起来响应更快,因为当请求转向服务器时以及解释响应时,Web页面上的内容更改不会出现长时间的停顿。


  在多数编程语言中,需要学习每种节点类型的实际对象名称,学习可用的属性,并弄清楚类型和强制转换;但在JavaScript中这都不是必需的。您可以只创建一个变量,并为它分配您希望的对象(正如您已经看到的):


  var domTree = document;
  var phoneNumberElement = document.getElementById(“phone”);
  var phoneNumber = phoneNumberElement.value;
 
  没有类型,JavaScript根据需要创建变量并为其分配正确的类型。结果,从JavaScript中使用DOM变得微不足道(将来有一篇文章会专门讲述与XML相关的DOM,那时将更加巧妙)。


  结束语


  在这里,我要给您留一点悬念。显然,这并非是对DOM完全详尽的说明;事实上,本文不过是DOM的简介。DOM的内容要远远多于我今天介绍的这些!


  本系列的下一篇文章将扩展这些观点,并深入探讨如何在JavaScript中使用DOM来更新Web页面、快速更改HTML并为您的用户创建更交互的体验。在后面专门讲述在Ajax请求中使用XML的文章中,我将再次返回来讨论DOM。所以要熟悉DOM,它是Ajax应用程序的一个主要部分。


  此时,深入了解DOM将十分简单,比如详细设计如何在DOM树中移动、获得元素和文本的值、遍历节点列表,等等,但这可能会让您有这种印象,即DOM是关于代码的,而事实上并非如此。


  在阅读下一篇文章之前,试着思考一下树结构并用一些您自己的HTML实践一下,以查看Web浏览器是如何将HTML转换为标记的树视图的。此外,思考一下DOM树的组织,并用本文介绍的特殊情况实践一下:属性、有元素混合在其中的文本、没有文本内容的元素(比如img元素)。


  如果扎实掌握了这些概念,然后学习了JavaScript和DOM的语法(下一篇文章),则会使得响应更为容易。


  而且不要忘了,这里有清单2和3的答案,其中还包含了示例代码!



  图2. 清单2的答案




 
  图3. 清单3的答案

我们一直都在努力坚持原创.......请不要一声不吭,就悄悄拿走。

我原创,你原创,我们的内容世界才会更加精彩!

【所有原创内容版权均属TechTarget,欢迎大家转发分享。但未经授权,严禁任何媒体(平面媒体、网络媒体、自媒体等)以及微信公众号复制、转载、摘编或以其他方式进行使用。】

微信公众号

TechTarget微信公众号二维码

TechTarget

官方微博

TechTarget中国官方微博二维码

TechTarget中国

相关推荐