从 DOM的来源 说起。

上世纪九十年代,在浏览器争夺战中,微软为了霸占更多的浏览器市场,于是山寨了Netscape的Javascript而开发了Jscript ,并且还在其自身的浏览器IE中加入一些私有事务,这也正是导致javascript在浏览器兼容性问题的主要原因。为了统一web的开发标准,于是,W3C在1998年发布了一套网页编程接口,即为DOM。

那么,究竟什么是DOM。

DOM 是一组用来描述脚本怎样与结构化文档进行交互和访问的web标准。DOM定义了一系列对象、方法和属性,用于访问、操作和创建文档中的内容、结构、样式和行为。

其实HTML,XML,SVG这些语言都是支持DOM的,而且正是通过DOM我们才能操纵这些文档结构。简而言之,DOM赋予了javascript操作结构化文档的能力。应该明白的是,DOM 不是javascript,,它是由W3C定义的一组规范。

DOM的级别

DOM 0级

在严格意义上来说,是没有DOM0级和0级规范的。如果有人提到 0 级,那很可能是指一组专有的DHTML方法、对象和集合。而这些专有特性在成为标准的规范之前,在不同浏览器中实现是不一致。

DOM1级

DOM1级于1998年10月发布,是作为推进标准发布的第一DOM标准版本。DOM1级是一个规范,由如下两部分组成。

DOM core:为XML文档规定了一般性的树形节点结构的内部运行机制,同时给出了创建、编辑和操纵这个树形结构的必要属性和方法。

DOM HTML:为与HTML文档、标签集合以及个别的HTML标签相关的具体元素定义了对象、属性和方法。

DOM1级规范包含诸如Document、Node、Attr、Element、Text、HtmlDocument、HtmlElement和HtmlCollection等对象的定义。

DOM2 级

DOM2 级于2000年11月发布,更新了核心并增加了其他一些规范。这些规范主要被分为这六部分:

  1. DOM2 Core:类似DOM Core,规定了对DOM文档结构的控制机制,添加了更多的特性。

  2. DOM2 Html:类似DOM Html 规定了HTML的DOM文档的控制机制。还包括另外一些属性。

  3. DOM2 Events:规定了与鼠标相关的事件。

  4. DOM2 Style: 提供了访问和操作所有与css相关的样式及规则的能力。

  5. DOM2 Traversal and Range:这两个规范使你能够迭代访问DOM,以便根据需要对文档进行遍历或操作。

  6. DOM2 Views:提供了访问和更新文档表现的能力。

DOM3 级

DOM3级包含更新之后的核心,总共包含三个推荐规范:

  1. DOM3 Core:向原有核心添加了更多的新方法和新属性,同时修改了已有的一些方法。

  2. DOM3 Load and Save:提供将XML文档的内容加载到DOM文档中和将DOM文档序列化为XML文档的能力。

  3. DOM3 Validation:提供了确保动态生成的文档的有效性的能力。

检测是否支持DOM(或DOM几级)

document.implementation 检测是否支持DOM

document.implementation.hasFeature()检测支持DOM几级,它有两个参数:

第一个参数可以是以下的其中一个:Core、XML、HTML、Views、StyleSheets、css、css2、Events、UIEvents等

第二个参数是DOM的级别:即1.0,2.0,3.0;

if (document.implementation) {
if (document.implementation.hasFeature('Core', '2.0')) {
alert("支持DOM2级");
} else {
alert("不支持DOM2级");
}
} else {
alert("不支持DOM");
}

DOM的属性

nodeName

nodeName属性返回的是节点的名称。此属性只读;

  • 元素节点的 nodeName 与标签名相同

  • 属性节点的 nodeName 与属性名相同

  • 文本节点的 nodeName 始终是 #text

  • 文档节点的 nodeName 始终是 #document

nodeValue

nodeValue属性返回的是节点的值。

  • 元素节点的 nodeValue 是 undefined 或 null

  • 文本节点的 nodeValue 是文本本身

  • 属性节点的 nodeValue 是属性值

nodeType

nodeType属性返回的是节点的类型。此属性只读的;这里列出了几个比较常用的节点对应的类型值:

  • 元素 — 1

  • 属性 — 2

  • 文本 — 3

  • 注释 — 8

  • 文档 — 9

DOM的方法

获取DOM

  • getElementById(id) — 返回指定id的元素

  • getElementsByName(nameVal) — 返回匹配所有name属性值的元素节点列表(NodeList)

  • getElementsByTagName(tagName) — 返回所有匹配tagName标签名的元素节点列表(NodeList)

  • getElementsByClassName(class) — 返回所有匹配class类名的元素节点列表(NodeList)

  • parentNode — 返回相关元素的父节点

  • childNodes — 返回指定元素的子元素集合 ,注意只是子元素(第一级),并不包含子元素的后代元素

  • firstChild — 返回指定元素的第一个子节点

  • lastChild — 返回指定元素的最后一个子节点

  • previousSibling — 返回指定元素的上一节点

  • nextSibling — 返回指定元素的下一节点

  • children — 返回指定元素的子元素集合,注意只是子元素(第一级),并不包含子元素的后代元素

  • querySelector() — 以css选择符作为参数,获取第一个匹配的元素

  • querySelectorAll() — 以css选择符作为参数,获取匹配的一个nodelist集合

  • rows(pos) — 获取第pos行的tr的内容

  • rows(pos) — 获取第pos行的tr的内容

  • cells(pos) — 获取第pos个td的内容

插入和操作DOM

  • appendChild(elem) — 在父级元素的最后插入elem元素

  • removeChild(elem) — 从父级元素中移除elem元素

  • insertBefore(newElem,curElem) — 在curElem元素前面插入newElem元素

  • replaceChild(newElem,oldElem) — 使用newElem元素替换oldElem元素

  • insertRow(pos) — tbody的第pos行插入tr,如 tbody.insertRow(1)

  • insertCell(pos) — tr的第pos个位置插入td,如tr.insertCell(1)

创建DOM

  • createElement(tagName) — 创建元素节点

  • createAttribute(attr) — 创建属性节点

  • createTextNode(text) — 创建文本节点

  • createDocumentFragment — 创建文档碎片

操作属性

  • getAttribute(attr) — 获取元素的attr属性值

  • removeAttribute(attr) — 移除元素的attr属性

  • setAttribute(attr,val) — 设置元素的attr属性为val

内容操作

  • innerHTML — 获取元素内(不含本身标签)所有的HTML(IE下标签名为大写)

  • outerHTML — 获取元素本身以及元素内(包含本身)所有的HTML(IE下标签名为大写)

  • innerText(或者 textContent) — 获取元素的所有文本内容(FF不支持,返回undefined,可用textContent代替)

  • outerText(或者 textContent) — 获取元素的所有文本内容

样式操作

  • style.cssText — 获取标签中style属性里css属性

  • getComputedStyle(或者 currentStyle) — 获取标签在样式表中的相关css属性

DOM的性能

“把DOM和javascript分别比喻成一个岛屿,它们两者要联系起来,必须缴纳过桥费。因此访问DOM次数越多,则过桥的费用就越高”。同时修改任何DOM元素的样式都会导致页面重绘(repaint)和重排(reflow)。如果你要提高网页DOM性能,可以从以下几方面着手:

存储局部变量更快些

//效率低
function loop1(){
for(var i=0;i<10000,i++){
document.getElementById(“div1”).innerHTML+=”a”;
}
}

//效率高
function loop2(){
var content=””;
for(var i=0;i<10000,i++){
content+=”a”;
}
document.getElementById(“div2”).innerHTML+=content;
}

很明显,第一次需要在每次循环都访问div1,而第二种方法就类似创建文档碎片,把每次循环的内容都添加到变量content中,最后只需要访问一次div2(DOM),就可以完成插入内容。

innerHTML对比原生DOM

在为一个DOM元素添加内容时,我们可以使用innerHTML直接插入,也可以先创建子元素,再插入子元素。大量实验证明,在普通情况下两者执行效率相差无几。但某些情况下,innerHTML还是比元素DOM方法效率更高些。

使用API去获取元素

因为API是高效封装的,而且通过原生的DOM获取相应的元素通常需要复杂的匹配。因此可以使用querySelector和querySelectorAll来代替原生的获取方法,同时一些新的属性也直接返回是元素节点。以下列出便利的DOM属性:

属性名 被替换属性
children childNodes
childElementCount childNodes.length
firstElementChild firstChild
lastElementChild lastChild
nextElementSibling nextSibling
previousElementSibling previousSibling

重绘和重排

前面说到,任何改变DOM样式的情况都加重浏览器的计算去重绘或重排网页,我们不能不让网页重排,因为网页交互是建立在操作DOM的基础上,但我们可以尽量减少重绘和重排的次数,通常情况下,我们可以采用如下方法:

  1. 使用ele.stye.cssText+=””来代替多次改变样式

  2. 改变DOM元素的class来代替修改内联样式。但此方法只能是固定某种样式,而不能反复修改DOM的样式属性

  3. 固定某些模块的宽、高

  4. css放在顶部

  5. 让需要改变属性的元素脱离文档流,当该元素发生样式改变时,不会影响到页面的其他元素(最多重绘)

事件委托

当我们要操作一组元素时,我们一般采用循环的方式,对每个元素添加事件,但是这样会导致事件处理程序过多而影响性能。事件委托是利用元素事件的冒泡,将事件添加到父级元素上,然后通过事件源判断目标元素,从而减少事件对DOM的引用。

<ul id="list">
<li>111</li>
<li>222</li>
<li>333</li>
</ul>

<script>
var oList=document.getElementById("list"),
arr=["background:green;color:#fff;","color:#000;"];

oList.onmouseover=function(e){
changeFn(arr[0]);
}
oList.onmouseout=function(e){
changeFn(arr[1]);
}

function changeFn(str){
var e=window.event||e,
target=e.target||e.srcElement;
if(target.nodeName.toLowerCase="li"){
target.style.cssText=str;
}
}
</script>

参考

  • javascript高级程序设计
  • javascript DOM高级程序设计
  • DOM百科
  • 高性能的javascript