4

I have a Javascript object literal:

var Toolbar = {

    init: function(toolbar) {
        this.Bar = $(toolbar); // scope is Toolbar object literal

        this.Bar.find('clearButton').click(function() {
            this.trigger('clear'); // doesn't work!
            this.Bar.trigger('clear'); // works!
    }
}

Toolbar.init($('div.toolbar'));
Toolbar.bind('clear', function() { ... }); // doesn't work!
Toolbar.Bar.bind('clear', function() { ... }); // works!

I'd like to be able to trigger the clear event on the Toolbar object literal rather than the toolbar DOM object referenced in the literal. Is this possible, and if so, how would I do it?

Daniel T.
  • 37,212
  • 36
  • 139
  • 206
  • Shouldn't something be passed to `init`? Anyway, I think the error messages should be fairly clear about "why it doesn't work". At the very least, *include them in the post*. –  Dec 03 '11 at 02:43
  • Corrected my question, I forgot to include the the jQuery object in the `init()` call. And when I say 'doesn't work', what I mean is that an object literal doesn't look like it can fire events (which kind of makes sense, since it's not a DOM object). – Daniel T. Dec 03 '11 at 02:52

3 Answers3

1

This should work:

var Toolbar = {

    init: function(toolbar) {
        this.Bar = $(toolbar); // scope is Toolbar object literal

        this.Bar.find('.clearButton').click($.proxy(function() {
            $(this).trigger('clear'); // should work now
            this.Bar.trigger('clear'); // still works
        }, this));
    }
};

Toolbar.init($('div.toolbar'));

$(Toolbar).bind('clear', function() { 
    console.log('Toolbar'); 
}); // should work now

Toolbar.Bar.bind('clear', function() { 
    console.log('Toolbar.Bar'); 
}); // still works
  1. You need to maintain the this reference in the click function. I used $.proxy; some folks use var self = this;

  2. Toolbar is not a jQuery object so it should be wrapped in $() to access jQuery's functions. Also wrap the this that refers to a Toolbar instance in the click function.

hyperslug
  • 3,473
  • 1
  • 28
  • 29
0

How about this? :

var Toolbar = {

    init: function(toolbar) {
        this.Bar = $(toolbar); // scope is Toolbar object literal

        this.trigger =
            function() { this.Bar.trigger.apply(this.Bar, arguments); };

        this.bind = function() { this.Bar.bind.apply(this.Bar, arguments); };

        this.Bar.find('clearButton').click(function() {
            this.trigger('clear');
    }
};

Toolbar.init();
Toolbar.bind('clear', function() { ... });

If you want, you can readily create a function to handle the wrapping; either of these, as you prefer:

function wrapperitize(wrapper, wrappee, method /*, more_methods...*/)
{
    wrapper[method] = function() { wrappee[method].apply(wrappee, arguments); };
    for(var i = 3; i < arguments.length; ++i)
        wrapperitize(wrapper, wrappee, arguments[i]);
}

function wrapperitize(wrapper, wrappeeProp, method /*, more_methods...*/)
{
    wrapper[method] = function()
    {
        wrapper[wrappeeProp][method].apply(wrapper[wrappeeProp], arguments);
    };
    for(var i = 3; i < arguments.length; ++i)
        wrapperitize(wrapper, wrappeeProp, arguments[i]);
}

where the former would be called as wrapperitize(this, this.Bar, 'trigger', 'bind') and the latter as wrapperitize(this, 'Bar', 'trigger', 'bind') (the difference being that the former will make this into a wrapper for whatever this.Bar currently is, whereas the latter will make this into a wrapper for whatever this.Bar might ever become in the future.

(Note, by the way, the tiny bit of recursion. This is to avoid problematic variable capture, due to the way closures work in JavaScript.)

ruakh
  • 175,680
  • 26
  • 273
  • 307
0

If you want to be able to call a method on the Toolbar object, you'll need to define it there. If you don't need to add very many of those methods this way, you can just define each of them in the Toolbar object itself:

var Toolbar = {
    clear: function() { this.Bar.trigger('clear'); }

    init: function(toolbar) {
        this.Bar = $(toolbar); // scope is Toolbar object literal
        this.Bar.find('clearButton').click(function() {
            this.clear(); 
    }
}

However, if you need to call very many of them this will quickly get ugly. If you really want to be able to call all of the methods available of the jquery object on the Toolbar object, you could try to loop through all of them with a for in loop and add them to the Toolbar object, but this would be tedious, annoying to read, inefficient, and potentially cause some bugs. I would just use $(toolbar) whenever you wanted to call a method on that object as it gets rid of all those disadvantages. :D

Gordon Gustafson
  • 40,133
  • 25
  • 115
  • 157