自定义事件和jQuery插件
Query 插件的实现深受自定义事件机制的影响,同样,自定义事件也是处理与DOM 产生交互的代码逻辑片段之间耦合的很好的架构方法。
当你想给你的应用添加一个功能片段时,或许经常纠结于是否应当将这个片段抽离为一个插件。自定义事件的思路可以帮你做这种解耦,并逐渐形成一个可复用的库。
比如,我们来看一个简单的jQuery插件——选项卡。我们让ul 列表来响应点击事件。当用户点击一个列表项,给这个列表项添加一个名为active 的类,同时将其他列表项中的active 类移除:
另外,id 为tabsContent 的div 用来存放每个选项卡对应的实际内容。根据当前激活的选项卡,来对应地给div 的子节点添加或删除active 类。实际的显示和隐藏选项卡和内容都由CSS 来控制,我们的插件仅仅处理active 类:
jQuery.fn.tabs = function(control){
var element = $(this);
control = $(control);
element.find("li").bind("click", function(){
// 从列表项中添加或删除 active 类
element.find("li").removeClass("active");
$(this).addClass("active");
// 给tabContent 添加或删除active 类
var tabName = $(this).attr("data-tab");
control.find(">[data-tab]").removeClass("active");
control.find(">[data-tab='" + tabName + "']").addClass("active");
});
// 激活第1 个选项卡
element.find("li:first").addClass("active");
// 返回this 以启用链式调用
return this;
};
插件位于jQuery 的prototype 里,因此可以基于jQuery实例来调用:
$("ul#tabs").tabs("#tabContent");
现在看上去插件有什么问题吗?没错,我们给所有的列表项都添加了click 事件回调,这是第1 个错误。我们可以使用上文提到的delegate() 来优化代码。同样,点击事件回调的实现很臃肿,很难一眼看出发生了什么。除此之外,如果另一个开发者想要扩展这个插件,他很可能会将其重写。
我们来看下如何使用自定义事件让代码变得更整洁。在点击选项卡时触发一个change.tabs 事件,并绑定若干回调方法来适当修改active 类:
jQuery.fn.tabs = function (control) {
var element = $(this);
control = $(control);
element.delegate("li", "click", function () {
// 遍历选项卡名称
var tabName = $(this).attr("data-tab");
// 在点击选项卡时触发自定义事件
element.trigger("change.tabs", tabName);
});
// 绑定到自定义事件
element.bind("change.tabs", function (e, tabName) {
element.find("li").removeClass("active");
element.find(">[data-tab='" + tabName +"']").addClass("active");
});
element.bind("change.tabs", function (e, tabName) {
control.find(">[data-tab]").removeClass("active");
control.find(">[data-tab='" + tabName + "']").addClass("active");
});
// 激活第1 个选项卡
var firstName =element.find("li:first").attr("data-tab");
element.trigger("change.tabs", firstName);
return this;
};
我们看到使用自定义事件回调可以让代码更加整洁。这也意味着选项卡状态切换回调彼此分离,这也让插件代码更具扩展性。比如我们可以在程序中直接更改选项卡的状态,只需触发被观察列表的change.tabs 事件即可:
$("#tabs").trigger("change.tabs","users");
同样,我们可以将切换选项卡的动作和窗口的hash 做关联,这样就可以使用浏览器的后退按钮了:
$("#tabs").bind("change.tabs", function(e,tabName){
window.location.hash = tabName;
});
$(window).bind("hashchange", function(){
var tabName = window.location.hash.slice(1);
$("#tabs").trigger("change.tabs", tabName);
});
自定义事件的运用实际上给其他开发者很大的空间来扩展我们的工作成果。
本文节选自《基于MVC的JavaScript Web富应用开发》一书
(美)麦卡劳(MacCaw,A.)著
李晶,张散集译
本书教你如何构建先进的富应用程序,书中给出的很多优秀的工具和最佳实践都是很多程序员和工程师在工作中亟需的。