[TOC]

尽管 DOM API 已经相当不错,但仍然不断有标准或专有的扩展出现,以支持更多功能。

W3C 开始着手将这些已成为事实标准的专有扩展编 制成正式规范。

基于以上背景,诞生了描述 DOM 扩展的两个标准:Selectors API 与 HTML5。这两个标准体现了社 区需求和标准化某些手段及 API 的愿景。

Selectors API

通过浏览器原生支持这个 API,解析和遍历 DOM 树可以通过底层编译语言实现,性能也有 了数量级的提升。

Selectors API Level 1 的核心是两个方法:querySelector()和 querySelectorAll()。
在兼容浏 览器中,Document 类型和 Element 类型的实例上都会暴露这两个方法。

Selectors API Level 2 规范在 Element 类型上新增了更多方法,比如 matches()、find()和 findAll()。
不过,目前还没有浏览器实现或宣称实现 find()和 findAll()。

选择第一个元素 querySelector( )

在 Document 上使用 querySelector()方法时,会从文档元素开始搜索;
在 Element 上使用 querySelector()方法时,则只会从当前元素的后代中查询。

只选择第一个元素。

// 取得<body>元素
let body = document.querySelector("body"); 
let div = body.querySelector("div"); // 在body子元素中取得第一个div元素

// 取得 ID 为"myDiv"的元素
let myDiv = document.querySelector("#myDiv"); 

// 取得类名为"selected"的第一个元素
let selected = document.querySelector(".selected"); 

// 取得类名为"button"的图片
let img = document.body.querySelector("img.button");

选择所有元素 querySelectorAll( )

在 Document 上使用 querySelector()方法时,会从文档元素开始搜索;
在 Element 上使用 querySelector()方法时,则只会从当前元素的后代中查询。

选择匹配的所有元素,返回Nodelist对象。

但它是一 个静态的“快照”,而非“实时”的查询。这样的底层实现避免了使用 NodeList 对象可能造成的性 能问题。

// 取得 ID 为"myDiv"的<div>元素中的所有<em>元素
let ems = document.getElementById("myDiv").querySelectorAll("em"); 

// 取得所有类名中包含"selected"的元素
let selecteds = document.querySelectorAll(".selected"); 

// 取得所有是<p>元素子元素的<strong>元素
let strongs = document.querySelectorAll("p strong"); 

//返回的 NodeList 对象可以通过 for-of 循环、item()方法或中括号语法取得个别元素。比如:
let strongElements = document.querySelectorAll("p strong"); 

// 以下 3 个循环的效果一样
for (let strong of strongElements) { 
 strong.className = "important"; 
} 

for (let i = 0; i < strongElements.length; ++i) { 
 strongElements.item(i).className = "important"; 
} 

for (let i = 0; i < strongElements.length; ++i) { 
 strongElements[i].className = "important"; 
}

是否匹配 matches( )

matches()方法接收一个 CSS 选择符参数,如果元素 匹配则该选择符返回 true,否则返回 false。

使用这个方法可以方便地检测某个元素会不会被 querySelector()或 querySelectorAll()方 法返回。

if (document.body.matches("body.page1")){ 
 // true 
}

=====================

元素遍历(新)

IE9 之前的版本不会把元素间的空格当成空白节点,而其他浏览器则会。

这样就导致了 childNodes 和 firstChild 等属性上的差异。为了弥补这个差异,同时不影响 DOM 规范,W3C 通过新的 Element Traversal 规范定义了一组新属性。

在支持的浏览器中,所有 DOM 元素都会有这些属性,为遍历 DOM 元素提供便利。这样开发者就 不用担心空白文本节点的问题了。

let parentElement = document.getElementById('parent'); 

// 1、返回子元素数量
let childNum = parentElement.childElementCount;

// 2、指向第一个 Element 类型的子元素(Element 版 firstChild);
let currentChildElement = parentElement.firstElementChild; 

// 循环遍历,没有子元素,firstElementChild 返回 null,跳过循环
while (currentChildElement) { 
        // 3、指向最后一个 Element 类型的子元素(Element 版 lastChild);
   if (currentChildElement === parentElement.lastElementChild) { 
     break; 
   } 
  // 4、指向后一个 Element 类型的同胞元素(Element 版 nextSibling)
   currentChildElement = currentChildElement.nextElementSibling; 

  // 5、指向前一个 Element 类型的同胞元素( Element 版previousSibling);
  let x = currentChildElement.previousElementSibling;
}

=====================

HTML5

HTML5 代表着与以前的 HTML 截然不同的方向。在所有以前的 HTML 规范中,从未出现过描述 JavaScript 接口的情形,HTML 就是一个纯标记语言。

JavaScript 绑定的事,一概交给 DOM 规范去定义。

然而,HTML5 规范却包含了与标记相关的大量 JavaScript API 定义。其中有的 API 与 DOM 重合, 定义了浏览器应该提供的 DOM 扩展。

1、CSS 类扩展

获取元素 getElementsByClassName()

这个方法脱胎于基于原有 DOM 特性实现该功能的 JavaScript 库,提供了性能高好 的原生实现。

返回值是 NodeList。

// 取得所有类名中包含"username"和"current"元素(交集)

// 这两个类名的顺序无关紧要
let allCurrentUsernames = document.getElementsByClassName("username current"); 

// 取得 ID 为"myDiv"的元素子树中所有包含"selected"类的元素
let selected = document.getElementById("myDiv").getElementsByClassName("selected");

操作类 classList

要操作类名,可以通过 className 属性实现添加、删除和替换。

HTML5 通过给所有元素增加 classList 属性为这些操作提供了更简单也更安全的实现方式。

classList 是一个新的集合类型 DOMTokenList 的实例。

与其他 DOM 集合类型一样,DOMTokenList 也有 length 属性表示自己包含多少项,也可以通过 item()或中括号取得个别的元素。

// <div class="bd user disabled">...</div>
let div = document.getElementsByClassName("bd user disabled"); 

// 删除"disabled"类
div.classList.remove("disabled"); 

// 添加"current"类
div.classList.add("current"); 

// 切换"user"类,如果类名列表中已经存在"user",则删除;如果不存在,则添加
div.classList.toggle("user"); 

// 检测类名,contains表示给定的 类名 是否存在
if (div.classList.contains("bd") && !div.classList.contains("disabled")){ 
 // 执行操作
) 

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

2、焦点管理

HTML5 增加了辅助 DOM 焦点管理的功能。

页面加载时,可以通过用户输入(按 Tab 键或代码中使用 focus()方法)让某个 元素自动获得焦点。

默认情况下,document.activeElement 在页面刚加载完之后会设置为 document.body。

而在页面完全加载之前,document.activeElement 的值为 null。

let button = document.getElementById("myButton"); 

button.focus();  // 获取焦点

console.log(document.hasFocus()); // true 返回布尔值,表示文档是否拥有焦点

确定文档是否获得了焦点,就可以帮助确定用户是否在操作页面。

第一个方法可以用来查询文档,确定哪个元素拥有焦点,第二个方法可以查询文档是否获得了焦点, 而这对于保证 Web 应用程序的无障碍使用是非常重要的。

无障碍 Web 应用程序的一个重要方面就是焦 点管理,而能够确定哪个元素当前拥有焦点(相比于之前的猜测)是一个很大的进步。

3、HTMLDocument 扩展

文档加载 readyState

document.readyState 属性有两个可能的值:
1、loading,表示文档正在加载;
2、complete,表示文档加载完成。

实际开发中,最好是把 document.readState 当成一个指示器,以判断文档是否加载完毕。

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

页面渲染模式 compatMode

if (document.compatMode == "CSS1Compat"){ 
  console.log("标准模式"); 
} else { 
  console.log("混杂模式"); // "BackCompat"
}

头部属性 head

let head = document.head; // 指向文档的<head>元素

4、字符集属性 characterSet

这个属性的默认值是”UTF-16”,但可以通过元素或响应头, 以及新增的 characterSeet 属性来修改。

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

5、自定义数据属性 data-

使用前缀 data-以便告诉浏览器,这些属性既不包含 与渲染有关的信息,也不包含元素的语义信息。

定义了自定义数据属性后,可以通过元素的 dataset 属性来访问。

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

// 本例中使用的方法仅用于示范
let div = document.getElementById("myDiv"); 

// 取得自定义数据属性的值
let appId = div.dataset.appId; // "12345"
let myName = div.dataset.myname; // "Nicholas"
//注意 data-my-name、data-My-Name 要通过 myName 来访问

// 设置自定义数据属性的值
div.dataset.appId = 23456; 
div.dataset.myname = "Michael"; 

// 有"myname"吗?
if (div.dataset.myname){ 
 console.log(`Hello, ${div.dataset.myname}`); 
}

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

另外,单页应用程序框架也非常多地使用了自定义数据属性。

6、插入标记

DOM 虽然已经为操纵节点提供了很多 API,但向文档中一次性插入大量 HTML 时还是比较麻烦。

相比先创建一堆节点,再把它们以正确的顺序连接起来,直接插入一个 HTML 字符串要简单(快速) 得多。

HTML 字符串 innerHTML

在读取 innerHTML 属性时,会返回元素所有后代的 HTML 字符串,包括元素、注释和文本节点。

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