最近看《精通javascript》,作者是jquery之父,相信很多学习javascript的园友也看过这本书,值得研习。看完第五章(the document object model)和第六章(events)感觉还是很受益的,虽然之前对dom和event也算了解,但这次应该说理解更深入了些。
在遍历Dom中,有个要注意的问题就是,dom中节点的指针(如elem.parentNode, elem.firstChild, elem.nextSibling等)可以指向元素节点,也可以指向文本节点,这里就会造成一定的混乱,比如:
1 <div id="content"> 2 <p>This is main content area</p> 3 </div>
你会怎么定位div中的p元素,可能会想当然的document.getElementById("content").firstChild,遗憾的是得到的是个"\n"(一个文本类型的节点nodeType为3),
你要用document.getElementById("content").firstChild.nextSibling才行,这样是不是很麻烦,而且不够简洁和利于阅读。书中封装了一些简单的遍历dom的方法。
1 function prev(elem) { 2 do { 3 elem = elem.previousSibling; 4 } while(elem && elem.nodeType != 1); // nodeType为1表明节点为元素如:<p>,<a> 5 6 return elem; 7 } 8 9 function next(elem) { 10 do { 11 elem = elem.nextSibling; 12 } while( elem && elem.nodeType != 1); 13 14 return elem; 15 } 16 17 function first(elem) { 18 elem = elem.firstChild; 19 20 return elem && elem.nodeType != 1 ? 21 next(elem) : elem; 22 } 23 24 function last(elem) { 25 elem = elem.lastChild; 26 27 return elem && elem.nodeType != 1 ? 28 prev(elem) : elem; 29 } 30 31 function parent(elem, num) { 32 //如果num不提供的话,就默认1, 指元素嵌套层数 33 num = num || 1; 34 35 for(var i = 0; i < num; i++) { 36 if(elem != null ) elem = elem.parentNode; 37 } 38 39 return elem; 40 }
通过这些简单的函数,我们可以像这样遍历dom:first(document.getElementById("content"));这似乎好多了,但是如果让函数的调用换成这种形式应该更符合习惯:document.body.first().next(),我们可以通过扩展HTMLElement达到这样的效果(可惜该死的IE中不能直接访问HTMLElement对象)。
1 HTMLElement.prototype.next = function() { 2 var elem = this; 3 4 do { 5 elem = elem.nextSibling; 6 } while(elem && elem.nodeType != 1); 7 8 return elem; 9 }
书中也提到了很常用的getElementByID和getElementsByTagName方法,并提供了简单的封装
1 function tag(name, elem) { 2 //如果elem没提供,那么默认document 3 return (elem || document).getElementsByTagName(name); 4 } 5 6 function id(elemId) { 7 return document.getElementById(elemId); 8 }
接下来的一个重点是waiting for the HTML DOM to load这点很重要,作者也特别说明了下网页加载的顺序:
1.HTML被浏览器解析
2.外部的js和css被载入
3.当在文档中js被解析时,js被执行
4.HTML DOM构造完成
5.图片和外部内容被载入
6.页面加载完成
很显然这里就有个问题,较文档靠前的js或是外部载入的js,如果没等HTML DOM全部构造完,那么这些js对DOM的访问当然会出现问题,作者就此问题给出了几个解决方案。
1.利用window.onload事件,这里为了说明我改了原文中的那addEvent方法(那个复杂些Dean Edwards写的)
1 function addEvent(obj, event, eventHandler) { 2 if(obj.addEventListener) { 3 //非IE浏览器 W3C标准 4 obj.addEventListener(event, eventHandler, false); 5 } else if (obj.attachEvent) { 6 //写到这里,我就觉得IE该死。。。 7 event = "on" + event; 8 obj.attachEvent(event, eventHandler); 9 } 10 }
然后我们可以这样:
1 addEvent(window, "load", function() { 2 first(id("content")).style.background = 'blue'; 3 });
这个方法很常用,觉得不错。
2.就是把js的执行代码放到DOM的最后位置
1 <h1>Testing DOM loading</h1> 2 <!-- lots of HTML goes here --> 3 <script type="text/javascript"> 4 init();//这里的init方法已写在<head>中 5 </script>
3.这个方法是作者自己写的一个确认dom load结束的一个函数,主要是用来实时监控dom文档的状态,代码如下:
1 function isDOMReady() { 2 //如果我们已经确认页面加载结束,那么忽略 3 if(domReady.done) return false; 4 5 //看看是否一些函数和元素能够被访问 6 if(document && document.getElementsByTagName && document.getElementById && document.body) { 7 //如果一切就绪,我们停止检查 8 clearInterval(domReady.timer); 9 10 domReady.timer = null; 11 12 //执行列队中所有的函数 13 for(var i = 0; i < domReady.ready.length; i++) { 14 domReady.ready[i](); 15 } 16 17 //记住我们现在搞定了 18 domReady.ready = null; 19 domReady.done = true; 20 } 21 } 22 23 function domReady(f) { 24 //如果DOM已经加载结束,那么马上执行该函数 25 if(domReady.done) return f(); 26 27 //如果我们已经添加了一个函数 28 if(domReady.timer) { 29 //将它加入到执行列表中 30 domReady.ready.push(f); 31 } else { 32 //为页面加载完毕附加一个事件 33 addEvent(window, 'load', isDOMReady); 34 35 //初始化执行函数列表 36 domReady.ready = [f]; 37 38 //尽快检查DOM是否加载完毕 39 domReady.timer = setInterval(isDOMReady, 13); 40 } 41 }
作者介绍了些找元素的方法,有个Finding Elements by Class Name,实现如下:
1 function hasClass(name, type) { 2 var r = []; 3 4 //定位class name(允许多个class) 5 var re = new RegExp("(^|\\s)" + name + "(\\s|$)"); 6 var e = document.getElementsByTagName(type || "*"); 7 8 for(var j = 0; j < e.length; j++) { 9 //如果该元素有该class,那么加入到结果集 10 if( re.test(e[j].className) ) r.push(e[j]); 11 } 12 13 return r; 14 }
今天就先到这,明天继续复习.......