8

I have a backbone view that looks like this:

var myView = Backbone.view.extend({
    events: {'click .myClass': 'myFunction'},
    initialze: //initialize function,
    render: //render function,
    myFunction: function (e) {
        //do something
    }
});

I want to make myFunction work only one time, then stop being called. I believe I could use the backbone once() method to achieve this but can't figure it out. Is this the best way and how do I structure it? Thanks!

mu is too short
  • 426,620
  • 70
  • 833
  • 800
YPCrumble
  • 26,610
  • 23
  • 107
  • 172

4 Answers4

13

Simply replace your current definition:

myFunction: function(){....}

by

myFunction: _.once(function(){....})

myFunction may be called several times, but it will be a noop after the first call.

Greg
  • 3,370
  • 3
  • 18
  • 20
  • This worked perfectly and seemed the simplest solution, thanks! – YPCrumble Feb 19 '14 at 05:40
  • What happens here if you have two instances of the view? Either at the same time or at different times while your application is running. – mu is too short Dec 01 '16 at 17:49
  • @muistooshort here it would be called only once _per instance_ . If you wanted to call it only once overall, you'd use a function defined at the same level than `myView` for instance. – Greg Dec 05 '16 at 12:43
  • @muistooshort my apologies, the object passed into View.extend is used as the prototype, and is common to all instances. With my code above it'll be called once overall. To call it once per instance, you could make use of the initialize method as shown here http://jsfiddle.net/u6oshjwn/1/ – Greg Dec 06 '16 at 10:24
  • 1
    But in the case of an event handler, the handlers will be bound *before* `initialize` gets called (http://jsfiddle.net/ambiguous/zqhaj97w/) so `f` won't be there or it won't be the right `f`, you'd need to throw a manual `delegateEvents` call into the mix (http://jsfiddle.net/ambiguous/jdhejxgz/) to get everything hooked up properly. Sorry to belabour this point but there are some subtle traps. – mu is too short Dec 06 '16 at 18:44
  • No worries ! Indeed, event handlers make it tricky with _.once when you want the function called once per instance. In that case your solution is an elegant one. I played a bit and worked out yet other solutions for that case http://jsfiddle.net/zqhaj97w/1/ (I quite like the one on `f`, not so much the one on `g`). – Greg Dec 07 '16 at 11:15
6

View events are bound using delegateEvents and that uses the delegation form of jQuery's on. That means that the event to handler mapping is dynamic and can be manipulated by manipulating the DOM. For example, you could do this:

Backbone.View.extend({
    events: {
        'click .myClass.onlyOnce': 'doThings'
    },
    doThings: function(e) {
        $(e.currentTarget).removeClass('onlyOnce');
        //...
    },
    //...
});

and make sure that the .myClass element also has an onlyOnce class. Then, the first time you click on it, it will match .myClass.onlyOnce and the click will get routed to doThings; doThings then proceeds to remove the onlyOnce class and do whatever needs to be done. Subsequent clicks on .myClass will be clicks on something that does not match .myClass.onlyOnce so doThings won't be called again.

This approach has the nice advantage of self documenting its behavior in events.

Demo: http://jsfiddle.net/ambiguous/7KJgT/

mu is too short
  • 426,620
  • 70
  • 833
  • 800
1

You could add an attribute to the element the first time, and then check if the element has the attribute:

var myView = Backbone.view.extend({
    events: {'click .myClass': 'myFunction'},
    initialze: //initialize function,
    render: //render function,
    myFunction: function (e) {
        if(e.target.getAttribute("data-fired")) return;
        e.target.setAttribute("data-fired", true);
        // Do your stuff
    }
});
S. A.
  • 3,714
  • 2
  • 20
  • 31
  • Aristizabal: data attribute has a partial support for IE, so we can use class in order to validate condition. – Mohit Pandey Feb 19 '14 at 04:57
  • @Mohit: Can you be more specific about this "partial support" in IE? – mu is too short Feb 19 '14 at 04:58
  • @muistooshort: http://caniuse.com/dataset is the reason of what i am saying. You may correct me, if i am wrong. – Mohit Pandey Feb 19 '14 at 05:21
  • 1
    @Mohit: That link does say that "All browsers can already use data-* attributes and access them using getAttribute" so this use is fine. caniuse also neglects to specify what specifically "partial support" means. – mu is too short Feb 19 '14 at 05:35
0

You could add a class to the element to validate your condition.

var myView = Backbone.view.extend({
    events: {
        'click .myClass': 'myFunction'
    },
    initialze: //initialize function,
    render: //render function,
    myFunction: function (e) {
        if (e.target.className.search(/triggered/) !== -1) return; // event already triggered once
        e.target.className += "triggered";
        //do something when its triggered first time
    }
});
Mohit Pandey
  • 3,679
  • 7
  • 26
  • 38