Question

I've looked around for a while now and have not been able to find anything that suggests what the cause of this is.

My code:

var faqView = Backbone.View.extend({
    tagName: 'div',
    id: 'faq-list',
    initialize: function() {
        var view = this;
        this.collection = new faqCollection();
        this.collection.fetch({
            success: function(collection, response) {
                collection.each(function(faq){
                    view.$el.append(_.template($('script#faq_item').html(),{faq: faq.attributes}));
                });
            },
            error: function(collection, response) {
                view.$el.html("<p>Unable to get the FAQ items.<br>Please try again later.</p>");
            }
        });
    },
    render: function() {
        this.$el.appendTo('div#container');

        return this;
    },
    events: {
        'click h3': 'toggleAnswer'
    },
    toggleAnswer: function(event) {
        console.log(this);
        console.log(event);
    }
});

var router = Backbone.Router.extend({
    routes: {
        "faq": "faq",
        "*other": "defaultRoute"
    },
    faqView: {},
    initialize: function() {
        this.faqView = new faqView();
    },
    defaultRoute: function() {
        this.resetPage();
    },
    faq: function() {
        this.resetPage();
        $('body').addClass('page-faq');
        this.faqView.render();
    },
    resetPage: function() {
        $('body').removeClass('page-faq');
        this.faqView.remove();
    }
});

The above code is included as the last items in the <body>. The HTML is as follows.

<body>
    <div id="container">
    </div>
    <script type="text/template" id="faq_item">
        <h3 class="contracted"><span>{{faq.question}}</span></h3>
        <p style="display: none;">{{faq.answer}}</p>
    </script>
    <script type="text/javascript" src="./js/models.js"></script>
    <script type="text/javascript" src="./js/views.js"></script>
    <script type="text/javascript" src="./js/collection.js"></script>
    <script type="text/javascript" src="./js/router.js"></script>
    <script type="text/javascript">
        //<![CDATA[
            $(function() {
                var app = new router;
                Backbone.history.start();
            });
        //]]>
    </script>
</body>

All the required elements exist (as far as I can tell) and I'm not manually setting the el attribute of the View. I'm lost as to why the events are not binding/firing when the <h3> is clicked.

Edit No errors thrown and the functionality works if I don't use the router and create the view by it self. e.g.

var app = new faqView();
app.render();
Was it helpful?

Solution

Your problem is in your router. Right here in fact:

resetPage: function() {
    $('body').removeClass('page-faq');
    this.faqView.remove();
}

View#remove is just jQuery's remove on the view's el by default and that:

[...] method takes elements out of the DOM. [...] all bound events and jQuery data associated with the elements are removed

So once you this.faqView.remove(), the delegate handler that drives the view's events is gone.

The usual approach is to create and destroy views as needed instead of creating a view and caching it for later. Your router should look more like this:

var router = Backbone.Router.extend({
    routes: {
        "faq": "faq",
        "*other": "defaultRoute"
    },
    defaultRoute: function() {
        this.resetPage();
    },
    faq: function() {
        this.resetPage();
        $('body').addClass('page-faq');
        this.view = new faqView();
        this.view.render();
    },
    resetPage: function() {
        $('body').removeClass('page-faq');
        if(this.view)
            this.view.remove();
    }
});

Demo: http://jsfiddle.net/ambiguous/aDtDT/

You could try messing around with detach inside an overridden remove method in faqView as well but there's really no need to have an instance of faqView around all the time: create it when you need it and remove it when you don't.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top