andreugrimalt
andreugrimalt

Reputation: 31

How to call jQuery code after Backbone template is rendered?

I am building an app with Backbone.js (using Underscore templates), Express and Node.js.

I made this very simple example to illustrate the problem (see code below), the app consists in a list of "titles" retrieved from the server.

I use an underscore template to render the HTML. After the HTML is rendered I would like to execute some jQuery code, like for example print in the console the position() of each "title". The problem is that the jQuery code gets called before the template has inserted the elements in the DOM, so jQuery can't find the elements.

What am I missing? Is there a way to get an event after the html is inserted in the DOM?

Here the code:

    <div class="container">
        <div id="tracks">

        </div>
    </div>

    <!-- Template -->
    <script id="trackTemplate" type="text/template">

        <ul id="<%= id2 %>" style="height:150px">
            <li><%= title %></li> 
        </ul>               

        <%
        // So it looks like this code gets code before the template's HTML is in the DOM.
            console.log($("#"+id2).position());
        %>
    </script>

    <script src="js/lib/jquery-1.10.2.min.js"></script>
    <script src="js/lib/underscore-min.js"></script>
    <script src="js/lib/backbone-min.js"></script>
    <script>
    var app = app || {};

    // Model
    app.Track = Backbone.Model.extend({
        defaults: {
            id2: '',
            title: 'Track',
        },
    });

    // Collection
    app.Library = Backbone.Collection.extend({
        model: app.Track,
        url: '/api/tracks' 
    });

    // Track view
    app.TrackView = Backbone.View.extend({
        tagName: 'div',
        className: 'trackContainer',
        template: _.template( $( '#trackTemplate' ).html() ),
        initialize:function(){
            this.model.on('change', this.render, this);
        },
        render: function() {
            this.$el.html( this.template( this.model.toJSON() ) );

            return this;
        },

    });

    // Collection View
    var numberOfTracks = 0;
    app.LibraryView = Backbone.View.extend({
        el: '#tracks',

        initialize: function( initialTracks ) {
            this.collection = new app.Library( initialTracks );
            this.collection.fetch(); 
            this.listenTo( this.collection, 'add', this.renderTrack);
        },

        // render library by rendering each track in its collection
        render: function() {
            this.collection.each(function( item ) {
                this.renderTrack( item );
                numberOfTracks++;
            }, this );
        },

        // render a track by creating a trackView and appending the
        // element it renders to the library's element
        renderTrack: function( item ) {
            var trackView = new app.TrackView({
                model: item
            });
            this.$el.append( trackView.render().el );
        },
    });

    // Initialise app
    var libraryView =  new app.LibraryView();
    </script>

Thanks

Upvotes: 3

Views: 3607

Answers (2)

backdesk
backdesk

Reputation: 1781

To cover some ground here, there isn't an event that fires once something is inserted into the DOM. I wouldn't actually advocate trying to get around that by putting code inside your template either. Rather take a look at the way your views are rendering.

If you want to throw some code into a pre-existing place holder you could look at doing something like this:

[view here].setElement(this.$([selector here])).render();

There's an excellent explanation of this at:

http://ianstormtaylor.com/rendering-views-in-backbonejs-isnt-always-simple/

It's a neat way of arranging sub-views if you don't need any heavyweight sub-view management. You should after that point be able to do stuff with jquery as your normally would within a view using this.$el and this.$. Providing your element is correctly scoped within the view.

Upvotes: 1

kapa
kapa

Reputation: 78741

  • The right place for your console.log call would be at the end of your render method or in renderTrack, right after append.

  • Backbone also makes it easy to create your own events and sign up for them through Backbone.Events. You could fire your own event at one of the places I mentioned in the previous paragraph.

  • There is a deprecated event family for tracking changes in the DOM called Mutation events - but they are deprecated and browser support is not quite reliable. The replacement is MutationObserver, defined in the DOM4 spec, browser support is not there yet (IE only has support from 11) but for testing it might be useful.

  • Another way would be to put your code in a setTimeout - this way the DOM node will surely be inserted at the time your console.log runs.

Upvotes: 0

Related Questions