设为首页 加入收藏
 
首 页 开源技术和代码信息库 软硬件共享平台 软件技术文献库 呼叫中心
关键字:      热门搜索:JAVA  .NET  JSP  PHP  WEB开发  
用户登录
用户名:
密 码:
验证码: 验证码,看不清楚请用鼠标点击此处!
网站导航
推荐给好友
您的姓名
好友邮箱
   
 
当前位置:首页 > 软件技术文献 > 掌握 Ajax 之操纵 DOM(二)

掌握 Ajax 之操纵 DOM(二)

 作者:Brett McLaughlin  出处:developerWorks 中国 发表时间: 2009-03-12 打印 下载 收藏 推荐给好友
了解什么是 DOM 树,以及知道它如何表示 HTML 和 CSS 仅仅是控制 Web 页面的第一步。接下来还需要了解如何处理 Web 页面的 DOM 树。一旦学会了如何处理 DOM 树,您就向实现丰富的、交互式动态网站迈出了一大步。

  API 设计问题

  再看一看各种节点提供的属性和方法。对于那些熟悉面向对象(OO)编程的人来说,它们说明了 DOM 的一个重要特点:DOM 并非完全面向对象的 API。首先,很多情况下要直接使用对象的属性而不是调用节点对象的方法。比方说,没有 getNodeName() 方法,而要直接使用 nodeName 属性。因此节点对象(以及其他 DOM 对象)通过属性而不是函数公开了大量数据。

  其次,如果习惯于使用重载对象和面向对象的 API,特别是 Java 和 C++ 这样的语言,就会发现 DOM 中的对象和方法命名有点奇怪。DOM 必须能用于 C、Java 和 JavaScript(这只是其中的几种语言),因此 API 设计作了一些折衷。比如,NamedNodeMap 方法有两种不同的形式:

  •   getNamedItem(String name)
  •   getNamedItemNS(Node node)

  对于 OO 程序员来说这看起来非常奇怪。两个方法目的相同,只不过一个使用 String 参数而另一个使用 Node 参数。多数 OO API 中对这两种版本都会使用相同的方法名。运行代码的虚拟机将根据传递给方法的对象类型决定运行哪个方法。

  问题在于 JavaScript 不支持这种称为方法重载 的技术。换句话说,JavaScript 要求每个方法或函数使用不同的名称。因此,如果有了一个名为 getNamedItem() 的接受字符串参数的方法,就不能再有另一个方法或函数也命名为 getNamedItem(),即使这个方法的参数类型不同(或者完全不同的一组参数)。如果这样做,JavaScript 将报告错误,代码不会按照预期的方式执行。

  从根本上说,DOM 有意识地避开了方法重载和其他 OO 编程技术。这是为了保证该 API 能够用于多种语言,包括那些不支持 OO 编程技术的语言。后果不过是要求您多记住一些方法名而已。好处是可以在任何语言中学习 DOM,比如 Java,并清楚同样的方法名和编码结构也能用于具有 DOM 实现的其他语言,如 JavaScript。

  让程序员小心谨慎

  如果深入研究 API 设计或者仅仅非常关注 API 设计,您可能会问:“为何节点类型的属性不能适用于所有节点?” 这是一个很好的问题,问题的答案与政治及决策关系更密切,而非技术原因。简单地说,答案就是,“谁知道!但有点令人恼火,不是吗?”

  属性 nodeName 意味着允许每种类型的节点都有一个名字,但是很多情况下名字要么未定义,要么是对于程序员没有意义的内部名(比如在 Java 中,很多情况下文本节点的 nodeName 被报告为 “#text”)。从根本上说,必须假设您得自己来处理错误。直接访问 myNode.nodeName 然后使用该值是危险的,很多情况下这个值为空。因此与通常的编程一样,程序员要谨慎从事。

  通用节点类型

  现在已经介绍了 DOM 节点的一些特性和属性(以及一些奇特的地方),下面开始讲述您将用到的一些特殊节点类型。多数 Web 应用程序中只用到四种节点类型:

  •   文档节点表示整个 HTML 文档。
  •   元素节点表示 HTML 元素,如 a 或 img。
  •   属性节点表示 HTML 元素的属性,如 href(a 元素)或 src(img 元素)。
  •   文本节点表示 HTML 文档中的文本,如 “Click on the link below for a complete set list”。这是出现在 p、a 或 h2 这些元素中的文字。

  处理 HTML 时,95% 的时间是跟这些节点类型打交道。因此本文的其余部分将详细讨论这些节点。(将来讨论 XML 的时候将介绍其他一些节点类型。)

  文档节点

  基本上所有基于 DOM 的代码中都要用到的第一个节点类型是文档节点。文档节点 实际上并不是 HTML(或 XML)页面中的一个元素而是页面本身。因此在 HTML Web 页面中,文档节点就是整个 DOM 树。在 JavaScript 中,可以使用关键字 document 访问文档节点:

// These first two lines get the DOM tree for the current Web page,
//   and then the <html> element for that DOM tree
var myDocument = document;
var htmlElement = myDocument.documentElement;

  JavaScript 中的 document 关键字返回当前网页的 DOM 树。从这里可以开始处理树中的所有节点。

  也可使用 document 对象创建新节点,如下所示:

  •   createElement(elementName) 使用给定的名称创建一个元素。
  •   createTextNode(text) 使用提供的文本创建一个新的文本节点。
  •   createAttribute(attributeName) 用提供的名称创建一个新属性。

  这里的关键在于这些方法创建节点,但是并没有将其附加或者插入到特定的文档中。因此,必须使用前面所述的方法如 insertBefore() 或 appendChild() 来完成这一步。因此,可使用下面的代码创建新元素并将其添加到文档中:

var pElement = myDocument.createElement("p");
var text = myDocument.createTextNode("Here's some text in a p element.");
pElement.appendChild(text);
bodyElement.appendChild(pElement);

  一旦使用 document 元素获得对 Web 页面 DOM 树的访问,就可以直接使用元素、属性和文本了。

  元素节点

  虽然会大量使用元素节点,但很多需要对元素执行的操作都是所有节点共有的方法和属性,而不是元素特有的方法和属性。元素只有两组专有的方法:

  1. 与属性处理有关的方法:

  •   o getAttribute(name) 返回名为 name 的属性值。
  •   o removeAttribute(name) 删除名为 name 的属性。
  •   o setAttribute(name, value) 创建一个名为 name 的属性并将其值设为 value。
  •   o getAttributeNode(name) 返回名为 name 的属性节点(属性节点在 下一节 介绍)。
  •   o removeAttributeNode(node) 删除与指定节点匹配的属性节点。

  2. 与查找嵌套元素有关的方法:

  •   o getElementsByTagName(elementName) 返回具有指定名称的元素节点列表。

  这些方法意义都很清楚,但还是来看几个例子吧。

  处理属性

  处理元素很简单,比如可用 document 对象和上述方法创建一个新的 img 元素:

var imgElement = document.createElement("img");
imgElement.setAttribute("src", "http://www.headfirstlabs.com/Images/hraj_cover-150.jpg");
imgElement.setAttribute("width", "130");
imgElement.setAttribute("height", "150");
bodyElement.appendChild(imgElement);

  现在看起来应该非常简单了。实际上,只要理解了节点的概念并知道有哪些方法可用,就会发现在 Web 页面和 JavaScript 代码中处理 DOM 非常简单。在上述代码中,JavaScript 创建了一个新的 img 元素,设置了一些属性然后添加到 HTML 页面的 body 元素中。

  查找嵌套元素

  发现嵌套的元素很容易。比如,下面的代码用于发现和删除 清单 3 所示 HTML 页面中的所有 img 元素:

      // Remove all the top-level <img> elements in the body
      if (bodyElement.hasChildNodes()) {
        for (i=0; i<bodyElement.childNodes.length; i++) {
          var currentNode = bodyElement.childNodes[i];
          if (currentNode.nodeName.toLowerCase() == "img") {
            bodyElement.removeChild(currentNode);
          }
        }
      }

  也可以使用 getElementsByTagName() 完成类似的功能:

// Remove all the top-level <img> elements in the body
      var imgElements = bodyElement.getElementsByTagName("img");

      for (i=0; i<imgElements.length; i++) {
        var imgElement = imgElements.item[i];
        bodyElement.removeChild(imgElement);
      }

  属性节点

  DOM 将属性表示成节点,可以通过元素的 attributes 来访问元素的属性,如下所示:

// Remove all the top-level <img> elements in the body
      var imgElements = bodyElement.getElementsByTagName("img");

      for (i=0; i<imgElements.length; i++) {
        var imgElement = imgElements.item[i];

        // Print out some information about this element
        var msg = "Found an img element!";
        var atts = imgElement.attributes;
        for (j=0; j<atts.length; j++) {
          var att = atts.item(j);
          msg = msg + "\n  " + att.nodeName + ": '" + att.nodeValue + "'";
        }
        alert(msg);

        bodyElement.removeChild(imgElement);
      }

  需要指出的是,attributes 属性实际上是对节点类型而非局限于元素类型来说的。有点古怪,不影响您编写代码,但是仍然有必要知道这一点。

  虽然也能使用属性节点,但通常使用元素类的方法处理属性更简单。其中包括:

  •   getAttribute(name) 返回名为 name 的属性值。
  •   removeAttribute(name) 删除名为 name 的属性。
  •   setAttribute(name, value) 创建一个名为 name 的属性并将其值设为 value。

  这三个方法不需要直接处理属性节点。但允许使用简单的字符串属性设置和删除属性及其值。

  文本节点

  需要考虑的最后一种节点是文本节点(至少在处理 HTML DOM 树的时候如此)。基本上通常用于处理文本节点的所有属性都属于节点对象。实际上,一般使用 nodeValue 属性来访问文本节点的文本,如下所示:

var pElements = bodyElement.getElementsByTagName("p");
for (i=0; i<pElements.length; i++) {
  var pElement = pElements.item(i);
  var text = pElement.firstChild.nodeValue;
  alert(text);
}

  少数其他几种方法是专门用于文本节点的。这些方法用于增加或分解节点中的数据:

  •   appendData(text) 将提供的文本追加到文本节点的已有内容之后。
  •   insertData(position, text) 允许在文本节点的中间插入数据。在指定的位置插入提供的文本。
  •   replaceData(position, length, text) 从指定位置开始删除指定长度的字符,用提供的文本代替删除的文本。

  什么节点类型?

  到目前为止看到的多数代码都假设已经知道处理的节点是什么类型,但情况并非总是如此。比方说,如果在 DOM 树中导航并处理一般的节点类型,可能就不知道您遇到了元素还是文本。也许获得了 p 元素的所有孩子,但是不能确定处理的是文本、b 元素还是 img 元素。这种情况下,在进一步的处理之前需要确定是什么类型的节点。

  所幸的是很容易就能做到。DOM 节点类型定义了一些常量,比如:

  •   1. Node.ELEMENT_NODE 是表示元素节点类型的常量。
  •   2. Node.ATTRIBUTE_NODE 是表示属性节点类型的常量。
  •   3. Node.TEXT_NODE 是表示文本节点类型的常量。
  •   4. Node.DOCUMENT_NODE 是表示文档节点类型的常量。

  还有其他一些节点类型,但是对于 HTML 除了这四种以外很少用到。我有意没有给出这些常量的值,虽然 DOM 规范中定义了这些值,永远不要直接使用那些值,因为这正是常量的目的!

  nodeType 属性

  可使用 nodeType 属性比较节点和上述常量 —— 该属性定义在 DOM node 类型上因此可用于所有节点,如下所示:

var someNode = document.documentElement.firstChild;
if (someNode.nodeType == Node.ELEMENT_NODE) {
  alert("We've found an element node named " + someNode.nodeName);
} else if (someNode.nodeType == Node.TEXT_NODE) {
  alert("It's a text node; the text is " + someNode.nodeValue);
} else if (someNode.nodeType == Node.ATTRIBUTE_NODE) {
  alert("It's an attribute named " + someNode.nodeName
                        + " with a value of '" + someNode.nodeValue + "'");
}

  这个例子非常简单,但说明了一个大问题:得到节点的类型非常 简单。更有挑战性的是知道节点的类型之后确定能做什么,只要掌握了节点、文本、属性和元素类型提供了什么属性和方法,就可以自己进行 DOM 编程了。

  好了,快结束了。

  实践中的挫折

  nodeType 属性似乎是使用节点的一个入场券 —— 允许确定要处理的节点类型然后编写处理该节点的代码。问题在于上述 Node 常量定义不能正确地用于 Internet Explorer。因此如果在代码中使用 Node.ELEMENT_NODE、Node.TEXT_NODE 或其他任何常量,Internet Explorer 都将返回如 图 4 所示的错误。

  图 4. Internet Explorer 报告错误

  任何时候在 JavaScript 中使用 Node 常量,Internet Explorer 都会报错。因为多数人仍然在使用 Internet Explorer,应该避免在代码中使用 Node.ELEMENT_NODE 或 Node.TEXT_NODE 这样的构造。尽管据说即将发布的新版本 Internet Explorer 7.0 将解决这个问题,但是在 Internet Explorer 6.x 退出舞台之前仍然要很多年。因此应避免使用 Node,要想让您的 DOM 代码(和 Ajax 应用程序)能用于所有主要浏览器,这一点很重要。

  结束语

  在本系列的上几期文章中您已经学习了很多。现在,您不 应该再坐等下一篇文章期待我介绍各种聪明的 DOM 树用法。现在的家庭作业是看看如何使用 DOM 创造出富有想像力的效果或者漂亮的界面。利用近几期文章中所学的知识开始实验和练习。看看能否建立感觉更与桌面应用程序接近的网站,对象能够响应用户的动作在屏幕上移动。

  最好在屏幕上为每个对象画一个边界,这样就能看到 DOM 树中的对象在何处,然后再移动对象。创建节点并将其添加到已有的孩子列表中,删除没有嵌套节点的空节点,改变节点的 CSS 样式,看看孩子节点是否会继承这些修改。可能性是无限的,每当尝试一些新东西时,就学到了一些新的知识。尽情地修改您的网页吧!

  在 DOM 三部曲的最后一期文章中,我将 介绍如何把一些非常棒的有趣的 DOM 应用结合到编程中。我将不再是从概念上说教和解释 API,而会提供一些代码。在此之前先发挥您自己的聪明才智,看看能做些什么。

对本文的评价
太差! (1)
需提高 (2)
一般 (3)
好文章 (4)
真棒!(5)
评论
发表评论人笔名:
其他用户评论
暂无评论
合肥服务外包公共服务平台网站群:合肥服务外包网   公共信息平台  公共技术平台   公共培训平台
合肥市外经贸局地址:合肥市政务新区B座9-10层 邮编:230022 监督电话:3538679 传真:3538677
Email:hffetc@mail.hf.ah.cn 合肥市外经贸局行政服务中心窗口电话:3537035、3537377
版权所有:合肥市对外贸易经济合作局 皖ICP备06004470号