dom(Document Object Model)
一、dom 简介
百度的,好长,懒得打字了,复制了过来。看下面。
文档对象模型(Document Object Model,简称DOM),是W3C组织推荐的处理可扩展标志语言的标准编程接口。在网页上,组织页面(或文档)的对象被组织在一个树形结构中,用来表示文档中对象的标准模型就称为DOM。Document Object Model的历史可以追溯至1990年代后期微软与Netscape的“浏览器大战”,双方为了在JavaScript与JScript一决生死,于是大规模的赋予浏览器强大的功能。微软在网页技术上加入了不少专属事物,既有VBScript、ActiveX、以及微软自家的DHTML格式等,使不少网页使用非微软平台及浏览器无法正常显示。DOM即是当时蕴酿出来的杰作。
二、浏览器的解析、渲染过程
感觉这位女士说的蛮好的:http://blog.csdn.net/xiaozhuxmen/article/details/52014901,这里摘取了部分如下:
当浏览器获得一个html文件时,会“自上而下”
加载,并在加载过程中进行解析渲染。
解析:
- 浏览器会将HTML解析成一个DOM树,DOM 树的构建过程是一个深度遍历过程:当前节点的所有子节点都构建好后才会去构建当前节点的下一个兄弟节点。
- 将CSS解析成 CSS Rule Tree 。
- 根据DOM树和CSSOM来构造 Rendering Tree。注意:Rendering Tree 渲染树并不等同于 DOM 树,因为一些像 Header 或 display:none 的东西就没必要放在渲染树中了。
4.有了Render Tree,浏览器已经能知道网页中有哪些节点、各个节点的CSS定义以及他们的从属 关系。下一步操作称之为Layout,顾名思义就是计算出每个节点在屏幕中的位置。
5.再下一步就是绘制,即遍历render树,并使用UI后端层绘制每个节点。
几个概念:
(1)Reflow(回流):浏览器要花时间去渲染,当它发现了某个部分发生了变化影响了布局,那就需要倒回去重新渲染。
(2)Repaint(重绘):如果只是改变了某个元素的背景颜色,文字颜色等,不影响元素周围或内部布局的属性,将只会引起浏览器的repaint,重画某一部分。
Reflow要比Repaint更花费时间,也就更影响性能。所以在写代码的时候,要尽量避免过多的Reflow。
reflow的原因:
(1)页面初始化的时候;
(2)操作DOM时;
(3)某些元素的尺寸变了;
(4)如果 CSS 的属性发生变化了。
减少 reflow/repaint
(1)不要一条一条地修改 DOM 的样式。与其这样,还不如预先定义好 css 的 class,然后修改 DOM 的 className。
(2)不要把 DOM 结点的属性值放在一个循环里当成循环里的变量。
(3)为动画的 HTML 元件使用 fixed 或 absoult 的 position,那么修改他们的 CSS 是不会 reflow 的。
(4)千万不要使用 table 布局。因为可能很小的一个小改动会造成整个 table 的重新布局。
编写CSS时应该注意:
CSS选择符是从右到左进行匹配的。从右到左!所以,#nav li 我们以为这是一条很简单的规则,秒秒钟就能匹配到想要的元素,但是,但是,但是,是从右往左匹配啊,所以,会去找所有的li,然后再去确定它的父元素是不是#nav。,因此,写css的时候需要注意:
- dom深度尽量浅。
- 减少inline javascript、css的数量。
- 使用现代合法的css属性。
- 不要为id选择器指定类名或是标签,因为id可以唯一确定一个元素。
- 避免后代选择符,尽量使用子选择符。原因:子元素匹配符的概率要大于后代元素匹配符。后代选择符;#tp p{} 子选择符:#tp>p{}
- 避免使用通配符,举一个例子,.mod .hd *{font-size:14px;} 根据匹配顺序,将首先匹配通配符,也就是说先匹配出通配符,然后匹配.hd(就是要对dom树上的所有节点进行遍历他的父级元素),然后匹配.mod,这样的性能耗费可想而知.
减少 JavaScript 对性能的影响的方法:
- 将所有的script标签放到页面底部,也就是body闭合标签之前,这能确保在脚本执行前页面已经完成了DOM树渲染。
- 尽可能地合并脚本。页面中的script标签越少,加载也就越快,响应也越迅速。无论是外链脚本还是内嵌脚本都是如此。
- 采用无阻塞下载 JavaScript 脚本的方法:
(1)使用script标签的 defer 属性(仅适用于 IE 和 Firefox 3.5 以上版本);
(2)使用动态创建的script元素来下载并执行代码;
三、dom操作
1.获取dom节点
无兼容问题的两个方法:
document.getElementById(id): 通过id获取节点。
document.getElementsByTagName(tagname): 通过标签名获取多个节点。
ie9+可以用的三个方法:
document.getElementsByClassName(classname): 通过类名找到多个节点(这个结果是动态的)。
document.querySelector(selector): 通过选择器找到一个节点。
document.querySelectorAll(selector): 通过选择器找到多个节点。
example1
var d1 = document.getElementById('d1');
var dvs = document.getElementsByTagName('div');
var dvs2 = document.getElementsByClassName('dv');
var selector = document.querySelector('.dv');
var selectors = document.querySelectorAll('.dv');
console.dir(d1);//div#d1
console.dir(dvs);//HTMLCollection(3)
console.dir(dvs2);//HTMLCollection(2)
console.dir(selector);//div.dv
console.dir(selectors);//NodeList(2)
//具体可可以看下控制台
2.dom节点的几个属性
**innerHTML:**获取元素内容的最简单方法是使用 innerHTML 属性。
nodeName: nodeName 属性规定节点的名称。
- nodeName 是只读的
- 元素节点的 nodeName 与标签名相同
- 属性节点的 nodeName 与属性名相同
- 文本节点的 nodeName 始终是 #text
- 文档节点的 nodeName 始终是 #document
**nodeValue:**nodeValue 属性规定节点的值。
- 元素节点的 nodeValue 是 undefined 或 null
- 文本节点的 nodeValue 是文本本身
- 属性节点的 nodeValue 是属性值
**nodeType:**nodeType 属性返回节点的类型。nodeType 是只读的。
比较重要的节点类型有:
元素类型 | NodeType |
---|---|
元素 | 1 |
属性 | 2 |
文本 | 3 |
注释 | 8 |
文档 | 9 |
example2
<button id="btn" >点啊</button>
<script>
var btn = document.getElementById('btn'); console.log(btn.nodeType,btn.nodeName,btn.childNodes[0].nodeValue, btn.innerHTML); //1 "BUTTON" "点啊" "点啊"
</script>
3.dom节点间的关系
childNodes: 获取所有直接子·节点(包括非元素节点)。
children: 获取所有直接子·节点(不包括非元素节点)。推荐!!!
parentElement: 获取父节点。
**parentNode:**获取父节点。
firstChild: 获取第一个子节点(包括非元素节点)。
firstElementChiild: 获取第一个子节点(不包括非元素节点)。ie9-不支持。
lastChild: 获取最后一个子节点(包括非元素节点)。
**lastElementChild:**获取最后一个子节点(不包括非元素节点)。ie9-不支持。
**previousSibling:**获取前一个兄弟子节点(包括非元素节点)。
**previousElementSibling:**获取前一个兄弟节点(不包括非元素节点)。ie9-不支持。
nextSibling: 获取下一个兄弟子节点(包括非元素节点)。
nextElementSibling: 获取下一个兄弟节点(不包括非元素节点)。ie9-不支持。
example3
<div id="dv">
<a href="#">a1</a><br>
<a id='a2' href="#">a2</a><br>
<a href="#">a3</a>
</div>
<script>
var a2 = document.getElementById('a2');
console.dir(a2.parentNode);
console.dir(a2.parentElement);
console.dir(dv.childNodes);
console.dir(dv.children);
console.dir(dv.firstChild);
console.dir(dv.firstElementChild);
console.dir(dv.lastChild);
console.dir(dv.lastElementChild);
console.dir(a2.previousSibling);
console.dir(a2.previousElementSibling);
console.dir(a2.nextSibling);
console.dir(a2.nextElementSibling);
</script>
写兼容处理,其实通过浏览器对象含有的属性来判断浏览器的范围,然后针对不同的情况选择对应的处理。
example4
<div id="dv">
<a href="#">a1</a>
<a id='a2' href="#">a2</a>text
<a href="#">a3</a>
</div>
<script>
//获取下一个兄弟节点
function nextSibling(node){
var nextSib = null;
if(node.nextElementSibling){
nextSib = node.nextElementSibling;
}else{
nextSib = node.nextSibling;
if(nextSib&&nextSib.nodeType != 1)
nextSib = nextSibling(nextSib);
}
return nextSib;
}
nextSibling(a2).style.color = 'red';
</script>
其他的兼容照着这个模仿就ok了。