简体   繁体   中英

overriding Backbone.View delegateEvents so the events object can include mobile events

I am using Hammer.js to capture touchscreen events in a Backbone View. Hammer has a special syntax for adding touch listeners, which I have been using in the View's initialize function:

$("#next-button").hammer({prevent_default: true}).on('tap', $.proxy(this.next, this));

I would rather add it to the standard events object, like this:

events: {"tap #next-button":"next"}

So I hacked the end of Backbone.View's delegateEvents method:

var isMobileEvent=["tap", "doubleTap"].indexOf(eventName)!=-1;
        if (selector === '') {
            if (isMobileEvent){
                this.$el.hammer({prevent_default: true}).on(eventName, method);
            } else {
                this.$el.on(eventName, method);
            }
        } else {
            if (isMobileEvent){
                this.$el.hammer({prevent_default: true}).on(eventName, selector, method);
            } else {
                this.$el.on(eventName, selector, method);
            }

        }

This works fine. But when I tried to override the method in the one View where it was needed the override (worried that I would forget about this hack in Backbone, or overwrite it with a new version of Backbone, etc.), delegateEvents for my View stopped working. The problem is that the method callback is not recognized as a method:

if (!_.isFunction(method)) method = this[events[key]];

Why? I literally copy and pasted the function into the View subclass. Here is the whole delegateEvents that works in Backbone, but not in my Backbone.View:

delegateEvents: function(events) {
      if (!(events || (events = _.result(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 eventName = match[1], selector = match[2];
        method = _.bind(method, this);
        eventName += '.delegateEvents' + this.cid;
        var isMobileEvent=["tap", "doubleTap"].indexOf(eventName)!=-1;
        if (selector === '') {
            if (isMobileEvent){
                this.$el.hammer({prevent_default: true}).on(eventName, method);
            } else {
                this.$el.on(eventName, method);
            }
        } else {
            if (isMobileEvent){
                this.$el.hammer({prevent_default: true}).on(eventName, selector, method);
            } else {
                this.$el.on(eventName, selector, method);
            }

        }
      }

THE FIX:

Replace this line:

var match = key.match(delegateEventSplitter);

with this one:

var match = key.match(/^(\S+)\s*(.*)$/);

This jsbin shows how it is possible to override the default Backbone View logic for delegateEvents .

You can easily extend it as needed to handle Hammer.JS as you had above to extend Backbone to support the various touch events.

I needed to copy the value of delegateEventSplitter locally as it's privately declared (within a closure) inside of the Backbone library. Only functions that were executed/returned within the context of that variable are able to access that value. Since your new class does not execute in that context, it's unable to directly access the value.

The relevant code:

var SampleView = Backbone.View.extend({
  events: {
    "click" : '_clicked' 
  },

delegateEvents: function(events) {
      if (!(events || (events = _.result(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(/^(\S+)\s*(.*)$/);
        var eventName = match[1], selector = match[2];
        method = _.bind(method, this);
        eventName += '.delegateEvents' + this.cid;
        if (selector === '') {
           this.$el.on(eventName, method);
        } else {
           this.$el.on(eventName, selector, method);
        }
      }  
},

  render: function() {        
     this.$el.html("hi");
     return this; 
  },        
  _clicked: function() {
     alert("clicked!"); 
  }
});

// assumes there's an element with an id of "content"
$(function() {
  var view = new SampleView();
  $("#content").append(view.render().$el);  
});

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM