对DOM的两个主要扩展是Selectors API和HTML5。javascript
Selectors API致力于让浏览器原生支持CSS查询。在没有原生支持以前,只能经过javascript代码来完成查询操做。以后,解析和树查询操做能够在浏览器内部经过编译后的代码来完成,极大地改善了性能。java
querySelector()
方法接收一个CSS选择符,返回与该模式匹配的第一个元素,若是没有找到匹配的元素,返回null。node
经过Document类型调用时,会在文档元素的范围内查找匹配的元素。而经过Element类型调用时,只会在该元素后代元素的范围内查找匹配的元素。web
<ul>
<li class='one'></li>
<li></li>
<li></li>
</ul>
$ul = document.querySelector('ul');
$ul.querySelector('.one')
// return <li class='one'></li>
复制代码
querySelectorAll()方法接收的参数与querySelector()方法同样,都是一个CSS选择符,但返回的时全部匹配的元素。这个方法返回的时一个Nodelist实例。浏览器
<ul>
<li class='one'></li>
<li></li>
<li></li>
</ul>
$ul = document.querySelector('ul');
$ul.querySelectorAll('.two')
// NodeList[]
复制代码
能够调用上面两种方法的node类型包括:document、element、DocumentFragment。安全
Element类型新增了一个方法matchesSelector()
。这个方法接收了一个参数,即CSS选择符,若是调用元素与该选择符匹配,返回true;不然,返回false。bash
这个方法尚未被全部浏览器都支持。app
专门用于element类型的元素遍历dom
HTML5规范则围绕如何使用新增标记定义了大量Javascript API。其中一些API和DOM重叠,定义了浏览器应该支持的DOM扩展。函数
这个方法能够经过document对象以及全部HTML元素调用。
在操做类名时,须要经过className属性添加、删除和替换类名。由于className中是一个字符串,因此即便只修改字符串的一部分,也必须每次都设置整个字符串的值。
<div class='a b c'></div>
var className = div.className.split(/\s+/);
var pos = -1;
var i;
var len;
for (i=0, len=className.length; i < len; i++) {
if (className[i] == 'a') {
pos = i;
break;
}
}
//删除类名
className.splice(i, 1)
//把剩下的类名拼成字符串并从新设置
div.className = className.join(' ');
复制代码
HTML5新增了一种操做类名的方式,可让操做更简单也更安全,那就是为全部元素添加classList属性。这个classList属性是新集合类型DOMTokenList的实例。
DOMTokenList有一个表示本身包含多少元素的length属性,而要取得每一个元素可使用item()方法,也可使用方括号语法。此外这个新类型还定义以下方法。
document.activeElement属性,这个属性始终引用了DOM中当前得到了焦点的元素。元素得到焦点的方式有页面加载、用户输入和在代码中调用focus()方法。
var dom = document.getElementById('dom');
dom.focus();
document.activeElement === dom; // true
dom.hasFocus(); // true
复制代码
Document的readyState属性有两个可能的值:
if(document.readyState === 'complete') {
// 执行操做
}
复制代码
新增document.body
方法,直接引用元素
HTML5新增了几个与文档字符集有关的属性。其中,charset属性表示文档中实际使用的字符集,也能够用来指定新字符集。
HTML5规定能够为元素添加非标准的属性,但要添加前缀data-。
var dom = `<div id='myDiv' data-appid='234' data-myname='zc'></div>`;
var $container = document.createElement('div');
$container.innerHTML = dom;
var $dom = $container.firstElementChild;
$dom.dataset; // DOMStringMap {appid: "234", myname: "zc"}
$dom.dataset.appid = 12;
$dom.dataset; // DOMStringMap {appid: "12", myname: "zc"}
复制代码
使用插入标记的技术,直接插入HTML字符串不只更简单,速度也更快。如下与插入标记相关的DOM扩展已经归入了HTML5规范。
在读模式下,innerHTML属性返回与调用元素的全部子节点(包括元素、注释和文本节点)对应的HTML标记。在写模式下,innerHTML会根据指定的值建立新的DOM树,而后用这个DOM树彻底替换调用元素原先的全部子节点。
可是,不一样浏览器返回的文本格式会有所不一样。IE和Opera会将全部标签转换成大写形式。而Safari、Chrome和Firefox则会原本来本地按照原先文档中的格式指定返回HTML,包括空格和缩进。
在写模式下,innerHTML的值会被解析为DOM子树,替换调用元素原来的全部子节点。由于它的值被认为是HTML,因此其中的全部标签都会按照浏览器处理HTML的标准方式转换为元素。
为innerHTML设置HTML字符串后,浏览器会将这个字符串解析为相应的DOM树。所以设置了innerHTML以后,再从中读取HTML字符串,会获得与设置时不同的结果。缘由在于返回的字符串是根据原始HTML字符串建立的DOM树通过序列化以后的结果。
在读模式下,outerHTML返回调用它的元素及全部子节点的HTML标签。在写模式下,outerHTML会根据指定的HTML字符串建立新的DOM子树,而后用这个DOM子树彻底替换调用元素。
插入标记的最后一个新增方式是insertAdjacentHTML()
方法。这个方法最先也是在IE中出现的,它接受两个参数:插入位置和要插入的HTML文本。第一个参数必须是下列值之一:
beforeBegin
,在当前元素以前插入一个紧邻的同辈元素使用本节介绍的方法替换子节点可能会致使浏览器的内存占用问题,尤为在IE中,问题更加明显。在删除带有事件处理程序或引用了其余Javascript对象子树时,就有可能致使内存占用问题。假设某个元素有一个事件处理程序(或者引用了一个javascript对象做为属性),在使用前述某个属性将该元素从文档树中删除后,元素与事件处理程序(javascript对象)之间的绑定关系在内存中并无一并删除。若是这种状况频繁出现,页面占用的内存数量就会明显增长。所以,在使用innerHTML、outerHTML、insertAdjacentHTML()方法时,最好先手工删除要被替换的元素的全部事件处理程序和Javascript对象属性。
同理: 每次循环都设置一次innerHTML的作法效率很低。
for (var i = 0; len = values.length; i < len; i++) {
ul.innerHTML += "<li>" + values[i] + "</li>"; // 要避免这种频繁操做
}
复制代码
效率更高的:
for (var i = 0, len = values.length; i <len; i++) {
itemsHtml += "<li>" + values[i] + "</li>";
}
ul.innerHTML = itemsHTML;
复制代码
IE8引入了一个新的概念叫作“文档模式”(document mode)。页面的文档模式决定了可使用什么功能。换句话说,文档模式决定了你可使用哪一个级别的CSS,能够在JavaScript中使用哪些API,以及如何对待文档类型(doctype)。到了IE9,总共有如下4种文档模式。
<meta http-equiv='X-UA-Compatible' content='IE=IEVersion'>
复制代码
The http-equiv attribute is used by servers to gather information about a page using the HTTP header. The meta tag’s http-equiv attribute set is similar to a http header.
这个属性是HTMLCollection的实例,只包含元素中一样仍是元素的子节点。除此以外,children属性与childNodes没有什么区别。即在元素只包含元素子节点时,这两个属性的值相同。
在实际开发中,常常须要知道某个节点是否是另外一个节点的后代。IE为此率先引入了contains()方法,以便不经过在DOM文档树中查找便可得到这个信息。调用contains()方法的应该是祖先节点,也就是搜索开始的节点,这个方法接收一个参数,即要检测的后代节点。若是被检测的节点是后代节点,该方法返回true;不然,返回false。
document.documentElement.contains(document.body); // true
复制代码
使用DOM Level3 compareDocumentPosition()也可以肯定节点间的关系。这个方法用于肯定两个节点间的关系,返回一个表示关系的位掩码。
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;
}
node = node.parentNode;
} while (node !== null);
return false;
}
}
复制代码
前面介绍过,IE原来专有的插入标记的属性innerHTML和outerHTML已经被HTML5归入规范。但另外两个插入文本的专有属性则没有这么好的运气。这两个没有被HTML5看中的属性是innerText和outerText。
经过innerText属性能够操做元素中包含的全部文本内容,包括子文档树中的文本。在经过innerText读取值时,它会按照由浅入深的顺序,将子文档树中的全部文本拼接起来。在经过innerText写入值时,结果会删除元素的全部子节点,插入包含相应文本值的文本节点。
支持innerText属性的浏览器包括IE4+、safari 3+、Opera 8+和Chrome。Firefox虽然不支持innerText,但支持做用相似的textContent属性。textContent是DOM Level3规定的一个属性。为了确保跨浏览器兼容,有必要编写一个相似于下面的函数来检测可使用哪一个属性。
function getInnerText (element) {
return (typeof element.textContent == 'string') ? element.textContent : element.innerText;
}
function setInnerText (element, text) {
if (typeof element.textContent == 'string') {
element.textContent = text;
} else {
element.innerText = text;
}
}
复制代码
除了做用范围扩大到了包含调用它的节点以外,outerText与innerText基本上没有多大区别。
div.outerText = 'Hello World';
// 这行代码实际上至关于以下两行代码
var text = document.createTextNode('Hello World');
div.parentNode.replaceChild(text, div)
复制代码
HTML5以前的规范并无就与页面滚动相关的API作出相关规定。但HTML5在将scrollIntoView()归入规范以后,仍然还有其余几个专有方法能够在不一样的浏览器中使用。下面列出的几个方法都是对HTMLElement类型的扩展,所以在全部元素中均可以调用。
本章主要介绍了一些DOM扩展: