jQuery除了为不同浏览器的事件模型建立了统一抽象之外,主要做了如下增强:
A. 增加了自定制事件(custom)机制. 事件的传播机制与事件内容本身原则上是无关的, 因此自定制事件完全可以和浏览器内置事件通过同一条处理路径, 采用同样的监听方式. 使用自定制事件可以增强代码的内聚性, 减少代码耦合. 例如如果没有自定制事件, 关联代码往往需要直接操作相关的对象
复制代码 代码如下:
$('.switch, .clapper').click(function() {
var $light = $(this).parent().find('.lightbulb');
if ($light.hasClass('on')) {
$light.removeClass('on').addClass('off');
} else {
$light.removeClass('off').addClass('on');
}
});
而如果使用自定制事件,则表达的语义更加内敛明确,
复制代码 代码如下:
$('.switch, .clapper').click(function() {
$(this).parent().find('.lightbulb').trigger('changeState');
});
B. 增加了对动态创建节点的事件监听. bind函数只能将监听函数注册到已经存在的DOM节点上. 例如
复制代码 代码如下:
$('li.trigger').bind('click',function(){}}
如果调用bind之后,新建了另一个li节点,则该节点的click事件不会被监听.
jQuery的delegate机制可以将监听函数注册到父节点上, 子节点上触发的事件会根据selector被自动派发到相应的handlerFn上. 这样一来现在注册就可以监听未来创建的节点.
复制代码 代码如下:
$('#myList').delegate('li.trigger', 'click', handlerFn);
最近jQuery1.7中统一了bind, live和delegate机制, 天下一统, 只有on/off.
复制代码 代码如下:
$('li.trigger').on('click', handlerFn); // 相当于bind
$('#myList').on('click', 'li.trigger', handlerFn); // 相当于delegate
8. 动画队列:全局时钟协调
抛开jQuery的实现不谈, 先考虑一下如果我们要实现界面上的动画效果, 到底需要做些什么? 比如我们希望将一个div的宽度在1秒钟之内从100px增加到200px. 很容易想见, 在一段时间内我们需要不时的去调整一下div的宽度, [同时]我们还需要执行其他代码. 与一般的函数调用不同的是, 发出动画指令之后, 我们不能期待立刻得到想要的结果, 而且我们不能原地等待结果的到来. 动画的复杂性就在于:一次性表达之后要在一段时间内执行,而且有多条逻辑上的执行路径要同时展开, 如何协调?
伟大的艾萨克.牛顿爵士在《自然哲学的数学原理》中写道:"绝对的、真正的和数学的时间自身在流逝着". 所有的事件可以在时间轴上对齐, 这就是它们内在的协调性. 因此为了从步骤A1执行到A5, 同时将步骤B1执行到B5, 我们只需要在t1时刻执行[A1, B1], 在t2时刻执行[A2,B2], 依此类推.
t1 | t2 | t3 | t4 | t5 ...
A1 | A2 | A3 | A4 | A5 ...
B1 | B2 | B3 | B4 | B5 ...
具体的一种实现形式可以是
A. 对每个动画, 将其分装为一个Animation对象, 内部分成多个步骤.
animation = new Animation(div,"width",100,200,1000,
负责步骤切分的插值函数,动画执行完毕时的回调函数);
B. 在全局管理器中注册动画对象
timerFuncs.add(animation);
C. 在全局时钟的每一个触发时刻, 将每个注册的执行序列推进一步, 如果已经结束, 则从全局管理器中删除.
复制代码 代码如下:
for each animation in timerFuncs
if(!animation.doOneStep())
timerFuncs.remove(animation)
解决了原理问题,再来看看表达问题, 怎样设计接口函数才能够以最紧凑形式表达我们的意图? 我们经常需要面临的实际问题:
A. 有多个元素要执行类似的动画
B. 每个元素有多个属性要同时变化
C. 执行完一个动画之后开始另一个动画
jQuery对这些问题的解答可以说是榨尽了js语法表达力的最后一点剩余价值.
复制代码 代码如下: