代码之家  ›  专栏  ›  技术社区  ›  Anders

Jquery观察者模式

  •  5
  • Anders  · 技术社区  · 12 年前

    我一直在互联网上寻找在jquery中实现观察者模式的例子。

    我想要这样

    observer1.observe(subject);
    observer2.observe(subject);
    

    为观察者定义一些自定义事件回调

    observer1.bind('customEvent', function(contextData) {
      //Some code
    });
    observer1.bind('anotherCustomEvent', function(contextData) {
      //Some code  
    });
    observer2.bind('customEvent', function(contextData) {
      //Some code  
    });
    

    接下来的行将触发两个观察程序的customEvent回调

    subject.trigger('customEvent', contextData);
    

    而下面的操作只会在观察者1上触发另一个CustomEvent,因为观察者2没有绑定该自定义事件

    subject.trigger('anotherCustomEvent', contextData);
    

    互联网上的指南更为普遍:

    $( document ).on( "topicName" , function () {
      //..perform some behaviour
    });
    
    $( document ).trigger( "topicName" );
    

    (示例来自 http://addyosmani.com/resources/essentialjsdesignpatterns/book/#observerpatternjquery ) 我看不出如何使用上面的代码来完成我想要的任务。

    要么我必须这样做(如果我像上面的例子一样保持):

    $(document).on("customEvent", function () {
      observer1.trigger("customEvent");
      observer2.trigger("customEvent");
    });
    
    $(subject).click(function() { 
      $(document).trigger("customEvent");
    });
    

    或者稍微好一点:

    $(subject).click(function() { 
      observer1.trigger("customEvent");
      observer2.trigger("customEvent");
    });
    

    无论哪种方式,我都不得不编辑主题点击回调或文档customEvent回调,而不是告诉观察者订阅主题。

    我是否误解了观察者模式,或者有没有办法实现我想要的?

    http://addyosmani.com/resources/essentialjsdesignpatterns/book/#observerpatternjavascript 在该章中进一步提到发布/订阅模式。这对我来说可能是一种方式,但我错过了示例背后的代码。

    3 回复  |  直到 12 年前
        1
  •  17
  •   bfavaretto    11 年前

    根据您的评论:

    当你必须告诉主题要由选择器触发哪些元素,而不是主题有一个观察者可以注册的列表时,我看不出这是如何实现的

    如果我错了,请纠正我的错误,但您似乎误解了该模式是如何在jQuery中实现的。你没有“告诉主题要触发哪些元素”,主题也没有“观察者可以注册的列表”。它是这样工作的:

    • 主题/发布者发出/触发某些事件(在您定义的特定情况下)。
    • 观察者/订阅者收听某些事件。它保留了订阅的事件列表。
    • 这一切都是基于DOM事件的,所以它受到DOM事件模型的限制。

    例如,考虑以下HTML:

    <div id="div1">div 1</div>
    <div id="div2">div 2</div>
    

    让我们让内部div触发一个名为 'custom' 。您定义了何时应该发生这种情况,在本例中,单击它们时会发生这种情况:

    $('div').on('click', function(e) {
        $(this).trigger('custom');
    });
    

    现在让我们制作 document 元素订阅该自定义事件:

    $(document).on('custom', function(e) {
        console.log('document is handling custom event triggered by ' + e.target.id);
    });
    

    当自定义事件由其中一个div触发时,会通知观察者/订阅者,并将消息记录到控制台。

    该示例使用 文件 作为观察者是有原因的:事件在DOM树中冒泡,并且只能被触发它的元素的祖先捕获 文件 是DOM树的根,它可以查看所有事件。如果 #div1 作为我们的观察者,它只会看到由 #div1型 本身,但不是由 #div2

    也许是这种限制让你感到困惑?


    有一些方法可以绕过这种限制,但通常情况下,如果你想做点什么 #div1型 基于由 #div2型 ,您只需在 文件 元素(或两个div最近的共同祖先)。无论如何,你似乎真的想要一个替代方案,所以这里有一个jQuery插件的形式:

    $.fn.observe = function(eventName, callback) {
        return this.each(function(){
            var el = this;
            $(document).on(eventName, function(){
                callback.apply(el, arguments);
            })
        });
    }
    

    您可以这样使用它:

    $('#div1').observe('custom', function(e) {
        // do something
    });
    

    现场示例: http://jsfiddle.net/JwJsP/1

        2
  •  0
  •   Felix    12 年前

    可能不是你想要的,但观察者可以订阅收听事件:

            $(document).on('customEvent', '.iObserver', function() {
                console.log('i am observer');
            });
    
            // add observer
            $('ul li#Observer').addClass('iObserver');
    
            //remove observer
            $('ul li#Observer').removeClass('iObserver');
    
        3
  •  -1
  •   sabotero    10 年前

    如果您仍在寻找一些使用jQuery事件引擎的实现,您可以尝试以下代码:

    //The implementation   
    (function($) {
    
        var o = $({});
    
        $.each({
            trigger: 'publish',
            on: 'subscribe',
            off: 'unsubscribe'
        }, function(key, val) {
            jQuery[val] = function() {
                o[key].apply(o, arguments);
            };
        });
    
    })(jQuery);
    
    
    // The way to use it is this:
    var somedata = [1, 2, 3, 5, 4, 7, 8];
    $.publish('myEvent', somedata); // with data
    $.publish('myEvent'); // without data
    
    $.subscribe('myEvent', function(event, data) {
        // handle the event
    });