Archive for December, 2009
知名站点js库使用情况统计
| jQuery | Prototype | YUI | Mootools |
|---|---|---|---|
| 微软 新版MSN 诺基亚 美国在线 Google Code 维基百科 W3C 戴尔 IBM Digg Mozilla.org wordpress.org 以及wordpress博客后台 Netflix alexa.com ESPN 亚马逊 Box.net QQ校友 QQ群 改版后的新浪NBA频道 豆瓣 土豆 |
Apple CNN Ajaxian Adobe 37signals 开心网 优酷 |
Yahoo mozilla.com 美味书签 WebQQ 淘宝 支付宝 口碑网 阿里中文站 阿里国际站 沃尔玛 Paypal |
phpMyAdmin CNET 必应 wufoo.com Ask.com About.com |
开源js库在这些顶尖站点中的使用率如此之高,可见一定有其过人之处。顺便推荐火狐的Library Detactor插件,在firefox的状态栏显示正在访问的站点使用的是什么js库。
javascript的事件绑定函数
最早给DOM节点绑定事件处理函数的方法是onevent方式,例如:
function handler() {
// 函数内容略
}
var aaa = document.getElementById('aaa');
aaa.onclick = handler;
这种方式具有不错的兼容性,但是缺点是最多只能给一个元素绑定一个函数,后面绑定的函数会把前面绑定的函数覆盖掉。
2001年Scott Andrew LePera写了一个包裹函数addEvent()来解决事件绑定的问题,后来被开发人员广泛采用。但是这个函数略显复杂,ppk在《ppk谈javascript》里面推荐的两个函数是这个函数的简化版:
function addEventSimple(obj,evt,fn) {
if (obj.addEventListener)
obj.addEventListener(evt,fn,false);
else if (obj.attachEvent)
obj.attachEvent('on'+evt,fn);
}
function removeEventSimple(obj,evt,fn) {
if (obj.removeEventListener)
obj.removeEventListener(evt,fn,false);
else if (obj.detachEvent)
obj.detachEvent('on'+evt,fn);
}
这个函数解决了给一个元素绑定多个事件处理函数的问题,然而缺点是在ie里面是事件处理函数里面的this关键字不能指向所绑定的DOM对象,而是指向window对象,即作用域错误。为了解决这个问题ppk在2005年举办了一个addEvent()函数重构竞赛。竞赛的优胜者是jQuery的作者john Resig,他的函数如下:
function addEvent( obj, type, fn ) {
if ( obj.attachEvent ) {
obj['e'+type+fn] = fn;
obj[type+fn] = function(){obj['e'+type+fn]( window.event );}
obj.attachEvent( 'on'+type, obj[type+fn] );
} else
obj.addEventListener( type, fn, false );
}
function removeEvent( obj, type, fn ) {
if ( obj.detachEvent ) {
obj.detachEvent( 'on'+type, obj[type+fn] );
obj[type+fn] = null;
} else
obj.removeEventListener( type, fn, false );
}
这个函数修正了作用域,也有相配套的解除绑定函数,但是仍然有严重缺陷:他将字符串和函数相加以得到唯一的hash值,这种做法效率低下,并且不兼容某些手机浏览器。后来John Resig自己也说他不建议别人使用这两个函数(需要翻墙)。
在ppk的竞赛结束之后,作为评委的Dean Edwards也写了一个自己的addEvent()函数:
// written by Dean Edwards, 2005 // http://dean.edwards.name/ function addEvent(element, type, handler) { // 为每个事件处理程序分配一个唯一的id if (!handler.$$guid) handler.$$guid = addEvent.guid++; // 为该元素的各种事件类型创建一个hash表 if (!element.events) element.events = {}; // 为每一个元素/事件对的所有事件处理程序创建一个hash表 var handlers = element.events[type]; if (!handlers) { handlers = element.events[type] = {}; // 存储已经存在的事件处理程序(如果有的话) if (element["on" + type]) { handlers[0] = element["on" + type]; } } // 将事件处理程序存储到hash表内 handlers[handler.$$guid] = handler; // 剩下的任务交给一个全局的事件处理程序来搞定 element["on" + type] = handleEvent; }; // 一个用来分配唯一ID的计数器 addEvent.guid = 1; function removeEvent(element, type, handler) { // 从hash表里面删除该事件处理程序 if (element.events && element.events[type]) { delete element.events[type][handler.$$guid]; } }; function handleEvent(event) { var returnValue = true; // 取得event对象(IE使用了一个全局的事件对象) event = event || fixEvent(window.event); // 找到事件处理程序的hash表 var handlers = this.events[event.type]; // 执行各个事件处理程序 for (var i in handlers) { this.$$handleEvent = handlers[i]; if (this.$$handleEvent(event) === false) { returnValue = false; } } return returnValue; }; function fixEvent(event) { // 增加符合W3C标准的事件模型 event.preventDefault = fixEvent.preventDefault; event.stopPropagation = fixEvent.stopPropagation; return event; }; fixEvent.preventDefault = function() { this.returnValue = false; }; fixEvent.stopPropagation = function() { this.cancelBubble = true; };
Dean Edwards的这个方法是相对来说最完美的一个方案,没有使用addEventListener和attachEvent就实现了多重函数的绑定,并且事件处理函数支持this关键字。
上面提到的这些都有提供相配套的解除绑定函数,但是大部分时候我们只需要绑定,不需要解绑,这种情况下下面这个简单的函数已经足够满足我们的需要了。
function addEvent( obj, type, fn ) {
if (obj.addEventListener)
obj.addEventListener(type, fn, false);
else if (obj.attachEvent)
obj.attachEvent('on' + type, function() { return fn.call(obj, window.event);});
}
大部分开源的js框架,如jQuery和YUI都有提供很方便的事件绑定接口,其实现的方式就复杂多了,当然功能也要强大得多。等有时间再研究。
谁说IE6不支持!important
许多人认为ie6不支持!important,其实是被一条针对ie 6的css hack给误导了。
这条css hack是:
.test {
height: auto !important;
height: 500px;
}
.test的高度在其他浏览器里面是auto,而在ie6里面是500px,许多人在解释这条css hack之所以会生效是因为ie6不支持!important,误导了不少人。
其实ie6本身是支持!important的,下面换一种写法:
.test {
height: auto !important;
}
.test {
height: 500px;
}
发现ie6里面.test的高度也是auto,这说明ie6是支持!important的。那上面的hack之所以会生效是因为ie6的一个小小的bug,即当你把两条相同的声明放到同一个选择器里面的时候ie6才不认识!important。然而大部分时候,这个小小的bug并不影响我们在ie6内使用!important。
JS库开发原则
1.保持无侵入式
html代码不必关心你的javascript在干什么。
2.严禁使用Object.prototype
这一条是如此重要以至于它有资格单独成为一条原则,对象是javascript最基本的构建元素,别把他弄乱了。
3.不要过度扩展
对javascript的内建对象的扩展越少越好。别误会,内建javascript对象本身有用的方法很少,你可能觉得有必要增加自己的一两个方法, 但是一两个对一个有创造力(js库)的程序员来说是不够的,停下来,只增加你真正想要的。越少去扩展内建对象,你与其他框架发生冲突的可能性就越少。
4.紧随标准
作为一个js库开发者,你在为javascript代码建立模式,然而模式在编程语言中意味着差劲,记住,关于javascript和DOM新 标准在不停地修订中,如果你打算去“修正”某些东西,那么先看看那些东西是否已经被修正过了。参考一下已有的解决方案。一旦跟随标准,请别掉队(比如,在 forEach方法中一个参数也不要漏掉)
5.或者跟随领袖
Mozilla引领javascript的潮流。该语言的发明 者,Brendan Eich,仍然在改进它。js语言的新特性总是在Mozilla浏览器中最先出现,如果你打算给javascript语言增加新特性,请参考 Mozilla标准先。例如,如果你想增加一个遍历数组的方法,那么请把这个方法命名为forEach而不是each。而如果你是在补全缺失的功能,则要 严格参照现有标准(参见上一条)。
6.灵活一点
如果我想在不动你的js库源代码的情况下更改某个方法的行为,是否足够简单?如果还不够简单就让它更简单一点吧。
7.管理好内存
8.去除浏览器检测
似乎浏览器厂商在增加新特性的竞争上永无休止。作为js库开发者,你应该紧跟最新的潮流,你不应该仅仅是偶尔看一下Ajaxian,你应该不知疲倦地阅读每一篇blog以便获知最新的hack,浏览器检测会让你越陷越深。
9.越小越好
许多js库已很成熟,其中一些已经被一些很重要的网站所采用。但不是每个人都在用2M的DSL宽带,所以保持你的js库小巧。最好能提供一个build页面让我能够快速的根据我的需要定制出我的js库。
10.第十条
很棒第十条,你总能信赖这一条。第十条是:可预见性。我应该可以根据方法的名字猜到它是干什么的,同样地,如果记不起一个方法的名字,我也应该可以猜得到。
11.附加的几条
1)文档,虽然讨厌但意义重大。
2)多用名字空间,这样我才不至于打电话骚扰你。
3)记住数以百万计的用户潜在用户可能会运行你的代码。
顺便声明,base2没有更改任何内建的javascript对象。
本文翻译自Rules For JavaScript Library Authors,这是Dean Edwards在开发base2时候的一些体会,对于在开发自己的js库的同学应该有较强的借鉴意义。Dean Edwards是公认的javascript高手,jQuery的作者Jhone Resig也很欣赏他。