对DOM的两个主要扩展是Selectors API(选择符API)和HTML5.这两个扩展都是源自开发社区。此外还有一个不那么因为瞩目的ELement Traversal(元素遍历)规范。为DOM添加了一些属性。
选择符API
众多JavaScript 库中最常用的一项功能,就是根据CSS选择符选择与某个模式匹配的DOM元素。 实际上 jQuery 的核心就是通过CSS选择符查询DOM 文档取得元素的引用,从而抛开了 getElementById() 和 getElementsByTagName()。
selectors API Level 1 的核心是两个方法:querySelector() 和 querySelectorAll()。在兼容的浏览器中,可以通过Document 及 Element 类型的实例调用它们。目前已经完全支持 Selectors API Level 1的浏览器有 IE 8+、Firefox 3.5+、Safari 3.1+、Chrome 和 Opera 10+。
querySelector() 方法接收一个CSS 选择符,返回与该模式匹配的第一个元素,如果没有找到匹配的元素,返回null。
querySelectorAll() 方法也是接收一个CSS 选择符,返回一个NodeList 类型的实例。
上面两个方法如果传入了浏览器不支持的选择符或者选择符中有语法错误,则会抛出错误。
元素遍历
对于元素之间的空格,IE9及之前的版本不会返回文本节点,而其他所有浏览器都会返回文本节点。这样就导致了在使用childNodes 和 fristChild 等属性时的行为不一致。为了弥补这一差异,而同时有保持DOM规范不变,Element Traversal 规范 新定义了一组属性。
Element Traversal API 为DOM 元素添加了以下 5个属性。
- childElementCount:返回子元素(不包括文本节点和注释)的个数。
- firstElementChild:指向第一个子元素;firstChild 的元素版。
- lastElementChild:指向最后一个子元素;lastChild的元素版。
- previousElementSibling:指向前一个同辈元素;previousSibling 的元素版。
- nextElementSibling:指向后一个同辈元素;nextSibling 的元素版。
支持Element Traversal 规范的浏览器有 IE9+、 Firefox3.5+、Safari 4+、Chrome 和 Opera 10+。
HTML5
HTML5 规范围绕如何使用新增标记定义了大量的JavaScript API。其中一些API与DOM重叠,定义了浏览器应该支持的DOM 扩展。
与类相关的扩充
class 属性在 HTML文档中应用越来越多,一方面可以通过它为元素添加样式,另一方面还可以用它表示元素的语义。为了简化CSS类的用法,HTML5新增了很多相关的API。
1.getElementsByClassName() 方法
可以通过document对象及所有HTML 元素调用该方法。该方法接收一个参数,即一个包含一个或多个类名的字符串,返回带有指定类的所有元素的NodeList。传入多个类名是,类名的先后顺序不重要。
var allCurrentUserNames = document.getElementsByClassName("username current"); var selected = document.getElementById("myDiv").getElementsByClassName("selected");
支持getElementsByClassName() 的浏览器有 IE9+、 Firefox3+、Safari 3.1+、Chrome 和 Opera 9.5+。
2.classList 属性
由于calssName 中是一个字符串,所以如果想删除或修改某个类名时,就要分析整个字符串,方法如下:
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title>study</title> </head> <body> <div class="bd user disabled">testdiv</div> <script type="text/javascript"> var allDiv = document.getElementsByClassName("disabled bd"); var div = allDiv[0]; var classNames = div.className.split(/\s+/); //find className var pos = -1, i, len; for(i=0, len=classNames.length; i<len; i++){ if(classNames[i] == "user"){ pos = i; break; } } //delete className classNames.splice(i, 1); //join div.className = classNames.join(" "); </script> </body> </html>
HTML5 新增了一个操作类名的方式,可以让操作更加简单也更安全,那就是为所有元素添加了classList 属性。这个classList 属性是新集合类型 DOMTokenList 的实例,与其他集合一样,DOMTokenList 有一个表示自己包含多少元素的 length 属性,而要取得每个元素可以使用 item() 方法,也可以又使用方括号语法。另外这个新类型还有如下方法:
- add(value):将给定的字符串值添加到列表中。如果值已经存在就不添加了。
- contains(value):表示列表中是否存在给定的值,如果存在则返回true,否则返回false。
- remove(value):从列表中删除给定的字符串。
- toggle(value):如果列表中已经存在给定的值,则删除它;如果没有,则添加它。
所以上例中的一坨代码就可以简化成一条:
div.classList.remove("user");
支持classList 属性的浏览器有 Firefox3.6+ 和 Chrome。
焦点管理
HTML5 中添加了辅助管理DOM焦点的功能,首先就是 document.activeElement 属性,这个属性始终会引用DOM中当前获得了焦点的元素。元素获得焦点的方式有页面加载、用户输入(通常是通过按Tab键)和在代码中调用 focus() 方法。
var button = document.querySelector("#mybutton"); button.focus(); alert(document.activeElement == button); //true
默认情况下,文档刚刚加载完成时,document.activeElement 中保存的是 document.body 元素的引用。文档加载期间,document.activeElement 的值是 null。
另外新增了 document.hasFocus() 方法,这个方法用于判确定文档是否获得了焦点。
实现了者两个属性的浏览器包括 IE4+、Firefox 3+、Safari 4+、 Chrome 和 Opera 8+。
HTMLDocument 的变化
HTML5 扩展了 HTMLDocument,增加了新的功能。
1.readyState 属性
Document的 readyState 属性有两个可能的值:
- loading,正在加载的文档;
- complete,已经加载完文档。
使用document.readyState 的最恰当的方式,就是通过它来实现一个指示文档已经加载完成的指示器。
支持readyState 属性的浏览器有 IE4+、Firefox3.6+、Safari 、Chrome 和 Opera 9+ 。
2.兼容模式
document.compateMode 的 值等于 “CSS1Compat”表示浏览器在标准模式下。document.compatMode 值等于“BackCompat” 表示浏览器在混杂模式下。
3.head属性
类似于 document.body HTML5 增加了 document.head 属性来引用文档的 <head> 元素。可以结合使用这个属性和另一种后备方法。
var head = document.head || document.getElementsByTagName("head")[0];
4.自定义数据属性
HTML5规定可以为元素添加非标准的属性,但是要添加前缀 data-,目的是为元素提供与渲染无关的信息,或者提供语义信息。这些属性可以任意添加、随便命名,只要以data- 开头即可。
添加了自定义属性之后,可以通过元素的dataset 属性来访问自定义属性的值。dataset 属性的值是一个DOMStringMap 的实例,也就是一个名值对儿的映射。在这个映射中,每个data-name 形式的属性都会有一个对应的属性,只不过属性名没有 data- 前缀。
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title>study</title> </head> <body> <button id="mybutton" data-apptype="iphone" data-myname="AJ">pushme</button> <script type="text/javascript"> var button = document.querySelector("#mybutton"); var appType = button.dataset.apptype; var myname = button.dataset.myname; alert(appType + " " + myname); button.dataset.apptype = "Android"; button.dataset.myname = "LL"; if(button.dataset.myname){ alert("Hello " + button.dataset.myname); } </script> </body> </html>
注意:以上代码中 dataset 后面的属性不可以用大写,因为HTML中属性都会被变成小写,所以JavaScript 中也必须使用小写才能访问。
支持自定义数据属性的浏览器有Firefox 6+ 和 Chrome。
5.插入标记
innerHTML属性返回与调用元素的所有子节点对应的HTML标记,写入innerHTML 时会根据指定的值创建新的DOM树,然后用这个DOM树完全替换调用元素原先所有的子节点。
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title>study</title> </head> <body> <div id="content"> <p>This is a <strong>paragraph</strong> with a list following it.</p> <ul> <li>1</li> <li>2</li> <li>3</li> </ul> </div> <script type="text/javascript"> var div = document.getElementById("content"); alert(div.innerHTML); /** <p>This is a <strong>paragraph</strong> with a list following it.</p> <ul> <li>1</li> <li>2</li> <li>3</li> </ul> */ div.innerHTML = "Hello World!"; </script> </body> </html>
outerHTML属性,返回调用它的元素及所有子节点的HTML标签。在写入的情况下,outerHTML会根据指定的HTML 字符串创建新的DOM子树,然后用这个DOM子树完全替换调用的元素。
6. scrollIntoView() 方法
scrollIntoView() 可以在所有 HTML元素上调用,通过滚动浏览器窗口或者某个容器元素,调用元素就可以出现在视口中。如果给这个方法传入true 作为参数,或者不传入任何参数,那么窗口滚动之后会然调用元素的顶部与视口顶部尽量平齐。如果传入false 作为参数,调用元素会尽可能全部出现在视口中。不过顶部不一定平齐。
document.forms[0].scrollIntoView();
专有扩展
1.children 属性
children属性是一个HTMLCollection 实例,值包含元素重同样还是元素的子节点,除此之外 children 和 childNode没有什么区别,即在元素值包含元素子节点时,者两个属性的值相同。
2.contains() 方法
contains 方法用于确定某个节点(参数)是不是被检测节点的后代节点。是则返回true 否则返回false。
除此之外还可以使用 DOM Level 3 中的 compareDocumentPosition() 方法来确定节点之间的关系。返回一个表示该关系的位掩码(bitmask)。掩码值如下:
- 1:无关(给定的节点不在当前文档中)
- 2:居前(给定的节点在DOM树中位于参考节点之前)
- 4:居后(给定的节点在DOM树中位于参考节点之后)
- 8:包含(给定的节点是参考节点的祖先)
- 16:被包含(给定的节点是参考节点的后代)
一个通用的contains() 函数可以写成如下形式:
function contains(refNode, otherNode){ if(typeof refNode.contains == "function" && (!client.engine.webkit || client.engine.webkit>522)){ return refNode.contains(otherNode); }else if(typeof refNode.compareDocumentPosition == "function"){ return !!(refNode.compareDocumentPosition(otherNode) & 16); }else{ var node = otherNode.parentNode; do{ if(node == refNode){ return true; }else{ node = node.parentNode; } }while(node != null); return false; } }
小结
虽然DOM 为与XML及HTML 文档交互制定了一系列核心API,但仍然有几个规范会标准的DOM进行了扩展。这些扩展中有很多原来是浏览器转悠的,但后来成为了事实标准,于是其他浏览器也都提供了相同的实现。
- Selectors API,定义了两个方法,让开发人员能够基于CSS选择符从DOM 中取得元素,这两个方法是 querySelector() 和 querySelectorAll()。
- Element Traversal,为DOM元素定义了额外的属性,让开发人员能够更方便地从一个元素跳到另一个元素。之所以会出现这个扩展,是因为浏览器处理DOM元素间空白符的方式不一样。
- HTML5, 为标准的DOM 定义了很多扩展功能。其中包括在innerHTML属性这样的事实标准基础上提供的标准定义,以及为管理焦点、设置字符集、滚动页面而规定的扩展API。