[关闭]
@EncyKe 2015-12-21T04:41:27.000000Z 字数 6506 阅读 2491

DOM

前端 JavaScript



一、DOM简介

1. 定义

DOM:即Document Object Model,文档对象模型;把整个页面映射为一个多层节点树结构。

2. 历史

为了解决netscape和microsoft两家浏览器之间在脚本上相互不兼容的问题,W3C联盟统一制定了DOM标准——

  • DOM 0级: NetScape 4-和Microsoft IE 4-洪荒年代DHTML;
  • DOM 1级:主要定义了HTML和XML文档的底层结构。由两个模块组成:DOM Core和DOM HTML。DOM Core规定了基于XML的文档结构标准,通过这个标准简化了对文档中任意部分的访问和操作;DOM HTML则在DOM核心的基础上加以扩展,添加了针对HTML的对象和方法;
  • DOM 2级:在原来DOM的基础上又扩充了鼠标、用户界面事件、范围、遍历等细分模块,而且通过对象接口增加了对CSS的支持,核心模块也经过扩展开始支持XML命名空间。引入了下列模块,包含了众多新类型和新接口:DOM视图(DOM Views):定义了跟踪不同文档视图的接口、DOM事件(DOM Events):定义了事件和事件处理的接口、DOM样式(DOM Style):定义了基于CSS为元素应用样式的接口、DOM遍历和范围(DOM Traversal and Range):定义了遍历和操作文档树的接口;
  • DOM 3级:进一步扩展了DOM,引入了以下模块:DOM加载和保存模块(DOM Load and Save):引入了以统一方式加载和保存文档的方法、DOM验证模块(DOM Validation):定义了验证文档的方法、DOM核心的扩展(DOM Style):支持XML 1.0规范,涉及XML Infoset、XPath和XML Base;

3. 文档类型

  • 通用标记语言 (GML):1969;
  • 标准通用标记语言 (SGML):1985;
  • 超文本标记语言 (HTML):1993;
  • 可扩展标记语言 (XML):1998;

二、DOM Ready

1. 浏览器渲染基本流程

  1. 解析HTML、构建DOM树
  2. 构建渲染树
  3. 布局渲染树
  4. 绘制渲染树
    Webkit主要渲染流程

2. DOM Ready实现策略

1) setTimeout()

特点:有闪动,不稳定,体验欠佳;

2) window.onload =

特点:稳妥,但大量JS存在时体验也是欠佳;

3) $(document).ready(...);

特点:应用了DOMContentLoaded及其对IE的hack,兼容性好;
DOMContentLoadedonload要快。

4) 自定义函数

  1. function myReady(fn){
  2. //对于现代浏览器,对DOMContentLoaded事件的处理采用标准的事件绑定方式
  3. if ( document.addEventListener ) {
  4. document.addEventListener("DOMContentLoaded", fn, false);
  5. } else {
  6. IEContentLoaded(fn);
  7. }
  8. //IE模拟DOMContentLoaded
  9. function IEContentLoaded (fn) {
  10. var d = window.document;
  11. var done = false;
  12. //只执行一次用户的回调函数init()
  13. var init = function () {
  14. if (!done) {
  15. done = true;
  16. fn();
  17. }
  18. };
  19. (function () {
  20. try {
  21. // DOM树未创建完之前调用doScroll会抛出错误
  22. d.documentElement.doScroll('left');
  23. } catch (e) {
  24. //延迟再试一次~
  25. setTimeout(arguments.callee, 50);
  26. return;
  27. }
  28. // 没有错误就表示DOM树创建完毕,然后立马执行用户回调
  29. init();
  30. })();
  31. //监听document的加载状态
  32. d.onreadystatechange = function() {
  33. // 如果用户是在domReady之后绑定的函数,就立马执行
  34. if (d.readyState == 'complete') {
  35. d.onreadystatechange = null;
  36. init();
  37. }
  38. }
  39. }
  40. }

三、DOM节点

1. 节点类型

共12种,常用7种:

节点类型 数值常量 字符常量
Element (元素节点) 1 ELEMENT_NODE
Attr (属性节点) 2 ATTRIBUTE_NODE
Text (文本节点) 3 TEXT_NODE
Comment (注释节点) 8 COMMENT_NODE
Document (文档节点) 9 DOCUMENT_NODE
DocumentType (文档类型节点) 10 DOCUMENT_TYPE_NODE
DocumentFragment (文档片段节点) 11 DOCUMENT_FRAGMENT_NODE

1) element.nodeType属性返回节点类型

返回值可以是数值常量,也可以是字符常量;鉴于数值常量兼容性更加,建议统一使用数值变量作判断。

  1. element.nodeType == Node.ELEMENT_NODE;
  2. // IE不支持;Node未定义。
  3. element.nodeType == 1;
  4. // 兼容性佳;

2) element.nodeName返回元素的名称

3) element.nodeValue设置或返回节点值

节点类型 nodeName nodeValue
Element (元素节点) 标签名 null
Attr (属性节点) 属性 属性值E
Text (文本节点) text 内容文本
Comment (注释节点) comment 注释文本
DocumentType (文档类型节点) doctype名 null
DocumentFragment (文档片段节点) document-fragment null

4) element.attributes返回元素属性集合

5) element.childNodes返回元素子节点的集合

2. 判断函数

1) isElement判断是否为元素节点

精简版:

  1. var isElement = function (el){
  2. return !!el && el.nodeType === 1;
  3. }

完整版:

  1. var isElement = function (obj) {
  2. if (obj && obj.nodeType === 1) {
  3. //先过滤最简单的
  4. if( window.Node && (obj instanceof Node )){
  5. //如果是IE9,则判定其是否Node的实例
  6. return true;
  7. //由于obj可能是来自另一个文档对象,因此不能轻易返回false
  8. }
  9. try {
  10. //最后以这种效率非常差但肯定可行的方案进行判定
  11. testDiv.appendChild(obj);
  12. testDiv.removeChild(obj);
  13. } catch (e) {
  14. return false;
  15. }
  16. return true;
  17. }
  18. return false;
  19. }

2) isHTML判断是否为HTML元素节点

严谨版:

  1. var isHTML = function(doc) {
  2. return doc.createElement("p").nodeName === doc.createElement("P").nodeName;
  3. }

3) isXML判断是否为XML元素节点

精简版:

  1. var isXML = window.HTMLDocument ? function(doc) {
  2. return !(doc instanceof HTMLDocument);
  3. } : function(doc) {
  4. return "selectNodes" in doc;
  5. }

完整版:

  1. var isXML = function(document) {
  2. return (!!document.xmlVersion) || (!!document.xml) || (toString.call(document) == '[object XMLDocument]')
  3. || (document.nodeType == 9 && document.documentElement.nodeName != 'HTML');
  4. };

严谨版:

  1. var isXML = function(doc) {
  2. return doc.createElement("p").nodeName !== doc.createElement("P").nodeName;
  3. }

4) contains (原生) 以及isContains判断节点包含关系

IE下的contains方法只对元素节点起作用;

  1. function isContains(a, b) {
  2. try {
  3. while ((b = b.parentNode)){
  4. if (b === a){
  5. return true;
  6. }
  7. }
  8. return false;
  9. } catch (e) {
  10. return false;
  11. }
  12. }

3. 节点继承层次

1) 元素节点继承层次

元素节点类

2) 文本节点继承层次

文本节点类
鉴于继承层次复杂,建议尽量使用现有框架(如MVVM)实现业务。


四、DOM事件

1. 事件流

  1. 事件冒泡流 (IE):指从事件所在的最具体的元素一直冒泡到最不具体的元素;
  2. 事件捕获流 (NetScape):不太具体的节点更早接收到事件,而最具体的节点最后接收到事件;

2. 事件处理程序

1) HTML事件处理程序

  1. 在标签内的事件属性内写JS后者HTML的script标签内嵌JS;
  2. 耦合度过高;

2) DOM 0级事件处理程序

  1. 取出元素,为其属性添加一个事件,即把一个函数赋值给一个事件处理程序的属性;
  2. 较传统,简单,可跨浏览器;
  3. 删除:对象.onclick = null;

3) DOM 2级事件处理程序

  1. addEventListener():添加事件监听;
    removeEventListener():删除事件监听;
  2. 语法:addEventListener('事件名',函数名,布尔值);
    • addEventListener()添加的事件只能通过removeEventListener()删除;
    • 删除时三个参数必须与添加的三个一模一样;
    • 可给一个元素上添加多个事件,依次执行;
    • 布尔值:false表冒泡、true表捕获;

4) IE事件处理程序

  1. IE 8及更早只支持事件捕获;
  2. attachEvent()添加事件监听;
    detachEvent()删除事件监听;
  3. 语法:attachEvent('事件名',函数名);

5) 跨浏览器事件处理程序

事件处理程序兼容性封装
  1. var eventUtil={
  2. // 添加句柄
  3. addHandler:function(element,type,handler){
  4. if(element.addEventListener){
  5. element.addEventListener(type,handler,false);
  6. }else if(element.attachEvent){
  7. element.attachEvent('on'+type,handler);
  8. }else{
  9. element['on'+type]=handler;
  10. // JS下:element.onclick===element['onclick'];
  11. }
  12. },
  13. // 删除句柄
  14. removeHandler:function(element,type,handler){
  15. if(element.removeEventListener){
  16. element.removeEventListener(type,handler,false);
  17. }else if(element.detachEvent){
  18. element.detachEvent('on'+type,handler);
  19. }else{
  20. element['on'+type]=null;
  21. }
  22. }
  23. }

引用示例:eventUtil.addHandler(go,'click',function);

3. 事件对象

1) DOM事件对象

  1. type属性:获取事件类型;
  2. target属性:获取事件目标;event.target.nodeName获取标签名;
  3. stopPropagation()方法:阻止事件冒泡;
  4. preventDefault()方法:阻止事件的默认行为;

2) IE事件对象

  1. type属性:获取事件类型;
  2. srcElement属性:事件目标;
  3. cancelBubble=true:阻止事件冒泡;
  4. returnValue=false:阻止事件的默认行为;
getElementByClassName兼容性封装
  1. function getByClass(clsName,parent){
  2. var oParent=parent?document.getElementById(parent):document,
  3. eles=[],
  4. elements=oParent.getElementsByTagName('*');
  5. for(var i=0,l=elements.length;i<l;i++){
  6. if(elements[i].className==clsName){
  7. eles.push(elements[i]);
  8. }
  9. }
  10. return eles;
  11. }

引用示例:getByClass(类名,父级)[0];父级可加可不加。

事件对象兼容性封装
  1. var eventUtil={
  2. getEvent:function(event){
  3. return event?event:window.event;
  4. // return event = event || window.event;
  5. // window.event是为IE 8及更早做的兼容;
  6. },
  7. getType:function(event){
  8. return event.type;
  9. },
  10. getElement:function(event){
  11. return event.target || event.srcElement;
  12. },
  13. preventDefault:function(event){
  14. if(event.preventDefault){ // 判断时不能加括号;
  15. event.preventDefault();
  16. }else{
  17. event.returnValue=false;
  18. }
  19. },
  20. stopPropagation:function(event){
  21. if(event.stopPropagation){
  22. event.stopPropagation();
  23. }else{
  24. event.cancelBubble=true;
  25. }
  26. }
  27. }

引用示例:eventUtil.getEvent(event);

4. 事件

  1. 鼠标事件
  2. 键盘事件
    • onkeydown:按下键盘上任意键时触发(按住不放会重复触发);
    • onkeypress:按下键盘上的字符键时触发;
    • onkeyup:释放键盘上的键时触发;
查询某键键码(通过控制台打印)
  1. document.onkeyup=function(event){
  2. event = event || window.event;
  3. console.log(event.keyCode);
  4. }

附:参考

慕课:DOM探索之基础详解篇
慕课:DOM事件探秘



添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注