命名空间
如果想要创建一个所有人都可以访问的函数alertNodeName(),该函数用来访问HTML元素的nodeName属性。同时,也想创建一个通过id来获取元素的$()函数,以便在alertNodeName()以及自己定义的库中的其它函数内部使用它:
function $(id) { return document.getElementById(id); } function alertNodeName(id) { alert($(id).nodeName); }
现在的问题是$()函数与Prototype库中众所周知的$()函数重名,这样会导致一些问题。要保证只有自己能使用这个$()函数,可以使用一些JavaScript技巧,即一个自执行的匿名函数:
(function(){ //代码 })();
包围函数的括号返回未命名的函数,随后的一对空括号立即执行返回的未命名函数。如果向其中的函数传入参数是这样的:
(function(arg){ alert(arg); })('This will show in the alert box');
这样把自己所有的代码都写在这个特殊的函数内,那么将没有人能够在这个特殊的函数之外访问到里面定义的函数或对象。为了解决上面的函数冲突问题,可以将自己的$()和alertNodeName()函数放在这个伪命名空间里:
(function(){ function $(id) { return document.getElementById(id); } function alertNodeName(id) { alert($(id).nodeName); } window['myNamespace'] = {}; window['myNamespace']['showNodeName'] = alertNodeName; })();
使用这种伪命名空间可以封装自己的所有函数、对象和变量。由于它们都位于同一个函数之内,所以它们之间可以互相访问。不过脚本其它部分的代码将无法使用里面这些函数。
为了对受保护的这些脚本进行全局化,将alertNode()函数复制给window的一个方法:
window[‘myNamespace’][‘showNodeName’]
把函数绑定到window方法中的showNodeName(),当执行showNodeName函数即相当于执行受保护的alertNodeName()函数:
myNamespace.showNodeName(‘example’);
开始构建ADS库
首先需要按照上面的方法来命名一个唯一的命名空间。其次,创建一个名为ADS.js的JavaScript文件,并以下列命名空间和方法框架作为库的起点:
(function(){ //The ADS namespace window['ADS'] = {}; function isCompatible(other) { }; window['ADS']['isCompatible'] = isCompatible; function $() { }; window['ADS']['$'] = $; function addEvent(node, type, listener) { }; window['ADS']['addEvent'] = addEvent; function removeEvent(node, type, listener) { }; window['ADS']['removeEvent'] = removeEvent; function getElementsByClassName(className, tag, parent){ }; window['ADS']['getElementsByClassName'] = getElementsByClassName; function toggleDisplay(node,value) { }; window['ADS']['toggleDisplay'] = toggleDisplay; function insertAfter(node, referenceNode) { }; window['ADS']['insertAfter'] = insertAfter; function removeChildren(parent) { }; window['ADS']['removeChildren'] = removeChildren; function prependChild(parent, newChild) { }; window['ADS']['prependChild'] = prependChild; })();
1. ADS.isCompatible()方法
ADS.isCompatible()用来确定当前浏览器是否与整个库兼容:
/** * 检查当前浏览器是否与整个库兼容 */ function isCompatible(other) { // 使用能力检测 if( other===false || !Array.prototype.push || !Object.hasOwnProperty || !document.createElement || !document.getElementsByTagName ) { alert('TR- if you see this message isCompatible is failing incorrectly.'); return false; } return true; } window['ADS']['isCompatible'] = isCompatible;
2. ADS.$()方法
ADS.$()只是document.getElementById()的替代函数,它可以减少很多输入工作量:
/** * document.getElementById()的替换函数 */ function $() { var elements = new Array(); // 遍历所有的参数 for (var i = 0; i < arguments.length; i++) { var element = arguments[i]; // 如果参数是一个字符串,那么假设它是一个id if (typeof element == 'string') { element = document.getElementById(element); } // 如果只提供一个参数,那么就返回它 if (arguments.length == 1) { return element; } // 否则,将它添加到数组中 elements.push(element); } // Return the array of multiple requested elements return elements; }; window['ADS']['$'] = $;
ADS.$()函数支持请求多个元素,它会返回一个包含这些元素的数组。它的使用方法是:
var elements = ADS.$( 'a' , 'b' , 'c' , 'd' ); for ( e in elements ) { // 执行某些操作 }
而且,$()函数还可以对库中的其它方法起到协助作用,例如一个判断一个DOM元素是否存在:
if(!(obj = $(obj))) return false; // 在库中其它方法中 function exampleLibraryMethod(obj) { if(!(obj = $(obj))) return false; // 对obj进行某些操作 } window['ADS']['exampleLibraryMethod'] = exampleLibraryMethod;
3. ADS.addEvent()和ADS.removeEvent()方法
为对象添加/移除事件侦听器。
/** * 注册事件侦听器 */ function addEvent( node, type, listener ) { // 使用前面的方法检查兼容性 if(!isCompatible()) { return false } if(!(node = $(node))) return false; if (node.addEventListener) { // W3C 的方法 node.addEventListener( type, listener, false ); return true; } else if(node.attachEvent) { // MSIE 方法 // 创建一个属性来保存事件响应函数。node['e'+type+listener]就是listener,即要触发的函数 // 方法名['e'+type+listener]只是一个自定义的约定,在必要的时候可以按约定删除这个响应函数。 node['e'+type+listener] = listener; // 重新包装这个函数,函数名不一定要用node[type+listener] // 执行listener()函数,并且第一个参数是window.event node[type+listener] = function(){node['e'+type+listener]( window.event );} node.attachEvent( 'on'+type, node[type+listener] ); return true; } // 如果都不具备,则返回false return false; }; window['ADS']['addEvent'] = addEvent; /** * 移除一个事件侦听器 */ function removeEvent(node, type, listener ) { if(!(node = $(node))) return false; if (node.removeEventListener) { node.removeEventListener( type, listener, false ); return true; } else if (node.detachEvent) { // MSIE 方法 node.detachEvent( 'on'+type, node[type+listener] ); node[type+listener] = null; return true; } // 如果都不具备,则返回false return false; }; window['ADS']['removeEvent'] = removeEvent;
下面是addEvent()方法的例子:
ADS.addEvent(window,'load',function(W3CEvent) { // 查找文档中所有带popup类的锚标签 var popups = ADS.getElementsByClassName('popup', 'a'); for(var i=0 ; i
4. ADS.getElementByClassName()方法
getElementById()是通过id属性来获取DOM元素,getElementsByTagName()用来获取相同标签的一组元素。如果要获得一组具有某个公共属性的DOM元素且它们不是相同的标签呢?可以为一组元素指定相同的类属性,通过自定义的getElementByClassName()方法来获取元素:
/** * 基于类名来检索一组DOM元素 */ function getElementsByClassName(className, tag, parent){ parent = parent || document; if(!(parent = $(parent))) return false; // 查找所有匹配的标签 var allTags = (tag == "*" && parent.all) ? parent.all : parent.getElementsByTagName(tag); var matchingElements = new Array(); // 创建一个正则表达式,用来判断className是否正确 // 使用 \- 替换 - className = className.replace(/\-/g, "\\-"); // ^ 开始符号;| 表示或者;\\s即\s表示空字符串,即\t\n\f\r\v;$结束符号 var regex = new RegExp("(^|\\s)" + className + "(\\s|$)"); var element; // 检查每个元素 for(var i=0; i<allTags.length; i++){ element = allTags[i]; if(regex.test(element.className)){ matchingElements.push(element); } } // 返回匹配的元素 return matchingElements; }; window['ADS']['getElementsByClassName'] = getElementsByClassName;
这个方法的工作原理是在DOM文档树中查找类属性包含某个className的元素。以下面的文档为例:
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml"> <head> <meta http-equiv="Content-Type" content="text/html; charset=gb2312" /> <title>ADS.getElementsByClassName() Example</title> </head> <body> <h1 class="findme">ADS.getElementsByClassName() Example</h1> <p class="findme">This is just an <em class="a">example</em>.</p> <div id="theList"> <h2 class="findme">A list!</h2> <ol> <li class="findme">Foo</li> <li class="findme">Bar</li> </ol> </div> </body> </html>
如果执行以下语句:
var found = ADS.getElementsByClassName('findme','*',document);
将会得到包含以下元素的数组:
□ <h1>元素
□ <p>元素
□ <em>元素
□ <h2>元素
□ 列表中的第一个<li>元素
□ 列表中的第二个<li>元素
5. ADS.toggleDisplay()方法
ADS.toggleDisplay()方法用来切换DOM树中元素的可见性。
/** * 切换DOM元素的可见性 */ 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; } window['ADS']['toggleDisplay'] = toggleDisplay;
6. ADS.insertAfter()方法
在DOM核心规范中好像缺少insertAfter方法:
/** * 在DOM节点之后插入节点 */ function insertAfter(node, referenceNode) { if(!(node = $(node))) return false; if(!(referenceNode = $(referenceNode))) return false; return referenceNode.parentNode.insertBefore(node, referenceNode.nextSibling); }; window['ADS']['insertAfter'] = insertAfter;
7. ADS.removeChildren()和ADS.prependChild()方法
最后实现删除所有子节点和追加子节点的方法:
/** * 删除元素的所有子节点 */ function removeChildren(parent) { if(!(parent = $(parent))) return false; // 循环遍历所有子节点,并删除它 while (parent.firstChild) { parent.firstChild.parentNode.removeChild(parent.firstChild); } // 返回父元素,以便实现方法连缀 return parent; }; window['ADS']['removeChildren'] = removeChildren; /** * 在元素中追加子节点,即在头部插入子节点 */ 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; } window['ADS']['prependChild'] = prependChild;