【写一个自己的js库】 1.搭个架子先
最近在看《javascript dom 高级程序设计》,想着跟着里面的代码敲一遍吧,也算是做一下学习笔记吧,所以这不是重新发明轮子,只是个学习的过程。
1.先确定自己的命名空间,并且加入几个常用的js方法。命名空间很重要啊,保证了自己库里的变量不污染全局空间,不和其他的库的变量名起冲突。命名空间的惯例就是把代码都放在自执行函数(function(){})()里了,然后暴露个方法给window对象了。所以0.1版本的代码就长下面这样了。(这里暂时没有用继承)
(function(){ //命名空间 var Lily = {} if(!window.Lily){ window['Lily'] = Lily } function isCompatible(other){} Lily['isCompatible'] = isCompatible; function $(){} Lily['$'] = $; function addEvent(node, type, listener){} Lily['addEvent'] = addEvent; function removeEvent(node, type, listener){} Lily['removeEvent'] = removeEvent; function getElementsByClassName(className, tag, parent){} Lily['getElementsByClassName'] = getElementsByClassName; function toggleDisplay(node, value){} Lily['toggleDisplay'] = toggleDisplay; function insertAfter(node, referenceNode){} Lily['insertAfter'] = insertAfter; function removeChildren(parent){} Lily['removeChildren'] = removeChildren; function prependChild(parent, newChild){} Lily['prependChild'] = prependChild; })()
2.开始填充方法了。isCompatible是看浏览器是否能与这个库兼容,这里用能力检测来看浏览器是否能支持库中所使用的方法。
function isCompatible(other){ if(other === false || !Array.prototype.push || !Object.hasOwnProperty || !document.createElment || !document.getElementsByTagName ){ return false; } return true; }
3.$是查找dom对象的方法,这个要比jquery的$方法简单许多,只支持代表id的字符串或字符串数组,或者dom对象。支持dom对象是方便库中的方法,当参数为字符串或者dom都可以用$转成dom对象。
function $(){ var elements = new Array(); for(var i = 0; i < arguments.length; i++){ var element = arguments[i]; if(typeof element == "string"){ element = document.getElementById(element); } if(arguments.length == 1){ return element; } elements.push(element); } return elements; }
4.addEvent是支持跨浏览器必须实现的一个方法,ie的事件绑定方法是attachEvent,并且它的方法参数中没有event对象,所以手动将window.event传入到listener中。之前有点困惑为什么没有用匿名函数绑定,查了下用匿名函数的话,就无法解除绑定了。ie6,7,8中用attachEvent绑定的事件中,this指向的是window,所以用node.call修正了this的指向问题。addEventListener的第三个参数指是否使用捕获,默认值是false,而且ie6,7,8中只支持冒泡,所以一般都用false。关于addEvent有个更周全的版本就是Dean Edwards的,jquery中也参考了他的很多方法,目前为了保持简单,先用这个,以后可能会替换成Dean Edwards版本的。
function addEvent(node, type, listener){ if(!isCompatible()) return false; if(!(node = $(node))) return false; if(node.addEventListener){ node.addEventListener(type, listener, false); return true; }else if(node.attachEvent){ node[type + listener] = function(){ listener.call(node, window.event); }; node.attachEvent('on'+type, node[type + listener]); return true; } return false; }
5.removeEvent就没什么好说的了,就是detach之后,别忘了把node[type + listener]释放掉。
function removeEvent(node, type, listener){ if(!(node = $(node))) return false; if(node.removeEventListener){ node.removeEventListener(type, listener, false); return true; }else if(node.detachEvent){ node.detachEvent('on'+type, node[type + listener]); node[type + listener] = null; return true; } return false; }
6.getElementsByClassName根据class选择dom对象,后两个参数可以不传。parent.all貌似是解决ie5的bug吧,没有细研究,总之满足条件就用document.all吧。正则表达式(^|\\s+)匹配起始位置或者空格。
function getElementsByClassName(className, tag, parent){ var elements = new Array(); parent = parent || document; tag = tag || "*"; if(!(parent = $(parent))) return false; var allTags = (tag == "*" && parent.all) ? parent.all : parent.getElementsByTagName(tag); className = className.replace(/\-/g, "\\-"); var regexp = new RegExp("(^|\\s+)" + className + "(\\s+|$)"); for(var i = 0; i < allTags.length; i++){ var element = allTags[i]; if(regexp.test(element.className)){ elements.push(element); } } return elements; }
7.toggleDisplay第二个参数也可以不传,但是当元素的display不是空值时(比如display:inline-block),可以传入第二个参数,这样在切换时就保留了原来的值。
function toggleDisplay(node, value){ if(!(node = $(node))) return false; if(node.style.display != "none"){ node.style.display = "none" }else{ node.style.display = value || ''; } return true; }
8.insertAfter在referenceNode后插入元素。
function insertAfter(node, referenceNode){ if(!(node = $(node))) return false; if(!(referenceNode = $(node))) return false; return referenceNode.parentNode.insertBefore(node, referenceNode.nextSibling); }
9.removeChildren删除所以子节点。
function removeChildren(parent){ if(!(parent = $(parent))) return false; while(parent.firstChild){ parent.firstChild.parentNode.removeChild(parent.firstChild); } return parent; }
10.prependChild将子节点加入到父节点中的第一个。
function prependChild(parent, newChild){ if(!(parent = $(parent))) return false; if(!(newChild = $(newChild))) return false; if(parent.firstChild){ parent.insertBefore(newChild, parent.firstChild); }else{ parent.appendChild(newChild); } return parent; }
现在一个简单的框架就搭完了,用下面的代码测试一下啊。以下是改写a标签的默认事件,用弹出事件代替跳转事件。
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Document</title> <script src="Lily-0.1.js" type="text/javascript"></script> <script type="text/javascript"> Lily.addEvent(window, "load", function(){ var aTags = Lily.getElementsByClassName("popup"); for(var i = 0 ; i < aTags.length; i++){ Lily.addEvent(aTags[i], "click", function(){ alert(this.href); return false; }); } }); </script> </head> <body> <ul> <li><a href="hello.html" class="popup">hello</a></li> <li><a href="hello.html" class="popup">hello</a></li> <li><a href="hello.html" class="popup">hello</a></li> </ul> </body> </html>
还未进行浏览器兼容性测试,如有问题,请指正,谢谢~