Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Change 'this' when firing custom events in jQuery using $.trigger()

I'm playing around with implementing an event pool pattern in my javascript, similar to what is described here: http://www.michaelhamrah.com/blog/2008/12/event-pooling-with-jquery-using-bind-and-trigger-managing-complex-javascript/

I need to be able to set the context of the event's closure(s) when they are triggered, rather than when they are bound. Is there some method of combining $.trigger and $.proxy to do this?

like image 462
jhogendorn Avatar asked Sep 19 '11 13:09

jhogendorn


2 Answers

I'm just going to put this here for anyone else googling, but its not the actual answer to the whole binding at trigger time part.

This is a workaround solution that wraps the binding function to set its context according to an argument supplied at trigger time. It's no good if you want to pass args at trigger time though.

EventPool = (function() {
  function EventPool() {}
  EventPool.Subscribe = function(event, fn) {
    return $(this).bind(event, $.proxy(function(event, context) {
      context = context != null ? context : this;
      return $.proxy(fn, context)(event);
    }, this));
  };
  EventPool.Publish = function(event, context) {
    return $(this).trigger(event, context);
  };
  return EventPool;
})();
like image 149
jhogendorn Avatar answered Oct 16 '22 20:10

jhogendorn


First off: Thank you for your answer to your own question. It brought me to my own solution which I thought I'd share with you and every other person searching for it.

It is a modification of yours that still permits passing additional arguments to your EventPool.Publish method. Note that my case looks a lot different from yours. Also note that jQuery.proxy does almost the same as Function.bind. (Btw, I do realize that this answer is already 2 years old.)

The demonstration code can be found here.

Snippets and explanations

function subscribe( ) {
    var args  = argsToArray(arguments), // Helper function to convert `arguments` object into array. Otherwise `Function.apply` won't accept it.
        fn    = args[args.length - 1], // The event handler is always the last argument passed to `jQuery.on`.
        $this = $(this);

    args[args.length - 1] = router.bind(this, fn); // Change handler to our context changer.

    $this.on.apply($this, args); // Call jQuery().on() with modified arguments.
    return this;
}

Function.bind essentially does the same as jQuery.proxy. We'll use this to memorize which handler to call in the end.


The router function calls the actual event handler, changing its context.

function router( ) {
    var args    = argsToArray(arguments),
        fn      = args.shift(), // Remember, we prefixed `router` with the actual event handler.
        evt     = args[0],
        context = evt.context || this;

    // No need to reference the context twice.
    delete evt.context;

    fn.apply(context, args);
}

jQuery allows easily creating events using jQuery.Event (function). We're retrieving a custom property called context here to use as the actual context of the real handler. Fallback to the old this context if unset / evaluates to false.

Ultimately we're manually calling the actual handler with all arguments that were passed to jQuery().trigger.

For usage, check the linked fiddle.

like image 40
Kiruse Avatar answered Oct 16 '22 22:10

Kiruse