[TOC]

原文链接:https://javascript.info/basic-dom-node-properties,translate with ❤️ by zhangbao.

让我们更深入地了解 DOM 节点。

在本章中,我们将更多地了解它们是什么以及它们最常用的属性。

DOM 节点类

DOM 节点根据其类别具有不同的属性。例如,<a> 标签对应的元素节点具有与链接相关的属性,<input> 标签对应的元素节点具有与输入相关的属性,依此类推。文本节点与元素节点不同,但是在它们之间也存在共同的属性和方法,因为所有 DOM 节点类都形成于单个层次结构。

每个 DOM 节点都属于相应的内置类。

层次结构的起点是 EventTargetNode 继承了它,其他更具体的节点又继承自 Node

下面这张图片展示了刚才说的关系:

节点属性:类型,标签和内容 - 图1

  • EventTarget:根“抽象”类,无法创建实例,是作为基类使用的。所有的 DOM 节点都支持的所谓的“事件”,就是起源于它,之后会介绍。

  • Node:也是一个“抽象”类,作为 DOM 节点的基础,也无法创建实例。它提供了 3 大核心功能:parentNodepreviousSibling/nextSiblingchildNodes 等(都是 getter)。它又被具体的几个类继承:代表文本节点的 Text,代表元素节点的 Element 和代表注释节点的 Comment

  • Element: 所有元素节点的基类。它提供了元素级别的遍历属性,像:previousElementSibling/nextElementSiblingchildren 和查找方法(像 getElementsByTagNamequerySelector/querySelectorAll)。Element 也充当 SVGElementXMLElementHTMLElement 这些具体类的基类。

  • HTMLElement:所有 HTML 元素的基类,几乎所有的 HTML 元素都继承自它:

  • HTMLInputElement<input> 元素类型,

  • HTMLBodyElement<body> 元素类型,

  • HTMLAnchorElement<a> 元素类型

  • ……每个标签都有自己的所属类型,以及伴随所属类型下包含的特定属性和方法

因此,一个节点所能提供的最终的完整属性和方法集,其实就是继承/叠加而来的结果。

我们以 <input> 元素为例,它属于 HTMLInputElement 类型,最终叠加到它身上的属性和方法来源自:

  • HTMLInputElement:提供与输入相关的属性,继承自……

  • HTMLElement:提供了通用的 HTML 元素方法(getter/setter),继承自……

  • Element:提供了通用元素方法,继承自……

  • Node:提供了通用 DOM 节点属性,继承自……

  • EventTarget:提供了事件支持(之后讲到),

  • ……最终继承自 Object,所以像 hasOwnProperty 这样的“纯对象”上的方法在 <input> 元素也有。

要查看 DOM 节点类名,我们可以回想一下,对象通常具有 constructor 属性,它指向对应的类构造器,constructor.name 的值就是构造器名称:

alert(document.body.constructor.name); // HTMLBodyElement

或者直接使用 toString()

alert(document.body); // [object HTMLBodyElement]

也可以使用 instanceof 检查继承关系:

alert(document.body instanceof HTMLBOdyElement); // true
alert(document.body instanceof HTMLElement); // true
alert(document.body instanceof Element); // true
alert(document.body instanceof Node); // true
alert(document.body instanceof EventTarget); // true

如你所见,DOM 节点就是一个 JavaScript 对象,JavaScript 对象采用的是基于原型的继承方式。

要在浏览器控制台中查看元素的话,可以使用 console.dir(elem),你会在控制台看到 HTMLElement.prototypeElement.prototype 等对象。

节点属性:类型,标签和内容 - 图2console.dir(elem)** 和 ****console.log(elem)** 的不同

多数浏览器同时支持 console.dirconsole.log 方法。这两个方法都是将参数输出到控制台显示。对于普通的 JavaScript 对象来说,使用这两个方法输出,结果没有区别。

但对于 DOM 元素就不同了:

  • console.log(elem) 现实的是元素的 DOM 树结构。

  • console.dir(elem) 将元素作为一个 DOM 对象显示,可以用来很方便地查看元素属性信息。

不信,就在 document.body 上试试吧。

节点属性:类型,标签和内容 - 图3标准里的 IDL

规范中,类并不是用 JavaScript 描述的,而是用一种特殊的接口描述语言(Interface description language,IDL),它通常更容易理解。

在 IDL 中,所有属性前面代表它的数据类型。例如,DOMStringBoolean 等。

以下是一段带注释的规范节选,描述的是 HTMLInputElement 类:

```javascript // 定义 HTMLInputElement // 冒号“:”表示 HTMLInputElment 继承自 HTMLElement interface HTMLInputElement: HTMLElment { // 下面列举了 元素的属性和方法

// “DOMString”表示这些属性的属性值都是字符串 attribute DOMString accept; attribute DOMString alt; attribute DOMString autocomplete; attribute DOMString value;

// 布尔属性(true/false) attribute boolean autofocus; …

// 方法。“void”表示该方法没有返回值 void select(); … }

> 
> 其他类的描述与此类型。


<a name="dd8oak"></a>
## 属性 nodeType

`nodeType` 属性是确定 DOM 节点的“类型”的经典方法。

它的属性值是一个数值:

- `elem.nodeType === 1` 表示元素节点。

- `elem.nodeType === 3` 表示文本节点。

- `elem.nodeType === 9` 表示 `document` 对象。

- 还有更多[类型](https://dom.spec.whatwg.org/#node)。


例如:

```html
<body>
  <script>
    let elem = document.body;

    // 它是什么类型的呢?
    alert(elem.nodeType); // 1 => 元素
    alert(elem.firstChild.nodeType); // 3 => 文本
    // document 对象对应的 nodeType 值是 9
    alert(document.nodeType); // 9
  </script>
</body>

在现代脚本中,我们可以使用 instanceof 和其他基于类的测试来查看节点类型,但有时 nodeType 可能更简单。 我们只能读取 nodeType,而不能修改它。

标签名:nodeName/tagName

给定一个 DOM 节点,我们可以通过它的 nodeName/tagName 属性获得其标签名。

例如:

alert(document.body.nodeName); // BODY
alert(document.body.tagName); // BODY

那么 tagNamenodeName 之间与什么区别吗?

当然,首先是不同的属性名,除此之外,还有一些微妙的差别。

  • tagName 只存在于 Element 节点上。

  • nodeName 则是定义在 Node 上的:

    • 对于元素,nodeNametagName 的返回结果是一样的。

    • 对于其他节点类型(文本、注释等),它返回节点类型的对应字符串表示(像 #text#comment 等)。

也就是说,tagName 是元素节点属性(源自 Element 类),而 nodeName 属性则能表示其他类型节点。

例如,下面来比较 document 和文本节点的 tagNamenodeName 属性。

<body><!-- 注释 -->

  <script>
    // 对注释
    alert(document.body.firstChild.tagName); // undefiend(不是元素)
    alert(document.body.firstCild.nodeName); // #comment

    // 对 document
    alert(document.tagName); // undefined(不是元素)
    alert(document.nodeName); // #document
  </script>
</body>

如果只是处理元素节点的话,使用 tagName 就行了。

节点属性:类型,标签和内容 - 图4tagName`` 的值总是大写的(XHTML 除外)

浏览器有两种处理文档的模式:HTML 和 XML。网页通常都是处于 HTML 模式下的;而 XML 模式,当浏览器接收网页时,接收的请求头里有 Content-Type: application/xml+xhtml``,就会启用。

浏览器有两种处理文档的模式:HTML 和 XML。 通常,HTML 模式用于网页。当浏览器收到带有标题 Content-Type: application/xml+xhtml`` 的 XML 文档时,将启用 XML 模式。

在 HTML 模式下,tagName/nodeName总是大写的:比如,<body>对应 tagName/nodeName 的值都是 BODY``。

在 XML 模式下,tagName/nodeName`` 的返回结果总是在文档中出现的样子,不过 XML 模式现在已经非常少使用了。

内容:innerHTML

innerHTML 属性允许将元素内的 HTML 作为字符串获取。

我们也可以修改它,所以这是改变页面的最有效方法之一。

下例中,先显示了 document.body 的内容,然后再完全替换它:

<body>
  <p>一个自然段</p>
  <div>一个 div</div>

  <script>
    alert(document.body.innerHTML); // 获取当前内容
    document.body.innerHTML = '新内容'; // 替换原本内容
  </script>
</body>

当我们使用 innerHTML 插入无效文本时,浏览器也会自己修复:

<body>
  <script>
    document.body.innerHTML = '<b>test'; // 忘记加 </b> 了
    alert(document.body.innerHTML); // <b>test</b>(修复后)
  </script>
</body>

节点属性:类型,标签和内容 - 图5脚本不会执行

当使用 innerHTML 向文档插入