[TOC]

HTML5代表着与以前的HTML截然不同的方向。
在所有以前的HTML规范中,从未出现过描述JavaScript接口的情形,HTML就是一个纯标记语言。
JavaScript绑定的事,一概交给DOM规范去定义。
然而,HTML5规范却包含了与标记相关的大量JavaScript API定义。
其中有的API与DOM重合,定义了浏览器应该提供的DOM扩展。
注:因为HTML5覆盖的范围极其广泛,所以本节主要讨论其影响所有DOM节点的部分。

15.3.1 CSS类扩展

自HTML4被广泛采用以来,Web开发中一个主要的变化是class属性用得越来越多,其用处是为元素添加样式以及语义信息。自然地,JavaScript与CSS类的交互就增多了,包括动态修改类名,以及根据给定的一个或一组类名查询元素,等等。为了适应开发者和他们对class属性的认可,HTML5增加了一些特性以方便使用CSS类。

1.getElementsByClassName()

getElementsByClassName()是HTML5新增的最受欢迎的一个方法,暴露在document对象和所有HTML元素上。这个方法脱胎于:基于原有DOM特性实现该功能的JavaScript库,提供了性能高好的原生实现。getElementsByClassName()方法接收一个参数,即包含一个或多个类名的字符串,返回类名中包含相应类的元素的NodeList。如果提供了多个类名,则顺序无关紧要。

// 取得所有类名中包含"username"和"current"元素
// 这两个类名的顺序无关紧要
let allCurrentUsernames = document.getElementsByClassName('username current');
// 取得ID为"myDiv"的元素子树中所有包含"selected"类的元素
let selected = document.getElementById('myDiv').getElementsByClassName('selected');

这个方法只会返回以调用它的对象为根元素的子树中所有匹配的元素。
在document上调用getElementsByClassName(),返回文档中所有匹配的元素;
在特定元素上调用getElementsByClassName(),返回该元素后代中匹配的元素。
若要给包含特定类(而不是特定ID或标签)的元素添加事件处理程序,使用这个方法会很方便。
但是要记住,因为返回值是NodeList,所以使用这个方法会遇到跟使用getElementsByTagName()和其他返回NodeList对象的DOM方法同样的问题。
IE9及以上版本,以及所有现代浏览器都支持getElementsByClassName()方法。

2.classList属性

要操作类名,可通过className属性实现添加、删除和替换。
但className是一个字符串,所以每次操作之后,都需要重新设置这个值才能生效,即使只改动了部分字符串也一样。

<div class="bd user disabled"></div>

这个

元素有3个类名。
要想删除其中一个,先把className拆开,删除不想要的那个,再把包含剩余类的字符串设置回去。

// 要删除"user"类
let targetClass = 'user';
// 把类名拆成数组
let classNames = div.className.split(/\s+/);
// \s    匹配任何空白字符,包括空格、制表符、换页符等等。等价于 [ \f\n\r\t\v]

// 找到要删除类名的索引
let idx = classNames.indexOf(targetClass);
// 如果有则删除
if (idx > -1) {
  classNames.splice(i, 1);
}
// 重新设置类名
div.className = classNames.join(' ');

替换类名和检测类名也要涉及同样的算法。
添加类名只涉及字符串拼接,但必须先检查以确保不会重复添加相同的类名。
HTML5给所有元素增加classList属性。
classList是一个新的集合类型DOMTokenList的实例。
与其他DOM集合类型一样,DOMTokenList也有length属性表示自己包含多少项,也可通过item()或中括号取得个别的元素。
DOMTokenList还增加了以下方法:
❑ add(value),向类名列表中添加指定的字符串值value。如果这个值已经存在,则什么也不做。
❑ contains(value),返回布尔值,表示给定的value是否存在。
❑ remove(value),从类名列表中删除指定的字符串值value。
❑ toggle(value),如果类名列表中已经存在指定的value,则删除;如果不存在,则添加。
代码简化:

div.classList.remove('user');

在不影响其他类名的情况下完成删除。
其他方法同样极大地简化了操作类名的复杂性

// 检测类名
if (div.classList.contains('bd') && div.classList.contains('disabled')) {
  // 执行操作
}

// 迭代类名
for (let class of div.classList) {
  doStuff(class);
}

添加了classList属性之后,除非是完全删除或完全重写元素的class属性,否则className属性就用不到了。

15.3.2 焦点管理

HTML5增加了辅助DOM焦点管理的功能。
首先是document.activeElement,始终包含当前拥有焦点的DOM元素。
页面加载时,可以通过用户输入(按Tab键或代码中使用focus()方法)让某个元素自动获得焦点。

let button = document.getElementById('myButton');
button.focus();
console.log(document.activeElement === button);  // true

其次是document.hasFocus()方法,该方法返回布尔值,表示文档是否拥有焦点

let button = document.getElementById('myButton');
button.focus();
console.log(document.hasFocus());  // true

确定文档是否获得了焦点,就可以帮助确定用户是否在操作页面。
第一个方法可以用来查询文档,确定哪个元素拥有焦点
第二个方法可以查询文档是否获得了焦点
而这对于保证Web应用程序的无障碍使用是非常重要的。
无障碍Web应用程序的一个重要方面就是焦点管理,而能够确定哪个元素当前拥有焦点(相比于之前的猜测)是一个很大的进步。

15.3.3 HTMLDocument扩展

1.readyState属性

有两个可能的值:
❑ loading,表示文档正在加载;
❑ complete,表示文档加载完成。
实际开发中,最好是把document.readState当成一个指示器,以判断文档是否加载完毕。

if (document.readyState == 'complete') {
  // 执行操作
}

2.compatMode属性

唯一的任务是指示浏览器当前处于什么渲染模式。

3.head属性

指向文档的元素。
可以像下面这样直接取得元素

let head = document.head;

15.3.4 字符集属性

characterSet属性表示文档实际使用的字符集,也可以用来指定新字符集。
这个属性的默认值是”UTF-16”,但可以通过元素或响应头,以及新增的characterSeet属性来修改。

console.log(document.characterSet);  // UTF-8

15.3.5 自定义数据属性

HTML5允许给元素指定非标准的属性,但要使用前缀data-以便告诉浏览器,这些属性既不包含与渲染有关的信息,也不包含元素的语义信息。
除了前缀,自定义属性对命名是没有限制的,data-后面跟什么都可以。
下面是一个例子:

<div id="myDiv" data-appId="12345" data-myname="Nick"></div>

定义了自定义数据属性后,可通过元素的dataset属性来访问。
dataset属性是一个DOMStringMap的实例,包含一组键/值对映射。
元素的每个data-name属性在dataset中都可以通过data-后面的字符串作为键来访问
(例如,属性data-myname、data-myName可以通过myname访问,但要注意data-my-name、data-My-Name要通过myName来访问)。
下面是一个使用自定义数据属性的例子:

let div = document.getElementById('myDiv');
// 取得自定义数据属性的值
let appId = div.dataset.appId;
let myName = div.dataset.myname;
// 设置自定义数据属性的值
div.dataset.appId = 23456;
div.dataset.myname = 'Mary';
// 有"myname"吗?
if (div.dataset.myname) {
  console.log(`你好,${div.dataset.myname}`);
}
// 你好,Mary

自定义数据属性适用场景:需要给元素附加某些数据
比如链接追踪和在聚合应用程序中标识页面的不同部分。
另外,单页应用程序框架也非常多地使用了自定义数据属性。

15.3.6 插入标记

1.innerHTML属性

读取innerHTML属性时,会返回元素所有后代的HTML字符串,包括元素、注释和文本节点。
写入innerHTML时,则会根据提供的字符串值以新的DOM子树替代元素中原来包含的所有节点。
注:设置innerHTML会导致浏览器将HTML字符串解析为相应的DOM树。这意味着设置innerHTML属性后马上再读出来会得到不同的字符串。这是因为返回的字符串是将原始字符串对应的DOM子树序列化之后的结果。

2.旧IE中的innerHTML

在所有现代浏览器中,通过innerHTML插入的