jQuery中不绑定DOM元素的自定义事件?

50

我想知道是否可以在jQuery中创建一个不绑定到DOM元素的自定义事件。

我非常喜欢jQuery而不是YUI,但是在YUI中有一件事情我很喜欢,那就是创建自定义事件并且稍后能够订阅它们。

例如,下面的代码创建了一个与变量绑定而不是DOM元素绑定的事件:

var myevent = new YAHOO.util.CustomEvent("mycustomevent");

我一直在阅读关于jQuery的例子和文档,它们都需要类似以下的代码:

$('body').bind('mycustomevent', function(){
    //do stuff
});

1
我猜 jQuery 完全不理解 'mycustomevent' 和 YUI 事件。不过你可以尝试使用指针:bind(myevent,.. - jantimon
我认为,类似于Django,JQuery也有一些最佳实践。首先是插件。您可以将自定义事件创建为外部类,作为JQuery的插件,并且可以使用任何选择器(例如$("#myDiv"))将您的事件简单地绑定到元素上,如$("#myDiv").myPlugin("does lots of stuff")。 有几种方法可以做到这一点,但您应该首先了解您需要什么。您能详细说明一下吗?我可以尝试给您更精确的评论。谢谢。 - 0100110010101
你好!他们接受的答案不正确(或者不再正确)。你能否将被接受的答案改为我的答案? - Stas Bichenko
7个回答

43
你可以在jQuery中像这样触发自定义全局事件:
jQuery.event.trigger('mycustomevent', [arg1, arg2, arg3]);

这些将触发任何元素。

由于jQuery是围绕DOM对象构建的,因此您必须将事件绑定到DOM对象。您可能会找到一些方法来绑定事件而不使用元素(您确实做了),但这不是受支持的方法。

正如您在自己的答案中所写的那样,如果您不想将事件绑定到单个页面元素,则可以将其绑定到全局DOM对象上:

$(document).bind('mycustomevent', function (e, arg1, arg2, arg3) { /* ... */ });

7
看起来 jQuery 现在支持这个功能了:http://bugs.jquery.com/ticket/7818#comment:26,http://forum.jquery.com/topic/triggering-custom-events-on-js-objects。链接中提到的 triggerHandler 解决方法已经在问题 7930 中得到修复。 - hyperslug
3
是的,现在jQuery确实支持这个功能(请参见我的回答:https://dev59.com/KXI_5IYBdhLWcg3wDOdC#26062543)。 - Stas Bichenko

41

根据John Resig的评论,通过jQuery将事件绑定到自定义对象是被支持的:

没有什么特别的原因不去记录它(除了对于大多数用户来说它非常不传统),但是是的,我们支持它并且有单元测试。

所以这个可以工作:

var a = {foo: 'bar'};
$(a).on('baz', function() {console.log('hello')});
$(a).triggerHandler('baz');
>>> 'hello'

大多数用户需要使用triggerHandler()而不是trigger()。如果使用.trigger("eventName"),它将查找对象上的“eventName”属性,并尝试在执行任何附加的jQuery处理程序之后执行它()。

编辑(28.02.2017):

在一个大型代码库中使用此模式约2年后,我可以证明这是一个坏主意。这些自定义事件导致了难以理解的数据流。最好使用回调函数。


1
愚蠢的问题:它没有文档的原因吗?难道不应该使用它吗?在新版本中可以删除/修改吗?我在想是否应该使用它来代替其他子发布库,如EvenEmitter - robsch

6

针对未来的读者,这是相当简单的。在发布问题后,我进行了进一步的研究。不幸的是,其他评论者都不正确。

为了绑定自定义事件:

$().bind('mycustomevent', function(){
    //code here
});

此外,您可以将数据构建到事件对象中,以后可以访问该数据:
$({mydata:'testdata'}).bind('mycustomevent',function(){
    //code here
});

18
你应该知道,使用$()仍然会将其绑定到DOM对象($()等同于执行$(document))。$({})是可行的,但你应该知道它之所以有效,是因为jQuery从不检查是否为DOM对象。它仍然将其包装为一个拥有方法(例如append(...))的jQuery对象,如果调用这些方法会停止你的脚本。而且,对非DOM对象进行包装的行为是未定义的,并且可能会在未来发生变化。因此,我仍然坚持我的观点,并建议您将其绑定到$(document)而不是JavaScript对象。 - Blixt

4

在jQuery 1.4.4中,绑定非DOM元素的功能已被移除。


也许他的意思是普通的JavaScript对象? - Con Antonakos

3

即使您不知道要将事件附加到哪个元素,仍然可以使用jQuery任意触发和响应事件。只需在文档中创建一个“接收器”元素,您就可以将所有自定义事件绑定到该元素上。这样,您可以在一个地方管理所有非特定于元素的事件处理逻辑。

$(document).ready(function() {

  $(body).append('<div id="receiver">');

  $("#receiver").bind("foo_event", function () {
    // decide what to do now that foo_event has been triggered.
  });

  $("#some_element").click(function() {
    $("#receiver").trigger("foo_event");
  });

});

我认为那是个不好的主意。如果你真的想要一个元素来附加事件,为什么不使用 $('body') 或者 $(document) 呢? - Blixt
5
这只是一个可行的惯例。我更喜欢它而不是使用document或body,因为它像一个无菌的事件清单一样运作。我知道里面的所有内容,减少了意外发生不良事件的可能性。你能解释一下为什么你认为这是个坏主意吗? - Matt Howell

3

jQuery Callbacks提供了一种简单的实现发布-订阅系统而不依赖于DOM的方法。

在链接页面的底部有示例代码,展示了如何实现该功能。


1

[编辑]

以下是使用下面概念的工作类:

https://github.com/stratboy/image-preloader

这只是一个顺序图像预加载器。您可以订阅一系列事件。看一下。
[/编辑]
旧帖:
这是另一个裸骨示例,具有像Himanshu P.建议的回调函数。
我正在构建一个类,并来自Mootools,在那里,类和自定义事件直接实现在类中(因此不绑定到DOM元素)是绝对自然的。因此,我尝试了一种解决方法并在下面分享。
  var step_slider;

  //sandbox
  ;(function($) {

//constructor
var StepSlider = function(slider_mask_selector,options) {

    //this.onStep = $.Event('step');
    this.options = null;
    this.onstep = $.Callbacks();

    this.init();
}//end constructor

StepSlider.prototype = {

    init:function(){
        this.set_events();
    },//end init

    set_events:function(){

        if(this.options){
            if(this.options.onstep){
                this.subscribe('step',options.onstep);
            }
        }

    },//set_events

    subscribe:function(event,action){
        this['on'+event].add(action);
    },

    fire_onstep:function(){
        this.onstep.fire({ data1:'ok' });
    }

}//end prototype/class

//--------------------

$(document).ready(function() {

    step_slider = new StepSlider('selector');

    //for example, say that you have a div#next-button, you can then do this:
    $('#next-button').click(function(){
        step_slider.fire_onstep();
    });

});//end domready


})(jQuery);

那不是一个类。 - low_rents
@low_rents:也许不适用于ES6,但只要你可以从它实例化对象(new ClassName),它就是一个类。或者说,这是一种(旧的)JavaScript方式来做类似类的事情,记住JavaScript没有像ActionScript 3和ES6那样真正的oop/inheritance语法/糖果。在ES6之前,可能最好的js oop实现之一是coffescript创建js类的方式(只需创建一个并查看编译后的代码),但上面的方法是更简单的实现,并且是获得自包含、组织良好的对象以无限次实例化的一个很好的折衷方案。 - Luca Reghellin

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接