对 DOM 的两个主要的扩展是 Selectors API(选择符 API)和 HTML5。
11.1 选择符 API
Selectors API Level 1的核心是两个方法: querySelector() 和 querySelectorAll() 。
-
11.1.1 querySelector() 方法
querySelector() 方法接收一个 CSS 选择符,返回与该模式匹配的第一个元素,如果没有找到匹 配的元素,返回 null 。
//取得 body 元素 var body = document.querySelector("body"); //取得 ID 为"myDiv"的元素 var myDiv = document.querySelector("#myDiv"); //取得类为"selected"的第一个元素 var selected = document.querySelector(".selected"); //取得类为"button"的第一个图像元素 var img = document.body.querySelector("img.button"); -
11.1.2 querySelectorAll() 方法
querySelectorAll() 方法接收的参数与 querySelector() 方法一样,都是一个 CSS 选择符,但 返回的是所有匹配的元素而不仅仅是一个元素。这个方法返回的是一个 NodeList 的实例。
//取得某<div>中的所有<em>元素(类似于 getElementsByTagName("em")) var ems = document.getElementById("myDiv").querySelectorAll("em"); //取得类为"selected"的所有元素 var selecteds = document.querySelectorAll(".selected"); //取得所有<p>元素中的所有<strong>元素 var strongs = document.querySelectorAll("p strong"); -
11.1.3 matchesSelector() 方法
Selectors API Level 2 规范为 Element 类型新增了一个方法 matchesSelector() 。这个方法接收 一个参数,即 CSS 选择符,如果调用元素与该选择符匹配,返回 true ;否则,返回 false 。
if (document.body.matchesSelector("body.page1")){ //true }
11.2 元素遍历
-
Element Traversal API 为 DOM 元素添加了以下 5 个属性:
- childElementCount :返回子元素(不包括文本节点和注释)的个数。
- firstElementChild :指向第一个子元素; firstChild 的元素版。
- lastElementChild :指向最后一个子元素; lastChild 的元素版。
- previousElementSibling :指向前一个同辈元素; previousSibling 的元素版。
- nextElementSibling :指向后一个同辈元素; nextSibling 的元素版。
//未使用 Element Traversal 新增的元素 var i, len, child = element.firstChild; while(child != element.lastChild){ if (child.nodeType == 1){ //检查是不是元素 processChild(child); } child = child.nextSibling; } //使用 Element Traversal 新增的元素 var i, len, child = element.firstElementChild; while(child != element.lastElementChild){ processChild(child); //已知其是元素 child = child.nextElementSibling; }
11.3 HTML5
-
11.3.1 与类相关的扩充
-
- getElementsByClassName() 方法
getElementsByClassName() 方法接收一个参数,即一个包含一或多个类名的字符串,返回带有 指定类的所有元素的 NodeList 。传入多个类名时,类名的先后顺序不重要。
//取得所有类中包含"username"和"current"的元素,类名的先后顺序无所谓 var allCurrentUsernames = document.getElementsByClassName("username current"); //取得 ID 为"myDiv"的元素中带有类名"selected"的所有元素 var selected = document.getElementById("myDiv").getElementsByClassName("selected");-
- classList 属性
操作类名时,需要通过 className 属性添加、删除和替换类名。
//删除"user"类 //首先,取得类名字符串并拆分成数组 var classNames = div.className.split(/\s+/); //找到要删的类名 var pos = -1, i, len; for (i=0, len=classNames.length; i < len; i++){ if (classNames[i] == "user"){ pos = i; break; } } //删除类名 classNames.splice(i,1); //把剩下的类名拼成字符串并重新设置 div.className = classNames.join(" ");- 新类型还定义如下方法:
- add(value) :将给定的字符串值添加到列表中。如果值已经存在,就不添加了。
- contains(value) :表示列表中是否存在给定的值,如果存在则返回 true ,否则返回 false 。
- remove(value) :从列表中删除给定的字符串。
- toggle(value) :如果列表中已经存在给定的值,删除它;如果列表中没有给定的值,添加它。 这样,前面那么多行代码用下面这一行代码就可以代替了:
//删除"disabled"类 div.classList.remove("disabled"); //添加"current"类 div.classList.add("current"); //切换"user"类 div.classList.toggle("user"); //确定元素中是否包含既定的类名 if (div.classList.contains("bd") && !div.classList.contains("disabled")){ //执行操作 ) //迭代类名 for (var i=0, len=div.classList.length; i < len; i++){ doSomething(div.classList[i]); }
-
-
11.3.2 焦点管理
HTML5 也添加了辅助管理 DOM 焦点的功能。首先就是 document.activeElement 属性,这个 属性始终会引用 DOM 中当前获得了焦点的元素。元素获得焦点的方式有页面加载、用户输入(通常是 通过按 Tab 键)和在代码中调用 focus() 方法。
var button = document.getElementById("myButton"); button.focus(); alert(document.activeElement === button); //true另外就是新增了 document.hasFocus() 方法
var button = document.getElementById("myButton"); button.focus(); alert(document.hasFocus()); //true -
11.3.3 HTMLDocument 的变化
HTML5 扩展了 HTMLDocument ,增加了新的功能。与 HTML5 中新增的其他 DOM扩展类似,这些 变化同样基于那些已经得到很多浏览器完美支持的专有扩展。
-
- readyState 属性
Document 的 readyState 属性有两个可能的值: loading ,正在加载文档; / complete ,已经加载完文档。
if (document.readyState == "complete"){ //执行操作 }-
- 兼容模式
IE 为此给 document 添加了一个名为 compatMode 的属性,这个属性就是为了告诉开发人员浏 览器采用了哪种渲染模式。
if (document.compatMode == "CSS1Compat"){ alert("Standards mode"); } else { alert("Quirks mode"); }-
- head 属性 HTML5 新增了 document.head 属性。
var head = document.head || document.getElementsByTagName("head")[0]; -
-
11.3.4 字符集属性
HTML5 新增了几个与文档字符集有关的属性。其中, charset 属性表示文档中实际使用的字符集, 也可以用来指定新字符集。
alert(document.charset); //"UTF-16" document.charset = "UTF-8"; -
11.3.5 自定义数据属性
HTML5规定可以为元素添加非标准的属性,但要添加前缀 data- ,目的是为元素提供与渲染无关的 信息,或者提供语义信息。这些属性可以任意添加、随便命名,只要以 data- 开头即可。
<div id="myDiv" data-appId="12345" data-myname="Nicholas"></div> var div = document.getElementById("myDiv"); //取得自定义属性的值 var appId = div.dataset.appId; var myName = div.dataset.myname; //设置值 div.dataset.appId = 23456; div.dataset.myname = "Michael"; //有没有"myname"值呢? if (div.dataset.myname){ alert("Hello, " + div.dataset.myname); } -
11.3.6 插入标记
-
- innerHTML 属性
div.innerHTML = "Hello world!";-
- outerHTML 属性
// 使用 outerHTML 属性以下面这种方式设置值: div.outerHTML = "<p>This is a paragraph.</p>"; // 这行代码完成的操作与下面这些 DOM 脚本代码一样: var p = document.createElement("p"); p.appendChild(document.createTextNode("This is a paragraph.")); div.parentNode.replaceChild(p, div);-
- insertAdjacentHTML() 方法
insertAdjacentHTML() 方法,它接收两个参数:插入位置和要插入的 HTML 文本。
- 第一个参数必须是下列值之一:
- "beforebegin" ,在当前元素之前插入一个紧邻的同辈元素;
- "afterbegin" ,在当前元素之下插入一个新的子元素或在第一个子元素之前再插入新的子元素;
- "beforeend" ,在当前元素之下插入一个新的子元素或在最后一个子元素之后再插入新的子元素;
- "afterend" ,在当前元素之后插入一个紧邻的同辈元素。
//作为前一个同辈元素插入 element.insertAdjacentHTML("beforebegin", "<p>Hello world!</p>"); //作为第一个子元素插入 element.insertAdjacentHTML("afterbegin", "<p>Hello world!</p>"); //作为最后一个子元素插入 element.insertAdjacentHTML("beforeend", "<p>Hello world!</p>"); //作为后一个同辈元素插入 element.insertAdjacentHTML("afterend", "<p>Hello world!</p>"); -
-
11.3.7 scrollIntoView() 方法 scrollIntoView() 方法,以方便开发人员更好地控制页面滚动。
11.4 专有扩展
- 11.4.1 文档模式
IE8 引入了一个新的概念叫“文档模式”(document mode)。页面的文档模式决定了可以使用什么功 能。换句话说,文档模式决定了你可以使用哪个级别的 CSS,可以在 JavaScript 中使用哪些 API,以及如何对待文档类型(doctype)。
到了 IE9,总共有以下 4 种文档模式:
IE5:以混杂模式渲染页面(IE5 的默认模式就是混杂模式)。IE8 及更高版本中的新功能都无法 使用。
IE7:以 IE7 标准模式渲染页面。IE8 及更高版本中的新功能都无法使用。
IE8:以 IE8 标准模式渲染页面。IE8 中的新功能都可以使用,因此可以使用 Selectors API、更多 CSS2 级选择符和某些 CSS3 功能,还有一些 HTML5 的功能。不过 IE9 中的新功能无法使用。
IE9:以 IE9 标准模式渲染页面。IE9 中的新功能都可以使用,比如 ECMAScript 5、完整的 CSS3 以及更多 HTML5 功能。这个文档模式是最高级的模式。
-
11.4.2 children 属性
这个属性是 HTMLCollection 的实例,只包含元素中同样还是元素的子节点。除此之外, children 属性与 childNodes 没有什么区别,即在元素只包含元素子节点时,这两个属性的值相同。
-
11.4.3 contains() 方法
如果被检测的节点是后代节点,该方法返回 true ;否则,返回 false 。
alert(document.documentElement.contains(document.body)); //true -
11.4.4 插入文本
-
- innerText 属性
div.innerText = "Hello world!";-
- outerText 属性
div.outerText = "Hello world!"; 这行代码实际上相当于如下两行代码: var text = document.createTextNode("Hello world!"); div.parentNode.replaceChild(text, div); -
-
11.4.5 滚动
-
scrollIntoViewIfNeeded(alignCenter) :只在当前元素在视口中不可见的情况下,才滚 动浏览器窗口或容器元素,最终让它可见。
-
scrollByLines(lineCount) :将元素的内容滚动指定的行高, lineCount 值可以是正值, 也可以是负值。
-
scrollByPages(pageCount) :将元素的内容滚动指定的页面高度,具体高度由元素的高度决 定。
//将页面主体滚动 5 行 document.body.scrollByLines(5); //在当前元素不可见的时候,让它进入浏览器的视口 document.images[0].scrollIntoViewIfNeeded(); //将页面主体往回滚动 1 页 document.body.scrollByPages(-1); -
11.5 小结
虽然 DOM 为与 XML 及 HTML 文档交互制定了一系列核心 API,但仍然有几个规范对标准的 DOM 进行了扩展。这些扩展中有很多原来是浏览器专有的,但后来成为了事实标准,于是其他浏览器也都提 供了相同的实现。本章介绍的三个这方面的规范如下。
Selectors API,定义了两个方法,让开发人员能够基于 CSS 选择符从 DOM中取得元素,这两个 方法是 querySelector() 和 querySelectorAll() 。
Element Traversal,为 DOM 元素定义了额外的属性,让开发人员能够更方便地从一个元素跳到 另一个元素。之所以会出现这个扩展,是因为浏览器处理 DOM 元素间空白符的方式不一样。
HTML5,为标准的 DOM 定义了很多扩展功能。其中包括在 innerHTML 属性这样的事实标准基 础上提供的标准定义,以及为管理焦点、设置字符集、滚动页面而规定的扩展 API。
虽然目前 DOM扩展的数量还不多,但随着 Web 技术的发展,相信一定还会涌现出更多扩展来。很 多浏览器都在试验专有的扩展,而这些扩展一旦获得认可,就能成为“伪”标准,甚至会被收录到规范 的更新版本中。