虽然DOM为XML及HTML文档交互制定了一系列的API,但仍然有几个规范对标准的DOM进行了扩展。这些扩展中,有很多是浏览器专有的,但后来成了事实标准,于是其他浏览器也提供了相同的实现;浏览器开发商发现某项功能缺失时,仍然会直接往DOM中添加专有扩展,以弥补不足。下面分别介绍这些标准扩展和专有扩展。
一、选择符API
选择符API的功能是根据CSS选择符选择与某个模式匹配的DOM元素。Jquery的核心就是通过CSS选择符查询DOM文档以取得元素的引用,从而抛开了getElementById()和getElementsByTagName()。选择符API是W3C发起的一个标准,目的是让浏览器支持原生支持CSS查询。它的核心方法是querySelector(),querySelectorAll()和matchesSelector()。
querySelector()接受一个CSS选择符,返回与该模式匹配的的第一个字符。
//取得body元素 var body = document.querySelector("body"); // 取得ID为“myDiv"的元素 var myDiv = document.querySelector("#myDiv"); // 取得类名为 ”selected"的第一个元素 var selected = document.querySelector(".selected"); // 取得类名为“button"的第一个图像元素 var img = document.querySelector("img.button");
querySelectorAll()返回的是所有匹配的元素而不仅仅是一个元素,即一个NodeList的实例。
// 取得div中的所有<em>元素 var ems = document.getElementById("myDiv").querySelectorAll("em"); // 取得类为selected的所有元素 var selectors = document.querySelectorAll(".selected"); // 取得所有<p>元素中的<strong>元素 var strongs = document.querySelectorAll("p strong");
matchesSelector()如果调用元素与选择符匹配,返回true;否则,返回false。不过,由于不是所有的浏览器都支持这个方法,我们需要写一个包装函数:
function matchesSelector(element, selector){ if(element.matchesSelector){ return element.matchesSelector(selector); }else if(element.msMatchesSelector){ return element.msMatchesSelector(selector); }else if(element.mozMatchesSelector(selector)){ return element.mozMatchesSelector(selector); }else if(element.webkitMatchesSelector(selector)){ return element.webkitMatchesSelector(selector); }else{ throw new Error("Not supported."); } }
二、元素遍历
对于元素间的空格,IE9以及之前的版本不会返回文本节点,而其他浏览器都会返回文本节点。由于浏览器的行为不一致,W3C为元素添加了以下几个新属性:
childElementCount: 返回子元素的个数,不包括文本节点和注释节点 firstElementChild: 指向第一个子元素,firstChild的元素版 lastElementChild:指向最后一个子元素,lastChild的元素版 previousElementSibling:指向前一个同辈元素,previousSibling的元素版 nextElmnetSibling:指向后一个同辈元素,nextSibling的元素版
使用这些属性,可以不必担心空白字符,从而更方便的查找DOM元素了。下面比较使用过去的方法和使用新增元素的代码:
var i, len, child = element.firstChild; while(child != element.lastChild){ if(child.noteType == 1){ processChild(child); // 检查是否是元素节点 } child = child.nextSibling; }
var i, len, child = element.firstElementChild; while(child != element.lastElementChild){ if(child.noteType == 1){ processChild(child); } child = child.nextElementSibling; }
三、HTML5
HTML5围绕如何使用新增标记定义了大量的JAVASCRIPT API,其中一些API与DOM重叠,定义了浏览器应该支持的DOM扩展。
1、与类相关的扩充
<html> <head></head> <body> <div id="test" class="username current"></div> <div id="test1" class="current username"></div> <script> // 取得所有类中包含”usrname"和“current”的元素的,类名的先后无所谓 var allCurrentUsernames = document.getElementsByClassName("username current"); var div = document.getElementById("test"); // 删除元素中的类 div.remove("username"); // 添加类 div.add("username"); // 切换类:如果已经存在,就删除它;如果不存在,就添加它 div.toggle("username"); // 确定元素中是否包含类 div.contains("username"); </script> </body> </html>
2、焦点管理
确认用户是否获得了焦点,可以知道用户是否正在交互。
var button = document.getElementById("myButon"); button.focus(); alert(document.hasFocus()); //true
3、HTMLDocument的变化
1)Document的readyState属性有两个可能的值:
loading,正在加载文档;
complete,已经加载完文档了。
2)从IE6就开始区分渲染页面的模式是标准的还是混杂的,检测页面的兼容模式就成了浏览器的必要功能。使用document.compatMode表示渲染模式,在标准模式下,这个值等于“CSS1Compat”,在混杂模式下,这个值等于“BackCompat”。
3)作为对document.body是引用文档的<body>元素的补充,HTML5新增了document.head元素,用于表示文档的<head>元素:
var head = document.head || document.getElementsByTagName("head")[0];
4、字符集属性
HTML5新增了几个与文档字符集相关的属性,document.charset表示文档中实际使用的字符集,也可以用来表示新的字符集;document.defaultCharset表示根据默认浏览器及操作系统的设置,当前文档默认的字符集应该是什么。
5、自定义数据属性
如果需要给元素添加一些不可见的数据以便进行其他处理,那就要用到自定义数据属性;自定义属性需要添加前缀“data-”,目的是为元素提供与渲染无关的信息,或者提供语义信息。
6、插入标记
虽然DOM为操作节点提供了细致入微的控制手段,但需要给文档插入大量HTML标记的情况下,通过DOM操作仍然非常困难,因为不仅需要创建一系列的DOM节点,还要小心的按照正确的顺便把它们连接起来。而使用插入技术,直接插入HTML字符会简单很多。
1)innerHTML
innerHTML在读模式下,会返回与调用元素的所有子节点(包括元素、注释和文本节点)对应的HTML标记。在写模式下,会根据指定的值创建新的DOM树,然后用DOM树完全替换元素原先的子节点。
2)outerHTML
outerHTML在读模式下,会返回调用元素自身以及所有的子节点对应的HTML标记。在写模式下,会根据执行的值创建新的DOM树,然后用DOM完全替换自身以及子节点。
3)insertAdjacentHTML()
这个方法接受两个参数,插入位置和要插入的文本,第一个参数必须是下列值之一:
- beforebegin:在调用元素之前插入一个相邻的同辈元素
- afterbegin:作为调用元素的第一个子元素插入
- beforeend:作为调用元素的最后一个子元素插入
- afterend:作为调用元素的最后一个同辈元素插入
7、内存与性能问题
替换或删除节点,可能会导致浏览器的内存占用问题:当把某个元素从文档树中删除后,元素与事件处理程序之间的绑定关系并没有删除。如果这种情况频繁出现,页面占用内存数就会明显增加。因此,在使用innerHTML/outerHTML/insertAdjacentHTML之前,最好先手工删除要被替换的元素的所有事情处理程序和javascript对象属性。
8、scrollIntoView()
scrollIntoView()可以在所有的HTML元素上调用,如果给这个方法传入true做参数,或者不传入任何参数,那么窗口滚动之后,会尽量让调用元素的顶部与视口的顶部保持齐平。如果传入false作参数,调用元素会尽可能全部出现在视口中。
三、专有扩展
虽然所有的浏览器厂商都知道坚守标准的重要性,但在发现某项功能缺失时,仍然会一如既往的在DOM中添加专有扩展,以弥补功能上的不足;这些扩展一旦获得认可,就有可能被收录到规范的新版本中。以下介绍一些专有扩展:
1、文档模式
文档模式是IE引入的概念,页面的文档模式决定了文档可以使用哪个级别的CSS,可以在Javascript中使用哪些API,以及如何对待文档类型(doctype)。
2、contains方法
contains方法用于判断一个节点是否是调用节点的子节点。
3、插入文本
1)innerText属性
通过innerText属性,可以操作元素中所有包含的文本节点。
<html> <head></head> <body> <div id="content"> <p>This is a paragragh.</p> <ul> <li>item 1</li> <li>item 2</li> <li>item 3</li> </ul> </div> </body> </html>
对这个例子而言,innerText会返回:
- This is a paragragh.
- item 1
- item 2
- item 3
如果通过innerText设置文本,则会先删除调用元素下所有的子节点,再添加上文本节点;即完全改变了dom树。
2)outerText
在读模式下,outerText与innerText完全一致,在写模式下,outerText会先删除调用节点本身以及子节点,再添加上文本节点。