操纵 DOM

本文介绍了如何使用DOM树中的节点属性和方法进行页面元素的操作,包括获取元素名称、文本内容及节点的增删等。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

操纵 DOM

 

节点的概念

节点是 DOM 中最基本的对象类型。实际上,您将在本文中看到,基本上 DOM 定义的其他所有对象都是节点对象的扩展。但是在深入分析语义之前,必须了解节点所代表的概念,然后再学习节点的具体属性和方法就非常简单了。

DOM 树中,基本上一切都是节点。每个元素在最底层上都是 DOM 树中的节点。每个属性都是节点。每段文本都是节点。甚至注释、特殊字符(如版权符号 © )、DOCTYPE 声明(如果 HTML 或者 XHTML 中有的话)全都是节点。因此在讨论这些具体的类型之前必须清楚地把握什么是节点。

节点是……

用最简单的话说,节点就是 DMO 树中的任何事物。之所以用 事物 这个模糊的字眼,是因为只能明确到这个程度。比如 HTML 中的元素(如 img )和 HTML 中的文本片段(如 “Scroll down for more details” )没有多少明显的相似之处。但这是因为您考虑的可能是每种类型的功能 ,关注的是它们的不同点。

但是如果从另一个角度观察,DOM 树中的每个元素和每段文本都有一个父亲 ,这个父节点可能是另一个元素(比如嵌套在 p 元素中的 img )的孩子,或者 DOM 树中的顶层元素(这是每个文档中都出现一次的特殊情况,即使用 html 元素的地方)。另外,元素和文本都有一个类型 。显然,元素的类型就是元素,文本的类型就是文本。每个节点还有某种定义明确的结构:下面还有节点(如子元素)吗?有兄弟节点 (与元素或文本 相邻的 节点)吗?每个节点属于哪个文档?

显然,大部分内容听起来很抽象。实际上,说一个元素的类型是元素似乎有点冒傻气。但是要真正认识到将节点作为通用对象类型的价值,必须抽象一点来思考。

通用节点类型

DOM 代码中最常用的任务就是在页面的 DOM 树中导航。比方说,可以通过其 “id” 属性定位一个 form ,然后开始处理那个 form 中内嵌的元素和文本。其中可能包含文字说明、输入字段的标签、真正的 input 元素,以及其他 HTML 元素(如 img )和链接(a 元素)。如果元素和文本是完全不同的类型 ,就必须为每种类型编写完全不同的代码。

如果使用一种通用节点类型情况就不同了。这时候只需要从一个节点移动到另一个节点,只有当需要对元素或文本作某种特殊处理时才需要考虑节点的类型 。 如果仅仅在 DOM 树中移动,就可以与其他节点类型一样用同样的操作移动到元素的父节点或者子节点。只有当需要某种节点类型的特殊性质时,如元素的属性,才需要对节点类型作 专门处理。将 DOM 树中的所有对象都看作节点可以简化操作。记住这一点之后,接下来我们将具体看看 DOM 节点构造应该提供什么,首先从属性和方法开始。

 

<!--[if !vml]--> <!--[endif]-->
<!--[if !vml]--> <!--[endif]-->

<!--[if !vml]--> <!--[endif]-->

<!--[if !vml]--> <!--[endif]-->

回页首

 

节点的属性

使用 DOM 节点时需要一些属性和方法,因此我们首先来讨论节点的属性和方法。DOM 节点的属性主要有:

  • nodeName 报告节点的名称(详见下述)。
  • nodeValue 提供节点的 (详见后述)。
  • parentNode 返回节点的父节点。记住,每个元素、属性和文本都有一个父节点。
  • childNodes 是节点的孩子节点列表。对于 HTML ,该列表仅对元素有意义,文本节点和属性节点都没有孩子。
  • firstChild 仅仅是 childNodes 列表中第一个节点的快捷方式。
  • lastChild 是另一种快捷方式,表示 childNodes 列表中的最后一个节点。
  • previousSibling 返回当前节点之前 的节点。换句话说,它返回当前节点的父节点的 childNodes 列表中位于该节点前面的那个节点(如果感到迷惑,重新读前面一句)。
  • nextSibling 类似于 previousSibling 属性,返回父节点的 childNodes 列表中的下一个节点。
  • attributes 仅用于元素节点,返回元素的属性列表。

其他少数几种属性实际上仅用于更一般的 XML 文档,在处理基于 HTML 的网页时没有多少用处。

不常用的属性

上述大部分属性的意义都很明确,除了 nodeName nodeValue 属性以外。我们不是简单地解释这两个属性,而是提出两个奇怪的问题:文本节点的 nodeName 应该是什么 ?类似地,元素的 nodeValue 应该是什么

如果这些问题难住了您,那么您就已经了解了这些属性固有的含糊性。nodeName nodeValue 实际上并非适用于所有 节点类型(节点的其他少数几个属性也是如此)。这就说明了一个重要概念:任何这些属性都可能返回空值(有时候在 JavaScript 中称为 未定义 )。比方说,文本节点的 nodeName 属性是空值(或者在一些浏览器中称为 未定义 ),因为文本节点没有名称。如您所料,nodeValue 返回节点的文本。

类似地,元素有 nodeName ,即元素名,但元素的 nodeValue 属性值总是空。属性同时具有 nodeName nodeValue 。下一节我还将讨论这些单独的类型,但是因为这些属性是每个节点的一部分,因此在这里有必要提一提。

现在看看 清单 1 ,它用到了一些节点属性。


清单 1. 使用 DOM 中的节点属性

 

    // 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;

    // What's the name of the <html> element? "html"

    alert("The root element of the page is " + htmlElement.nodeName);

    // Look for the <head> element

    var headElement = htmlElement.getElementsByTagName("head")[0];

    if (headElement != null) {

      alert("We found the head element, named " + headElement.nodeName);

      // Print out the title of the page

      var titleElement = headElement.getElementsByTagName("title")[0];

      if (titleElement != null) {

        // The text will be the first child node of the <title> element

        var titleText = titleElement.firstChild;

        // We can get the text of the text node with nodeValue

        alert("The page title is '" + titleText.nodeValue + "'");

      }

      // After <head> is <body>

      var bodyElement = headElement.nextSibling;

      while (bodyElement.nodeName.toLowerCase() != "body") {

        bodyElement = bodyElement.nextSibling;

      }

      // We found the <body> element...

       // We'll do more when we know some methods on the nodes.

    }

 

<!--[if !vml]--> <!--[endif]-->
<!--[if !vml]--> <!--[endif]-->

<!--[if !vml]--> <!--[endif]-->

<!--[if !vml]--> <!--[endif]-->

回页首

 

节点方法

接下来看看所有节点都具有的方法(与节点属性一样,我省略了实际上不适用于多数 HTML DOM 操作的少数方法):

  • insertBefore(newChild, referenceNode) 将 newChild 节点插入到 referenceNode 之前。记住,应该对 newChild 的目标父节点调用该方法。
  • replaceChild(newChild, oldChild) 用 newChild 节点替换 oldChild 节点。
  • removeChild(oldChild) 从运行该方法的节点中删除 oldChild 节点。
  • appendChild(newChild) 将 newChild 添加到运行该函数的节点之中。newChild 被添加到目标节点孩子列表中的末端
  • hasChildNodes() 在调用该方法的节点有孩子时则返回 true ,否则返回 false
  • hasAttributes() 在调用该方法的节点有属性时则返回 true ,否则返回 false

注意,大部分情况下所有这些方法处理的都是节点的孩子。这是它们的主要用途。如果仅仅想获取文本节点值或者元素名,则不需要调用这些方法,使用节点属性就可以了。清单 2 清单 1 的基础上增加了方法使用。


清单 2. 使用 DOM 中的节点方法

 

// 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;

    // What's the name of the <html> element? "html"

    alert("The root element of the page is " + htmlElement.nodeName);

    // Look for the <head> element

    var headElement = htmlElement.getElementsByTagName("head")[0];

    if (headElement != null) {

      alert("We found the head element, named " + headElement.nodeName);

      // Print out the title of the page

      var titleElement = headElement.getElementsByTagName("title")[0];

      if (titleElement != null) {

        // The text will be the first child node of the <title> element

        var titleText = titleElement.firstChild;

        // We can get the text of the text node with nodeValue

        alert("The page title is '" + titleText.nodeValue + "'");

      }

      // After <head> is <body>

      var bodyElement = headElement.nextSibling;

      while (bodyElement.nodeName.toLowerCase() != "body") {

        bodyElement = bodyElement.nextSibling;

      }

      // We found the <body> element...

      // 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);

          }

        }

      }

    }

 

测试一下!

目前虽然只看到了两个例子,清单 1 2 ,不过通过这两个例子您应该能够了解使用 DOM 树能够做什么。如果要尝试一下这些代码,只需要将 清单 3 拖入一个 HTML 文件并保存,然后用 Web 浏览器打开。


清单 3. 包含使用 DOM JavaScript 代码的 HTML 文件

 

<html>

  <head>

  <title>JavaScript and the DOM</title>

  <script language="JavaScript">

   function test() {

     // 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;

    // What's the name of the <html> element? "html"

    alert("The root element of the page is " + htmlElement.nodeName);

    // Look for the <head> element

    var headElement = htmlElement.getElementsByTagName("head")[0];

    if (headElement != null) {

      alert("We found the head element, named " + headElement.nodeName);

      // Print out the title of the page

      var titleElement = headElement.getElementsByTagName("title")[0];

      if (titleElement != null) {

        // The text will be the first child node of the <title> element

        var titleText = titleElement.firstChild;

        // We can get the text of the text node with nodeValue

        alert("The page title is '" + titleText.nodeValue + "'");

      }

      // After <head> is <body>

      var bodyElement = headElement.nextSibling;

       while (bodyElement.nodeName.toLowerCase() != "body") {

        bodyElement = bodyElement.nextSibling;

      }

      // We found the <body> element...

      // 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);

          }

        }

      }

     }

  }

  </script>

  </head>

  <body>

  <p>JavaScript and DOM are a perfect match.

     You can read more in <i>Head Rush Ajax</i>.</p>

  <img src="http://www.headfirstlabs.com/Images/hraj_cover-150.jpg" />

  <input type="button" value="Test me!" onClick="test();" />

  </body>

</html>

 

 

API 设计问题

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

其次,如果习惯于使用重载对象和面向对象的 API ,特别是 Java C++ 这样的语言,就会发现 DOM 中的对象和方法命名有点奇怪。DOM 必须能用于 CJava 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 然后使用该值是危险的,很多情况下这个值为空。因此与通常的编程一样,程序员要谨慎从事。

 

<!--[if !vml]--> <!--[endif]-->
<!--[if !vml]--> <!--[endif]-->

<!--[if !vml]-->

标题基于PHP + JavaScript的助眠小程序设计与实现AI更换标题第1章引言介绍助眠小程序的研究背景、意义,以及论文的研究内容和创新点。1.1研究背景与意义阐述助眠小程序在当前社会的重要性和应用价值。1.2国内外研究现状分析国内外在助眠小程序方面的研究进展及现状。1.3论文研究内容与创新点概述论文的主要研究内容和创新之处。第2章相关理论基础介绍助眠小程序设计与实现所涉及的相关理论基础。2.1PHP编程技术阐述PHP编程技术的基本概念、特点和在助眠小程序中的应用。2.2JavaScript编程技术介绍JavaScript编程技术的核心思想、作用及在小程序中的实现方式。2.3小程序设计原理讲解小程序的设计原则、架构和关键技术。第3章助眠小程序需求分析对助眠小程序进行详细的需求分析,为后续设计与实现奠定基础。3.1用户需求调研用户需求调研的过程和方法,总结用户需求。3.2功能需求分析根据用户需求,分析并确定助眠小程序的核心功能和辅助功能。3.3性能需求分析明确助眠小程序在性能方面的要求,如响应速度、稳定性等。第4章助眠小程序设计详细阐述助眠小程序的设计方案,包括整体架构、功能模块和界面设计。4.1整体架构设计给出助眠小程序的整体架构设计思路和实现方案。4.2功能模块设计详细介绍各个功能模块的设计思路和实现方法。4.3界面设计阐述助眠小程序的界面设计风格、布局和交互设计。第5章助眠小程序实现与测试讲解助眠小程序的实现过程,并进行详细的测试与分析。5.1开发环境搭建与配置介绍开发环境的搭建过程和相关配置信息。5.2代码实现与优化详细阐述助眠小程序的代码实现过程,包括关键技术的运用和优化措施。5.3测试与性能分析对助眠小程序进行全面的测试,包括功能测试、性能测试等,并分析测试结果。第6章结论与展望总结论文的研究成果,展望未来的研究方向和应用前景。6.1研究成果总结概括性地总结论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值