Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Jquery Observer pattern

I've been looking over the internet for examples of implementing the observer pattern in jquery.

I would like to have it like this

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

Define some custom event callbacks for the observers

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

The following line would then trigger the customEvent callbacks of both observers

subject.trigger('customEvent', contextData);

while the following would fire anotherCustomEvent on observer1 only, since observer2 doesn't have that custom event binded

subject.trigger('anotherCustomEvent', contextData);

Guides on the internet is more general:

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

$( document ).trigger( "topicName" );

(Example from http://addyosmani.com/resources/essentialjsdesignpatterns/book/#observerpatternjquery) I can't see how the above code can be used to accomplish what I'm looking for.

Either i would have to make it like this (if I keep it like the above example):

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

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

or a little bit better:

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

Either way I'm stuck with having to edit the subject-click-callback or document-customEvent-callback instead of telling the observer to subscribe to the subject.

Have I misunderstood observer pattern or is there a way to achieve what I'm looking for?

http://addyosmani.com/resources/essentialjsdesignpatterns/book/#observerpatternjavascript mention Publish/Subscribe Pattern a bit further down in that chapter. That could be a way for me, but I'm missing the code behind the example.

like image 611
Anders Avatar asked Sep 25 '12 19:09

Anders


1 Answers

From your comment:

I just can't see how this is achieved when you have to tell the subject which elements to trigger by selectors instead of the subject having a list that the observer can register to

Please correct me if I'm wrong, but you seem to be misunderstanding how the pattern was implemented in jQuery. You don't "tell the subject which elements to trigger", and the subject doesn't have "a list that the observer can register to" either. It works like this:

  • The subject/publisher emits/triggers certain events (on certain circumstances you define).
  • The observer/subscriber listens to certain events. It keeps a list of the events it subscribed to.
  • That's all based on DOM events, so it's limited by the DOM Event Model.

For example, consider the following HTML:

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

Let's make the inner divs trigger a custom event called 'custom'. You define when that should happen, on this example this will happen when they are clicked:

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

Now let's make the document element subscribe to that custom event:

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

When the custom event is triggered by one of the divs, the observer/subscriber is notified and a message is logged to the console.

The example uses document as the observer for a reason: events bubble up the DOM tree, and can only be caught by elements that are ancestors of the one that triggered it. Since document is the root of the DOM tree, it can see all events. If #div1 was our observer, it would only see events triggered by #div1 itself, but not the ones triggered by #div2.

Maybe that limitation is what confused you?


There are ways to circumvent that limitation, but usually, if you want to do something to #div1 based upon an event triggered by #div2, you just do it from the callback you have setup on the document element (or the closest common ancestor to both divs). Anyway, it seems you really want an alternative, so here is one in the form of a jQuery plugin:

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

You can use it like this:

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

Live example: http://jsfiddle.net/JwJsP/1

like image 126
bfavaretto Avatar answered Oct 23 '22 04:10

bfavaretto