Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Backbone.View: delegateEvents not re-binding events to subview

I have broken down this issue into the smallest example possible (i.e., it's only to demonstrate the problem, not to necessarily represent a real-world scenario).

Let's say I have a parent view ("MainView" here) that contains a child view ("SubView" here). If, at any point, I need to re-render the parent view (which thereby re-renders the child view), I am losing the event bindings in the child view, despite calling delegateEvents.

A jsfiddle can be found here: http://jsfiddle.net/ya87u/1/

Here is the code in full:

var MainView = Backbone.View.extend({
    tagName : "div",
    initialize : function() {
        this.subView = new SubView();
    },
    render : function() {
        this.$el.html(this.subView.render().$el); // .html() breaks binds
        this.subView.delegateEvents(); // this re-establishes them
        return this;
    }
});

var SubView = Backbone.View.extend({
    tagName : "div",
    events : {
        "click .button1" : "onButtonPress"
    },
    onButtonPress : function() {
        alert("Button pressed");
    },
    render : function() {
        var html = "<button class='button1'>Button1</button>";
        this.$el.html(html);
        return this;
    }
});

var v = new MainView();
$("#area1").html(v.render().$el); // binds work fine

// do some work... then re-render...

$("#area1").html(v.render().$el); // breaks event binds

I do not understand why this is happening. Any input would be greatly appreciated.

like image 254
EleventyOne Avatar asked Oct 01 '22 22:10

EleventyOne


1 Answers

To preserve the bindings you could do three things.

Use $.append instead of $.html

Using $.append will keep your bindings intact and also take care that you don't lose any sibling content.

Just re-render without re-attaching

In your code you re-render your view and also re-attach it's content. This is not necessary. Simply calling v.render() will achieve the same visual result but keep your bindings intact.

var v = new MainView();
$("#area1").html(v.render().$el); // binds work fine

// do some work... then re-render…

v.render(); // keeps event binds

$("#area1").append(v.render().$el); // works too

Use $.empty() before calling $.html()

According to the jQuery documentation for $.html(htmlString)

If you are keeping references to these DOM elements and need them to be unchanged, use .empty().html( string ) instead of .html(string) so that the elements are removed from the document before the new string is assigned to the element.

The important part is that $.html expects an html string, not elements and also unbinds any event handlers present on child nodes. The only way I can explain this behavior is that somehow through transformation of the dom tree to a string and vice versa the events get removed. Interestingly enough, using $.empty() as mentioned in the documentation solves the problem.

// any $.html reference needs to have an $.empty too
this.$el.empty().html(this.subView.render().$el);

// and then later for your mainView
$("#area1").empty().html(v.render().$el);
like image 91
Torsten Walter Avatar answered Oct 05 '22 16:10

Torsten Walter