Backbone events firing twice
Asked Answered
S

1

12

Why must I create a whole close prototype just to have my events unbinded from my view? Shouldn't Backbone just build that in? Is there a way to detect when a view is being removed?

My backbone events fire twice after I navigate away and back to the view.

        events : {
            "click #userDropdownButton > a" : "toggleUserDropdownMenu",
            "click" : "hideUserDropdownMenuIfClickedOutside"
        },

        el : "body",

        initialize : function() {
            this.render();
        },

        // Shows/hides the user dropdown menu
        toggleUserDropdownMenu : function() {
            if(!this.$el.find('#userDropdownButton > ul').is(':visible')) {
                this.showUserDropdownMenu();
            } else {
                this.hideUserDropdownMenu();
            }
            return false;
        },
        showUserDropdownMenu : function() {
            this.$el.find('#userDropdownButton').addClass('hover');
            this.$el.find('#userDropdownButton > ul').show();
            this.$el.find('#userDropdownButton > a .arrow-down').removeClass('arrow-down').addClass('arrow-up');
        },
        hideUserDropdownMenuIfClickedOutside : function() {
            if(this.$el.find('#userDropdownButton > ul').is(':visible')) {
                this.hideUserDropdownMenu();
            }
        },
        hideUserDropdownMenu : function() {
            this.$el.find('#userDropdownButton').removeClass('hover');
            this.$el.find('#userDropdownButton > ul').hide();
            this.$el.find('#userDropdownButton > a .arrow-up').removeClass('arrow-up').addClass('arrow-down');
        },

The first time the view is rendered, the dropdown opens and closes properly, but on the second time the view is rendered, the dropdown interprets every click twice, so as soon as it opens, the second click closes it.

Stivers answered 11/6, 2012 at 21:24 Comment(4)
If we knew what you were doing then we could probably tell you what you're doing wrong. Maybe if there was some code to look at...Vashtivashtia
I'd call this a duplicate of https://mcmap.net/q/324390/-recreating-a-removed-view-in-backbone-js/479863 or at least the answer is pretty much the same.Vashtivashtia
It is, and the answer was a bit of help, but the real question is why this isn't already in Backbone - events are such a core feature that Backbone should have some GC that knows when a view is thrown away and automatically call undelegateEvents().Stivers
The most common pattern is to have each view wrapped in its own distinct element and then you v.remove() to get rid of it, that just calls this.$el.remove() by default and removing the element takes the delegated events with it. If you're binding a view to an existing element then you have to adjust remove accordingly, I don't see why that's a problem.Vashtivashtia
L
15

Update 2013/05/01: Backbone 0.9.9+ has added some built-in functionality to facilitate easy dealing with teh zomg problem (see View.remove and StopListening); but you'll still need to kill your zombies by calling one of these.


Why must I create a whole close prototype just to have my events unbinded from my view?

Derick's article is awesome at covering this whole issue. But I can add my two bits, addressing your question of "why" its not built in.

Because of the way that Backbone event delegation works, views won't be garbage collected when they go out of scope if they have event bindings. This is because the objects whose events they bind to--Backbone objects, in the case of binding to Backbone object events, or the DOM event system in the case of "events" callbacks--maintain references to the view's functions.

Believe it or not, some Backbone users rely on this behavior and expect views to continue auto-responding to events like they were told to do, even if they have gone completely out of scope. (I've seen several tutorials that do this. ) This assumes that you never need to remove the view, or that the view can respond to events and remove itself, since you've lost any reference to it, but IMO, this 'create and forget' functionality is nice as long as you understand the implications.

mu is too short makes a good point about UI events. Removing the el from the DOM should remove the delegated events as well. The same can't be said for binding to model or collection events or to other Backbone objects' events (any object can extend the Backbone Events prototype). You'll need to follow Derick Bailey's advice in the article you link to and manually close the view in these cases. I am not sure if this is a weakness of Backbone compared to other JS MVC frameworks, I haven't tried others as in depth.

"Is there a way to detect when a view is being removed?"

Not directly, that I'm aware of. But generally, whichever code is removing view should also clean up the event bindings if it needs done. Often, in good MVC architectures, views can set up an event observer on a corresponding model or collection, and then remove & cleanup themselves based on the event occuring (e.g., the "remove" event from a corresponding model). If you want to make your views' removal universally "detectable", one way would be to override the remove function in your own view prototype and trigger a custom event that can be observed by others.

Lagomorph answered 11/6, 2012 at 23:0 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.