本系列内容由ZouStrong整理收录
整理自《JavaScript权威指南(第六版)》,《JavaScript高级程序设计(第三版)》
DOM经非常完善了,但为了实现更多的功能,仍然会有一些标准或专有的扩展
很多JavaScript库都可以根据CSS选择器来直接选取元素,但本质上还是使用现有的DOM方法查询文档并找到匹配的节点,并且库开发人员在不知疲倦地改进这一过程的性能,但到头来都只能通过运行JavaScript代码来完成查询操作
而把这个功能变成原生API之后,解析和树查询操作可以在浏览器内部通过编译后的代码来完成,极大地改善了性能,这就是Selectors API
该方法接受一个CSS选择器,返回与该模式匹配的第一个元素,如果没有找到匹配的元素,返回null,如果传入了不被支持的选择器,则会抛出错误
可以通过Document及Element类型的实例调用它
var body = document.querySelector("body");
var div = document.querySelector("#div");
var img = div.querySelector("img.button");
该方法接受一个CSS选择器,返回与该模式匹配的所有元素(NodeList的实例),如果没有找到匹配的元素,则返回空的NodeList对象,如果传入了不被支持的选择器,则会抛出错误
可以通过Document及Element类型的实例调用它
这里返回的NodeList,是一组元素的快照,而非不断对文档进行搜索的动态查询。这样实现可以避免使用NodeList对象通常会引起的大多数性能问题
var sel = document.querySelectorAll(".s");
要取得返回的NodeList中的每一个元素,可以使用方括号语法或item()方法
该方法接受一个CSS选择器,如果调用元素与该选择器匹配,返回true;否则,返回false
只能通过Element类型的实例调用它
if (document.body.matchesSelector(".page")){
}
截至现在(2015/7/20 )还没有浏览器支持该方法
但是IE9+通过msMatchesSelector()支持该方法,Firefox 3.6+通过mozMatchesSelector()支持该方法,Safari和Chrome通过webkitMatchesSelector()支持该方法
function matchesSelector(element, selector){
if (element.matchesSelector){
return element.matchesSelector(selector);
} else if (element.msMatchesSelector){
return element.msMatchesSelector(selector);
} else if (element.mozMatchesSelector){
return element.mozMatchesSelector(selector);
} else if (element.webkitMatchesSelector){
return element.webkitMatchesSelector(selector);
} else {
throw new Error("Not supported.");
}
}
if (matchesSelector(document.body, ".page1")){
}
对于元素间的空格,IE9及之前版本不会返回文本节点,而其他所有浏览器都会返回文本节点。这就导致了在使用childNodes和firstChild等属性时的行为不一致
元素遍历规范新定义了一组属性(IE6、7、8不支持)
HTML5新增了很多API,致力于简化CSS类的用法
该方法接收一个包含一个或者多个类名(顺序无关)的字符串,返回带有指定类名的元素的NodeList(与使用getElementsByTagName()
以及其他返回 NodeList 的 DOM 方法都具有同样的性能问题,这一点上选择器API就做的很好)
可以通过Document及Element类型的实例调用它
在操作类名时,需要通过className属性添加、删除和替换类名。因为className中是一个字符串,所以即使只修改字符串一部分,也必须每次都设置整个字符串的值
HTML5为所有元素添加了classList属性,是一个class值的集合,可以使用length属性查看class值的数量,使用item()方法或方括号语法访问指定位置的class值
此外,还定义如下方法,极大的减少了类名操作的复杂性(就好像在使用JavaScript库一样方便)
例
div.classList.remove("hide");
div.classList.add("hide");
div.classList.toggle("hide");
有了classList属性,除非需要全部删除所有类名,或者完全重写元素的class属性,否则也就用不到className属性了
HTML5也添加了辅助管理DOM焦点的功能
这个属性始终会引用DOM中当前获得了焦点的元素,元素获得焦点的方式有
例
var button = document.getElementById("myButton");
button.focus();
alert(document.activeElement === button); //true
默认情况下,文档加载期间
document.activeElement===null
而文档刚刚加载完成时
document.activeElement===document.body
该方法用于确定文档是否获得了焦点。
var button = document.getElementById("myButton");
button.focus();
alert(document.hasFocus()); //true
HTML5扩展了HTMLDocument,增加了新的功能
IE4最早为document对象引入了readyState属性。然后,其他浏览器也都陆续添加这个属性,最终HTML5把这个属性纳入了标准当中
readyState属性有两个可能的值
例
div.onclick = function(){
if(document.readyState==="complete"){
}
};
在这个属性得到广泛支持之前,要实现这样一个指示器,必须借助onload事件处理程序设置一个标签,表明文档已经加载完毕
window.onload = function(){
div.onclick = function(){
}
}
IE给document添加了compatMode属性,告诉开发人员浏览器采用了哪种渲染模式,最终,HTML5也把这个属性纳入标准
在标准模式下
document.compatMode==="CSS1Compat"
而在混杂模式下
document.compatMode==="BackCompat"
document.body属性可以取得body元素,因此HTML5新增document.head属性,引用文档的head元素
var head = document.head ||
document.getElementsByTagName("head")[0];
HTML5新增了几个与文档字符集有关的属性
charset属性表示文档中实际使用的字符集,也可以用来指定新字符集
alert(document.charset);
document.charset = "UTF-8";
defaultCharset属性表示根据浏览器及操作系统的设置,当前文档默认的字符集应该是什么(它是只读的)
alert(document.defaultCharset);
HTML5规定可以为元素添加非标准的属性,但要添加前缀data-,目的是为元素提供与渲染无关的信息,或者提供语义信息
<div data-age="26 data-name="strong"></div>
添加了自定义属性之后,可以通过元素的dataset属性来访问自定义属性的值
dataset属性返回所有自定义属性组成的对象,属性值就是自定义属性名(没有data-前缀),属性值就是自定义属性值
div.dataset.age;
div.dataset.name="strongs";
之前讲到的DOM操作(appendChild()、insertBefore()等)都是比较精细的手段,但是有的时候却不需要那么精细,以下与插入标记相关的DOM扩展已经纳入了HTML5规范
在读模式下,innerHTML属性返回与调用元素的所有子节点(包括元素、注释和文本节点)对应的HTML标记。在写模式下,innerHTML会根据指定的值创建新的DOM树,然后用这个DOM树完全替换调用元素原先的所有子节点
并不是所有元素都支持innerHTML属性,不支持innerHTML的元素有
<col>、<colgroup>、<frameset>、<head>
<html>、<style>、<table>、<tbody>
<thead>、<tfoot>和<tr>
此外,在IE8及更早版本中,
在读模式下,outerHTML返回调用它的元素及所有子节点的HTML标签。在写模式下,outerHTML会根据指定的HTML字符串创建新的DOM子树,然后用这个DOM子树完全替换调用元素
该方法接收两个参数,第一个参数表示插入位置,必须是下列值之一
第二个参数表示插入的值
使用本节介绍的方法替换子节点可能会导致浏览器的内存占用问题,尤其是在IE中
假设某个元素有一个事件处理程序(或者引用了一个JavaScript对象作为属性),在使用前述某个属性将该元素从文档树中删除后,元素与事件处理程序(或JavaScript对象)之间的绑定关系在内存中并没有一并删除。如果这种情况频繁出现,页面占用的内存数量就会明显增加
因此,在使用innerHTML、outerHTML属性和insertAdjacentHTML()方法时,最好先手工删除要被替换的元素的所有事件处理程序和JavaScript对象属性
不过,使用这几个属性——特别是使用innerHTML,仍然还是可以为我们提供很多便利的。一般来说,在插入大量新HTML标记时,使用innerHTML属性与通过多次DOM操作先创建节点再指定它们之间的关系相比,效率要高得多
这是因为在设置innerHTML或outerHTML时,就会创建一个HTML
解析器。这个解析器是在浏览器级别的代码(通常是C++编写的)基础上运行的,因此比执行JavaScript快得多。不可避免地,创建和销毁HTML解析器也会带来性能损失,所以最好能够将设置innerHTML
或outerHTML的次数控制在合理的范围内
for (var i=0, len=values.length; i < len; i++){
ul.innerHTML += "<li>" + values[i] + "</li>";
//要避免这种频繁操作!!
}
最好的做法是单独构建字符串,然后再一次性地将结果字符串赋值给innerHTML
var itemsHtml = "";
for (var i=0, len=values.length; i < len; i++){
itemsHtml += "<li>" + values[i] + "</li>";
}
ul.innerHTML = itemsHtml;
这个例子的效率要高得多,因为它只对innerHTML执行了一次赋值操作
scrollIntoView()可以在所有HTML元素上调用,通过滚动浏览器窗口或某个容器元素,调用元素就可以出现在视口中。如果给这个方法传入true作为参数,或者不传入任何参数,那么窗口滚动之后会让调用元素的顶部与视口顶部尽可能平齐。如果传入false作为参数,调用元素会尽可能全部出现在视口中(可能的话,调用元素的底部会与视口顶部平齐)
//让元素可见
document.forms[0].scrollIntoView();
很多专有扩展都被HTML5纳入了标准之中,但仍然还有大量专有的DOM扩展没有成为标准(或许以后会成为标准)
强制IE以不同版本的模式来渲染
<meta http-equiv="X-UA-Compatible" content="IE=IE版本">
其中,IE版本值可能为
children属性是childNodes属性的进化版,它只会返回元素的子元素,不会有那些莫名其妙的节点混进来
IE8及以下浏览器会返回注释节点
调用contains()方法的应该是祖先节点,也就是搜索开始的节点,这个方法接收一个参数,即要检测的后代节点。如果被检测的节点是后代节点,该方法返回true;否则,返回false
IE原来专有的插入标记的属性innerHTML和outerHTML已经被HTML5纳入规范。
但另外两个插入文本的专有属性并没有被纳入规范
通过innertText 属性可以操作元素中包含的所有文本内容,包括子文档树中的文本。在通过innerText 读取值时,它会按照由浅入深的顺序,将子文档树中的所有文本拼接起来。在通过innerText写入值时,结果会删除元素的所有子节点,插入包含相应文本值的文本节点
除了作用范围扩大到了包含调用它的节点之外,outerText与innerText基本上没有多大区别
由于这个属性会导致调用它的元素不存在,因此并不常用
HTML5在将scrollIntoView()纳入规范之后,仍然还有其他几个专有方法可以在不同的浏览器中使用
可以在HTMLElement类型上调用它