遍历文件


只要你获取了文件中某个节点(Node),你就可以获取它的父节点、子节点、邻接节点等,相关的特性有:

  • 获取父节点:parentNode
  • 前邻接节点:previousSibling
  • 后邻接节点:nextSibling
  • 首个子节点:firstChild
  • 最后一个子节点:lastChild
  • 所有直接子节点:childNodes

以〈W3C DOM 简介〉中的文件为例:

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

这份 HTML 文件,假设你使用document.body获取body节点,假设就是被body变量参考,从body开始,各特性可以获取的节点标示如下(为了简化,不考虑换行与缩排的文本节点):

document                             
       |-html(body.parentNode)                       
            |-head(body.previousSibling)
            |    |-title             
            |          |-首页        
            |body
                 |-h1(body.firstNode)
                 |  |-Hello!World!   
                 |-a(body.lastNode)
                   |-学习笔记

如果是body.childNodes,则获取NodeList对象,这是一个类数组具有索引访问的特性,索引从 0 开始就是第一个子节点,所以body.childNodes[0]就是h1节点,body.childNodes[1]就是a节点。

NodeList规范的是使用item()搭配索引来获取元素,不过在 JavaScript 中,可以直接使用[]搭配索引来获取元素。)

实际上不建议透过以上特性来访问元素,因为这会强烈依赖文件结构,一旦调整了文件内容,程序可能就无法运作了。

常见的作法是,如果节点是元素(Element),透过元素的getElementsByTagNamegetElementByIdgetElementsByClassName方法来获取节点。例如若想获取文件中所有的<p>标签代表的元素,则可以如下:

let ps = document.getElementsByTagName('p');

由于文件中可能不只有一个<p>标签,获取的节点也可能不只一个,getElementsByTagName获取的是HTMLCollection,可依标签在文件中的顺序使用索引获取对应节点。例如,document.getElementsByTagName('p')[0]获取的就是文件中第一个出现的<p>标签对应的节点。

如果标签上定义有id属性,则可以使用getElementById来获取元素。例如文件中有个<div>

<div id="console">Console here</div>

则可以透过以下获取:

let console = document.getElementById('console');

id在文件中基本上应该是独一无二的,如果文件中出现重复的id,那getElementById获取的会是文件中第一个符合的元素。

如果元素上定义有class,可以透过getElementsByClassName来查找,例如若有个 HTML 片段:

<div class="article newest">
    <div class="sport">OOOOO</div>
</div>
<div class="article">
    <div class="music">XXXXX</div>
</div>

那么可以使用底下片段获取HTMLCollection,包含全部class属性上设定有article的元素:

let articles = document.getElementsByClassName('article');

透过getElementsByTagNamegetElementByIdgetElementsByClassName方法获取的元素,是以该元素为根的 DOM 树,因而可以进一步获取子元素。例如文件有以下的内容:

<div id="test">
    <div>Test 1 Here</div>
    <div>Test 2 Here</div>
</div>

以下可以获取 Test 1 Here 的内容:

let testDiv = document.getElementById('test');
let test1DivHtml = testDiv.getElementsByTagName('div')[0].innerHTML;

innerHTML可以获取标签内含之 HTML,以字符串形态返回,过去innerHTML不是标准特性,不过几乎所有浏览器都支持它,而 HTML 5 正式将innerHTML纳入标准。

如果是HTMLDocument,会有个getElementsByName方法,只要标签上有设定name属性,就可以使用这个方法来获取对应元素。HTML 文件中document就是HTMLDocument,所以就可以使用这个方法。标签的name属性值可以重复,所以getElementsByName获取的不只一个元素,会以HTMLCollection收集符合的元素。

获取某个节点或元素,自然会想要知道有关这个节点或元素的一些信息。例如方才使用innerHTML获取元素内含的 HTML 就是一个例子。另外,经常地,会想要得知元素的属性为何,例如获取最上面列出的 HTML 文件中,<a>href属性:

let href = document.getElementsByTagName('a')[0].href;

如果要以标准方式,可以透过getAttribute方法来获取标签的属性值。例如:

let href = document.getElementsByTagName('a')[0].getAttribute('href');

使用标准的作法,好处是处理像class属性这样的东西比较方便。例如有个标签:

<div id="console" class="demo">DEMO</div>

如果要用特性的方式获取,由于class是保留字(在 ES6 中也被用来定义类了),必须改用className

let clzName = document.getElementById('console').className;

但使用标准作法,可以这么写:

var clzName = document.getElementById('console').getAttribute('class');

<label>for属性也是类似,由于for是 JavaScript 的关键字,使用特性获取时,必须改用htmlFor

let htmlFor = document.getElementById('someLabel').htmlFor;

但使用标准方法的话,可以这么写:

let htmlFor = document.getElementById('someLabel').getAttribute('for');

要以特性方式要获取标签设置的属性,特性名称要注意大小写的问题,通常会是驼峰式命名。例如要获取 HTML 中设置的属性如cellspacingcolspanframebordermaxlengthreadonlyrowspantabindexusemap等,要透过 DOM 的特性获取则必须是cellSpacingcolSpanframeBordermaxLengthreadOnlyrowSpantabIndexuseMap等。

若使用getAttribute则通常不用注意大小写。例如若要获取某个<input>元素的readonly属性,则使用getAttribute('readonly')getAttribute('readOnly')getAttribute('Readonly')等任意大小写组合都是可以的。

float特性是 JavaScript 的保留字(虽然目前没有使用),标准使用了cssFloat名称。

或许访问属性最常见的,就是获取表单中某个<input>标签中的value属性。例如:

<input id="username" name="user" value="caterpillar">

可以透过以下来获取字段值:

let username = document.getElementById('username').value;

有个初学者常犯的错误,例如,想获取 HTML 文件中<a>标签间的文本,却编写如下:

let text = document.getElementsByTagName('a')[0].value;

这是错的!<a>上面并没有value属性,对应的对象上也没有value特性,而且要记得,文本也是一个节点,所以要先获取文本节点,也就是<a>的子节点,再使用data(定义在Text)或nodeValue(定义在Node)获取文本本身:

let a = document.getElementsByTagName('a')[0];
let text = a.firstChild.data;

你也可以透过querySelectorquerySelectorAll方法,搭配〈CSS 选择器语法〉来选取元素,底下举几个简单的例子,像是…

let testDiv = document.getElementById('test');

可以使用底下程序达到相同效果:

let testDiv = document.querySelector('#test');

querySelector始终返回第一个匹配的元素,因此像是标签选择器:

let div = document.querySelector('div');

只会返回第一个遇到的<div>标签,如果想要获取全部的标签,可以使用querySelectorAll,也就是底下这个片段:

let ps = document.getElementsByTagName('p');

可以改成:

let ps = document.querySelectorAll('p');

querySelectorAll会返回NodeList,可透过[]来指定索引获取个别元素。

先前看过的:

let articles = document.getElementsByClassName('article');

改用querySelectorAll的话,可以写成:

let articles = document.querySelectorAll('.article');

基本上,getElementsByTagNamegetElementByIdgetElementsByClassName方法能够获取的元素,就使用相对应的方法,因为效率会比querySelectorquerySelectorAll高一些,而由于querySelectorquerySelectorAll是原生 API,如果过去你使用其他程序库以程序流程实现的选择器,可以改成querySelectorquerySelectorAll,以获得更高的效率。


展开阅读全文