JavaScript Day 24. DOM API 节点

之前我们有提到,JavaScript 如何与浏览器沟通,於是我们讨论到透过 JavaScript 取得 DOM API 节点,使浏览器可以处理使用者行为与互动。

DOM 是 W3C 制定的一个规范,是 HTML 与 XML 文档的编程接口,一个 HTML 文件,我们可以用编辑器以代码的形式展示,也可以用浏览器以页面的形式展示,DOM 将文件档解析为一个由节点和对象 ( 包含属性和方法 ) 组成的结构集合,因此开发者能够在一个规范及方法之下改变内容、结构及样式。


如何访问 DOM

我们可以透过 JavaScript 来获取 DOM 的 document 和 window 节点 API,来操作里面的文件以及讯息。而 DOM 的节点大致归纳为:元素节点、文字节点以及属性节点。

以下为常见的 DOM 选取方法:

// 根据传入的值,找到 DOM 中 id 为 'xxx' 的元素。
document.getElementById('xxx');

// 针对给定的 tag 名称,回传所有符合条件的 NodeList 物件
document.getElementsByTagName('xxx');

// 针对给定的 class 名称,回传所有符合条件的 NodeList 物件。
document.getElementsByClassName('xxx');

// 针对给定的 Selector 条件,回传第一个 或 所有符合条件的 NodeList。
document.querySelector('xxx');
document.querySelectorAll('xxx');

DOM 节点类型

Node 是一个接口,有许多接口都从 Node 继承方法与属性,下面为常用的 DOM 节点:

节点 数值 说明
Node.ELEMENT_NODE 1 一个元素节点,例如 <p> 和 <div>
Node.TEXT_NODE 3 实际文字节点,包括了换行与空格
Node.COMMENT_NODE 8 一个 Comment 节点
Node.DOCUMENT_NODE 9 一个 Document 节点
Node.DOCUMENT_TYPE_NODE 10 描述文档类型的 DocumentType 节点。例如 <!DOCTYPE html> 就是用於 HTML5 的
Node.DOCUMENT_FRAGMENT_NODE 11 一个 DocumentFragment 节点

假如我们要判断一个 Node 是否为一个元素,可以透过以上表格得知属性值,并使用 nodeType

if(X.nodeType === 1){
console.log('X 是一个元素');
}

节点关系 API

先前我们也有谈论到「DOM Tree」,因此可以知道 DOM 是一层一层的,需要有效的抓取元素则需要弄清楚这一层一层的关系。

  • 父子关系:

    除了 document 之外,每一个节点都会有上层与下层的关系,上层称为「父节点 ( Parent node ) 」,下曾则是「子节点 ( Child node ) 」。

  • 兄弟关系:

    故名思义就是同一个父亲底下的产物(?),拥有同一个「父节点」的节点,他们的关系就是「兄弟节点 ( Siblings node ) 」。

父关系 API

  • parentNode / 每一个节点都会有一个 parentNode 属性,它表示元素的父节点;我们可以透过 Node.parentNode 来取得父元素,回传的值可能会是元素节点 ( Element )、根节点 ( Document ) 或 DocumentFragment 节点。

    <ul><li>text 1</li><li>text 2</li><li>text 3</li></ul>
    
    <script>
      var li = document.querySelector('li');
      console.log(li.parentNode.nodeName); // "ul"
    </script>
    
  • parentElement / 回传当前节点的父元素节点,如果该元素没有父节点或者父节点不是一个 DOM 元素,则回传 null。

子关系 API

  • childNodes / 回传一个即时的 NodeList,表示元素的子节点列表,子节点可能会包含文本节点,注释节点等,这边我们可以透过 Node.hasChildNodes() 来检查看看 DOM 节点是否有子节点:

    var node = document.querySelector('#hi');
    
    // 如果 node 内有子元素
    if( node.hasChildNodes() ) {
    
        // 可以透过 node.childNodes[n] (n 为数字索引) 取得对应的节点
        // 注意,NodeList 物件内容为即时更新的集合
        for (var i = 0; i < node.childNodes[i].length; i++) {
          // ...
        };
    }
    

    Node.childNodes 可能会回传的有:

    • HTML 元素节点 (element nodes)
    • 文字节点 (text nodes)、文字间的空白
    • 注解节点 (comment nodes)
  • firstChild / 取得 node 第一个子节点,如果没有子节点,则回传 null

    底下可以看到子节点也会抓取空白行,因此这里抓到的会是 ulli 中间的空白行,所以回传 undefined

    <ul>
    	<li>text 1</li>
    	<li>text 2</li>
    	<li>text 3</li>
    </ul>
    
    <script>
    	var ul = document.querySelector('ul');
    
    	// tagName 属性可以取得 node 的标签名称
    	console.log(ul.firstChild.tagName); // undefined
    </script>
    

    如果我们改成这样:

    <ul><li>text 1</li><li>text 2</li><li>text 3</li></ul>
    
    <script>
    	var ul = document.querySelector('ul');
    	console.log(ul.firstChild.tagName); // LI
    </script>
    

    少了空白行就可以正确抓取我们要的第一个子节点。

  • lastChild / 取得 node 最後一个子节点,如果没有子节点,则回传 null。

    <ul>
    	<li>text 1</li>
    	<li>text 2</li>
    	<li>text 3</li>
    </ul>
    
    <script>
      var ul = document.querySelector('ul');
    
      // textContent 属性可以取得节点内的文字内容
      console.log(ul.lastChild.textContent); // '\n\t' (换行字元)
    </script>
    

    这边跟 firstChild ****的情况一样,所以我们同样改成底下范例,把空行去掉:

    <ul><li>text 1</li><li>text 2</li><li>text 3</li></ul>
    
    <script>
      var ul = document.querySelector('ul');
      console.log(ul.lastChild.textContent); // 'text 3'
    </script>
    

    也成功抓取到我们要的最後一个子节点。

  • hasChildNodes / 这个方法会回传一个布林值,用途在上面有说到,他替我们抓取当前是否含有子节点。

    底下给一个范例,如果 id 为 a 的这个元素有子节点,则从 DOM 中删除第一个子节点:

    var a = document.getElementById("a");
    
    if ( foo.hasChildNodes() ) {
      foo.removeChild(a.childNodes[0]);
    }
    

兄弟关系 API

  • previousSibling / 回传当前节点的前一个兄弟节点,没有则回传 null

    <ul><li>text 1</li><li>text 2</li><li>text 3</li></ul>
    
    <script>
      var li = document.querySelector('li');
    	// 这里已经没有前一个兄弟节点
      console.log(li.previousSibling); // null
    
    	// document.querySelectorAll 会取得所有条件符合的元素
    	// document.querySelectorAll('span')[2] 指的是「第三个」符合条件元素
    	var li2 = document.querySelectorAll('li')[2];
      console.log(li2.previousSibling.textContent); // 'text 2'	
    </script>
    
  • nextSibling / 回传当前节点的下一个兄弟节点,没有则回传 null

    <ul><li>text 1</li><li>text 2</li><li>text 3</li></ul>
    
    <script>
      var li = document.querySelector('li');
    	// 这里直接抓取 text 1 的下一个兄弟节点
      console.log(li.nextSibling.textContent); // 'text 2'
    </script>
    

参考资料:

重新认识 JavaScript: Day 12 透过 DOM API 查找节点
JavaScript操作DOM常用的API


<<:  Day 20 : 动态爬虫-利用webdriver达到自动登入

>>:  [第18天]理财达人Mx. Ada-证券扣款帐户银行余额资讯

Unity - VR - Step运用

制作 VR 虚拟实境游戏控制玩家在 3D 空间移动,实际上是一个重要的问题,使用键盘滑鼠缺乏真实体验...

[Python 爬虫这样学,一定是大拇指拉!] DAY09 - TCP / UDP

本篇将简单介绍 TCP 与 UDP,藉由介绍两者的差异,来解释为何 TCP 为什麽会比较耗时间,然而...

#28 JS: Timing Events - Part 2

After introducing about the 2 methods for timing e...

[Day 1] JavaScript 的运行

在了解 JavaScript 如何运行前,首先要先知道,我们所撰写的 JavaScript 是无法直...

利用大数据分析预测MLB胜负(上)

本文将要介绍由Andrew Y. Cui撰写的《Forecasting Outcomes of Ma...