W3C DOM 简介


W3C 联合各浏览器厂商制订了标准对象模型,试图让各浏览器厂商遵合此一模型进行实现,以解决各浏览器间对象模型不一致的问题,在新的对象模型中,也对文件操作的功能加以扩充。

简单来说,在 DOM 的标准下,一份文件中所有的标签定义,包括文本,都是一个对象,这些对象以文件定义的结构,形成了一个树状结构。例如:

<html>
    <head>
        <title>首页</title>
    </head>
    <body>
        <h1>Hello!World!</h1>
        <a href="Gossip/index.html">学习笔记</a>
    </body>
</html>

这份 HTML 文件,会形成以下树状的对象结构:

document                             (Document)
       |-html                        (HTMLHtmlElement)
            |-head                   (HTMLHeadElement)
            |    |-title             (HTMLTitleElement)
            |          |-首页        (Text)
            |
            |body                    (HTMLBodyElement)
                 |-h1                (HTMLHeadingElement
                 |  |-Hello!World!   (Text)
                 |
                 |-a                 (HTMLAnchorElement)
                   |-学习笔记         (Text)

上图右边的括号,表示每个对象的类型。注意,document代表整个文件,而不代表html标签节点,你可以使用document.childNodes[0]获取html标签 DOM 元素,childNodes表示获取子节点,取回的会是NodeList对象,是个类数组对象,可使用索引值来指定获取某个子节点。

方便的document.documentElement也可用来获取html标签 DOM 元素。如果想获取body标签 DOM 元素,也可以透过document.body来获取。注意,文本也会形成树状结构中的元素。

尽管你在上面看到的元素形态,有许多都带有HTML字眼,但 DOM 并非专属于 HTML 的对象模型,DOM API 分为两部份,一个是核心 DOM API,一个是 HTML DOM API。

核心 API 是一个独立的规范,可以任何语言实现,可操作的对象是基于 XML 的任何文件,你可以在〈XML DOM Tutorial〉找到 DOM 核心 API 的相关数据。

HTML API 是 核心 API 的延伸,专门操作 HTML,各种对象对应的形态,通常会有个 HTML 字眼在前头,你可以在〈JavaScript and HTML DOM Reference〉找到 HTML DOM API 的相关数据。

核心 API 文件中所有内容都视为节点,包括文件本身,再依类型区分出不同的形态:

Node
   |Document
   |Element
   |Text
   |Attr
   ...

Document代表整份文件,Element是所有标签(也是节点),Text代表文本符素(也是节点)。

Level 0 DOM 在window对象上有navigatorlocationframesscreenhistory等与浏览器相关的对象,与文件相关的对象,实际上只有document,功能也有限,这个部份纳入了 DOM 标准,成为了 DOM 的子集,由于这些旧式 API 都是专属于 HTML,所以你在〈Document〉的文件上看不到相关操作,而必须在〈The HTML DOM Document Object〉这个专属于 HTML 的 DOM API 文件上才可以找到。

同样地,Element仅定义核心 DOM API 中元素的操作,而HTMLHeadElement等元素也有一些专属 HTML 的 API,这在〈Element〉文件中找不到,而必须在〈The HTML DOM Document Object〉等相关类型中寻找。

每个节点都会有nodeNamenodeType特性,前者可以获取节点的名称,后者可以获取节点类型常数,这个常数用来查找对应的类型名称,常数与类型名称的对照可在〈HTML DOM nodeType Property
Element Object〉文件中找到。

举例来说,可以搭配 JavaScript,如下显示出一个网页的节点与类型:

<!DOCTYPE html>
<html>
    <head>
        <meta charset="utf-8">
        <meta name="viewport" content="width=device-width">
        <title>首页</title>
    </head>
    <body>
        <h1>Hello!World!</h1>
        <a href="https://openhome.cc/Gossip/">学习笔记</a>
        <div id="console"></div>

<script type="text/javascript">
    let typeNames = new Map([
        [Node.ELEMENT_NODE, 'ELEMENT_NODE'],
        [Node.ATTRIBUTE_NODE, 'ATTRIBUTE_NODE'],
        [Node.TEXT_NODE, 'TEXT_NODE'],
        [Node.CDATA_SECTION_NODE, 'CDATA_SECTION_NODE'],
        [Node.ENTITY_REFERENCE_NODE, 'ENTITY_REFERENCE_NODE'],
        [Node.ENTITY_NODE, 'ENTITY_NODE'],
        [Node.PROCESSING_INSTRUCTION_NODE, 'PROCESSING_INSTRUCTION_NODE'],
        [Node.COMMENT_NODE, 'COMMENT_NODE'],
        [Node.DOCUMENT_NODE, 'DOCUMENT_NODE'],
        [Node.DOCUMENT_TYPE_NODE, 'DOCUMENT_TYPE_NODE'],
        [Node.DOCUMENT_FRAGMENT_NODE, 'DOCUMENT_FRAGMENT_NODE'],
        [Node.NOTATION_NODE, 'NOTATION_NODE']
    ]);

    function subNodesOf(parent, indent = '  ') {
        let nodes = parent.childNodes;
        let nodeDesc = Array.from(nodes).reduce((nodeDesc, node) => {
            let nodeName = node.nodeName;
            let typeName = typeNames.get(node.nodeType);
            return nodeDesc + `${indent} ${nodeName} ${typeName} <br>` + 
                   subNodesOf(node, `  ${indent}`);
        }, '');

        return nodeDesc;
    }

    let nodeName = document.nodeName;
    let typeName = typeNames.get(document.nodeType);
    document.getElementById('console').innerHTML = 
        `${nodeName} ${typeName} <br>` + subNodesOf(document);

</script>

    </body>
</html>

按此观看结果


展开阅读全文