28

I was wondering if it is possible to bind multiple event types in backbone within a single line.

Consider the following:

var MyView = Backbone.View.extend({
    id: 'foo',
    events: {
        'click .bar': 'doSomething',
        'touchstart .bar': 'doSomething'
    },
    doSomething: function(e) {
        console.log(e.type);
    }
});

Basically what I am wondering is if it is possible to combine the event binding for 'click' and 'touchstart' into one line - along the lines of:

events: { 'click,touchstart .bar': 'doSomething' }

Any suggestions would be appreciated.

stephenmuss
  • 2,445
  • 2
  • 20
  • 29
  • did you check the **Trigger** section here http://documentcloud.github.com/backbone/#Events. But in your case do the binding in Views initialize method. – Deeptechtons May 30 '12 at 08:29
  • I already have a decent amount of code in most of my views' initialize method, so I'd prefer to avoid binding the events there as a matter of preference. – stephenmuss May 30 '12 at 08:55
  • For those looking for the right Trigger link: http://backbonejs.org/#Events-trigger – user823447 Sep 18 '18 at 18:01

3 Answers3

18

It's impossible for views jQuery events, which are bound through delegateEvents. It's is possible for backbone events, though:

book.on("change:title change:author", ...);
OlliM
  • 7,023
  • 1
  • 36
  • 47
rinat.io
  • 3,168
  • 2
  • 21
  • 25
  • 1
    Yes, I am aware of being able to do this with models. But is it truly not possible in the events declaration of a backbone view? I'd also prefer to not do the binding to the view within the view's initialize method. I'm curious to see what others have to say whether it's possible or not. But if as you say it isn't I'll try to do some extending of Backbone.View – stephenmuss May 30 '12 at 08:51
  • You can check how [`delegateEVents`](http://documentcloud.github.com/backbone/docs/backbone.html) works in annotated source code. You can also define `events` property of view as a function that returns an event hash. – rinat.io May 30 '12 at 10:41
  • Yes, no possible, the actual [delegateEvents implementation](https://github.com/documentcloud/backbone/blob/master/backbone.js#L1230-1247) is based in only one kind of _key_ structure. – fguillen May 30 '12 at 15:34
  • Just out of curiosity, is there a reason you can't just list them individually in your events: {} ? Or are you just trying to minimize your code as much as possible? – jmk2142 May 31 '12 at 04:42
15

For anyone interested I ended up overriding delegateEvents in Backbone.View.

There are only a few modified lines to get the desired functionality.

You can see a diff in my commit on github

Here is delegateEvents in its modified state:

delegateEvents: function(events) {
    if (!(events || (events = getValue(this, 'events')))) return;
    this.undelegateEvents();
    for (var key in events) {
        var method = events[key];
        if (!_.isFunction(method)) method = this[events[key]];
        if (!method) throw new Error('Method "' + events[key] + '" does not exist');
        var match = key.match(delegateEventSplitter);
        var eventTypes = match[1].split(','), selector = match[2];
        method = _.bind(method, this);
        var self = this;
        _(eventTypes).each(function(eventName) {
            eventName += '.delegateEvents' + self.cid;
            if (selector === '') {
              self.$el.bind(eventName, method);
            } else {
                self.$el.delegate(selector, eventName, method);
            }
        });
    }
}
stephenmuss
  • 2,445
  • 2
  • 20
  • 29
1

We could also do it as below. With the advantage to manage space between each event.

Github commit here

Add it in Backbone directly :

delegateEvents: function(events) {
  events || (events = _.result(this, 'events'));
  if (!events) return this;
  this.undelegateEvents();
  for (var key in events) {
    var method = events[key];
    if (!_.isFunction(method)) method = this[method];
    if (!method) continue;
    var match = key.match(delegateEventSplitter);
    this.delegate(match[1], match[2], _.bind(method, this));
  }
  return this;
}

Override delegateEvents method :

Backbone.View.prototype.originalDelegateEvents = Backbone.View.prototype.delegateEvents;
Backbone.View.prototype.delegateEvents = function(events) {
    events || (events = _.result(this, 'events'));
    if (!events) return this;
    this.undelegateEvents();
    for (var key in events) {
        var method = events[key], combinedEvents = key.split(',');
        if (!_.isFunction(method)) method = this[method];
        if (!method) continue;

        for(var i = 0, match = null; i < combinedEvents.length; ++i) {
            match = combinedEvents[i].trim().match(/^(\S+)\s*(.*)$/);
            this.delegate(match[1], match[2], _.bind(method, this));
        }
    }
    return this;
};

We could now manage events in one line :

events: { 
    'click a[data-anchor], wheel, keydown': 'scroll'
}
ElJackiste
  • 4,011
  • 5
  • 23
  • 36