假设您正在使用 jQuery 开发一个 Web 应用程序,并且需要编写一个适合 Web 应用程序需求的手风琴。 您决定将其做成一个插件。 这样,应用程序中任何需要手风琴的地方都可以加载此插件并将其调用到准备变为手风琴的语义标记上。 然后,您将得到一个漂亮的手风琴。
现在,另一个开发人员正在使用您的手风琴,他们希望能够将一些内容通过 AJAX 加载到手风琴的某个部分中,但前提是该部分已打开。 您共同决定代码应该分开。 手风琴工作方式的通用功能应该与特定于应用程序中一个页面上的一个手风琴的代码分开。
回调,传统方法
一种方法是在插件中编写“回调”。 另一位开发人员将指定一个函数的名称,当手风琴上发生某些操作时,她希望运行该函数。 因此,调用它可能类似于
$(".accordion").accordion({
panelOpen: myPanelOpenCallback,
panelClose: myPanelCloseCallback;
});
他们将自行创建这些回调函数
function myPanelOpenCallback() {
// Do Ajax stuff
}
然后插件本身将在其相关的内部函数中遵守这些回调
$.fn.accordion = function(options) {
return this.each(function(i, el) {
var base = el;
base.init = function() {
// Do initialization stuff
};
base.openPanel = function(panel) {
// Open panel
// Do callback
options.panelOpen.call();
};
base.closePanel = function(panel) {
// Open panel
// Do callback
options.panelClose.call();
};
base.init();
});
};
自定义事件,更好的方法
现在假设第三位开发人员也参与进来,他们也希望编写一些 JavaScript 代码来响应面板打开。 为了便于用例,假设手风琴面板中的内容是设置,这位开发人员希望在打开或关闭键时自动保存这些设置。
现在我们处于一个有趣的境地。 我们已经为手风琴面板的打开和关闭定义了回调。 因此,在回调模型中,这些开发人员将不得不一起处理该回调并在该回调中运行他们的代码。 这并不算什么大问题,但现在我们被迫混合代码,而这可能并非理想。 请记住,我们一开始就是通过将特定功能分开来启动整个过程的。
这就是自定义事件非常棒的地方。 手风琴功能插件不会触发指定的回调,而是会触发自定义事件。 自定义事件就像其他任何事件(例如单击)一样,只是它们 **仅** 通过编程方式声明和调用。
首先,回调消失了,我们只需调用手风琴插件,无需回调
$(".accordion").accordion();
现在,在我们调用回调的地方,我们将触发我们的自定义事件。 基本上,只需想出一个有意义的名称(就像您为函数命名一样)。
// OUT
// options.panelOpen.call();
// IN
panel.trigger("panelOpen");
现在,其他开发人员可以将他们的代码绑定到此自定义事件并执行他们自己的操作。
$(".panel").on("panelOpen", function() {
// Developer 1: do Ajax stuff
});
// Meanwhile, in another part of town...
$(".panel").on("panelOpen", function() {
// Developer 2: do saving stuff
});
自由万岁! 功能分离万岁!
我认为自定义事件总的来说更好。 但是,我认为它们需要更多沟通。 您可能需要在某个地方写一些注释,解释哪些自定义事件被触发以及何时触发,并使它们易于发现。
更多信息
上面概述的场景非常简单。 以下两篇文章更深入地介绍了自定义事件,包括更复杂的示例和有关它们工作方式的更多信息。
- jQuery 中自定义事件的解密 由 Rebecca Murphey 撰写
- jQuery 特殊事件 由 Ben Alman 撰写
更棒的是,可以为您的事件命名空间。
例如,“open”事件在“panel”下命名空间。
Chris,好文章。
自定义事件绝对棒。
一个小小的说明是,有时使用 .triggerHandler() 比仅使用 .trigger 更好,以停止事件冒泡到 DOM 树。
例如,如果您编写一个基于事件的表单验证器,可以创建一个名为“validate”的自定义事件并将其附加到表单元素以及表单中的所有字段。
如果您现在在字段上调用 .trigger('validate'),那么表单元素也会被触发。 如果您在字段上使用 .triggerHandler('validate'),则只会触发该字段,不会触发包含它的表单。
还值得注意的是,.trigger 和 .triggerHandler 都接受一个额外的选项数组,有点像 .call() 函数可以接受的参数。
总之,很高兴看到人们在回调函数上使用自定义事件!
我喜欢这两种方法。 在创建时提供回调会在幕后使用自定义事件。 这部分解决了对额外文档的需求。
我认为 qTip2 在自定义事件方面有一个很好的实现,如果您需要查看现实世界的示例,请参考它。 尤其是事件对象的用法。 使用它提供了更大的灵活性。 例如
有关 jQuery 事件对象的文档,请查看 https://api.jqueryjs.cn/category/events/event-object/
我经常使用自定义事件,我已经写了一篇关于它的文章: http://blog.imaginea.com/implementing-event-delegation-and-custom-events-in-javascript-using-jquery-prototypejs/
这些让我想起了 eventListeners,区别在于您无法知道谁在监听什么。 我觉得有点奇怪。
假设,两个按钮在按下时都广播一个“press”事件。
一个按钮需要更改主体背景颜色,而另一个按钮需要更改 div 的高度。
如果您无法指定主体需要仅监听一个按钮,那么无论按下哪个按钮,它都会做出反应。 当然,div 也是如此。
jQuery 非常有用的 JS 脚本库。 使用它帮助我在开发中获得了很多帮助。
实际上,我不同意。 这种方法鼓励 API 虚报其依赖关系。
我的意思是什么? 开发人员访问使用您的
.accordion
插件的网站。 他看到的是这个在不查看该插件的源代码或某种文档的情况下,开发人员永远无法知道该插件实际上是否触发了所述事件。 但是,使用回调方法
在这里,插件明确声明了它为了运行而需要的条件。 API 是 **真实的**,它不会撒谎说“我没有可以随我一起使用的函数,我只是一个手风琴”,它现在说“我是一个手风琴, *我支持一个
whenOpened
和whenClosed
回调函数,当这些事件发生时会被调用*”我同意这一点。 Chris,我很想听听你的想法——你认为自定义事件方法会创建更多意大利面条式的代码吗? 我的意思是——在您自己的文件中(而不是在插件中),是否有许多看似随机的自定义事件监听器(使用 .on()),它们没有以与插件相关的结构组织(回调方法)? 我完全能理解您在文章中提到的优势(感谢您的信息,顺便说一句——太棒了),但我认为,如果这种方法在整个项目中使用很多,代码将变得难以管理。
谢谢!
虽然我有点同意你的观点,但你提到了 API 和文档,我会期望一个不错的插件在文档中列出它的 API,因此我认为这不是一个大问题。
我想我们可以将这两种方法结合起来。 Jquery 插件有使用回调的传统,因此我倾向于保持这种方式。
插件初始化后的事件调用可能会被忽略,而选项中的回调则不会。
一个快速实现: http://jsbin.com/usaboz/2
我喜欢这样
谢谢 Chris ::D
这是一篇很棒的文章,自定义事件确实给了我们开发者更多自由。感谢分享这些信息,我会转发给几个朋友,我知道他们也会喜欢阅读这篇文章。
关于你的评论表单,我想给Chris发个简短的信息。虽然我喜欢这个评论表单的外观和布局,但我不太喜欢你用来解释每个文本框用途的图标。我不得不思考每个文本框应该输入什么内容……我勉强认出了姓名和电子邮件图标,但第三个网站URL图标让我很费解。我知道你非常重视博客的可访问性,所以我觉得应该向你指出这个问题,因为我相信其他人也遇到过同样的问题。即使你只是为图标添加一个简单的悬停提示,也能解决问题。就我个人而言,我会将第三个URL图标改为链/链接图标,因为每个人都能理解这个图标。
希望你不介意我在这里发布这些内容,我知道这与这篇文章本身无关,所以在你阅读完之后,我不介意你删除它。另外,以防你确实在每个文本框上都有悬停提示,而实际上是我的浏览器出了问题,我使用的Firefox版本是3.6。
如果你使用的是现代浏览器,你会在文本框中看到预览文本。如果你有视力障碍,你会听到与字段相关的描述性文本。我认为你该升级了。
如果你使用的是现代浏览器,你会在文本框中看到预览文本。如果你有视力障碍,你会听到与字段相关的描述性文本。我认为你该升级了。
—–
^ 这对一个有效的用户界面抱怨来说是粗鲁和刻薄的回答,尤其是因为Firefox 3.6是一个有效的浏览器,并不是每个人都能够“升级”。
当字段获得焦点时,文本会消失,所以除非你记住了之前那里的文本,否则你将不知道在那里输入什么,而“图标”毫无用处。
这不算什么大事,因为该字段不是必需的,但为什么要费这个劲呢?
jQuery UI的widget工厂支持在widget中触发事件。它还允许在初始化对象中绑定事件。
这些太棒了!我现在正在玩自定义事件。
自定义事件和模块化、可扩展的代码都是非常棒的东西……我只是希望人们能够意识到,他们现在才开始意识到一些在MooTools中存在了6年以上的功能和原理——this.fireEvent('customEventName')。如果你想要更灵活、更可扩展的代码,拥有文章中提到的所有功能(甚至更多),我建议你尝试一下MooTools,它在很多方面都超越了jQuery有限的DOM中心范围(我最近用它构建了一个完整的node.js框架,因为它在服务器端也很棒!)。
只想说:祝大家圣诞快乐,新年快乐。
什么?
Chris!我喜欢你的方法——当你将鼠标悬停在标题上时,阅读更多链接也会被悬停。你能告诉我它是如何实现的吗?提前感谢。
我有点糊涂。只有当组件被编程为触发自定义事件时,它才会起作用,对吧?
谢谢。