2
votes

I have a fairly simple table created using a Backbone Marionette CompositeView. The only requirements on this table are that it display data and allow the user to sort it by clicking the header row. When the table is sorted, the columns used to sort the header column are tagged with classes to allow an arrow to be displayed using CSS (e.g. 'class="sort asc"').

Here is the problem: When the header row is clicked, the element that is passed to the callback (via event.target) is not part of the table that is on the DOM. Instead, using Chrome's debugging tools it appears that the target element is actually part of the template script block, and not the element the user actually sees.

The best work-around I've got right now is to lookup the element actually attached to the DOM by retrieving unique info from its clone in the template.

Here's the composite view code (borrowed from one of Derick Bailey's sample Fiddle's):

// The grid view
var GridView = Backbone.Marionette.CompositeView.extend({
    tagName: "table",
    template: "#grid-template",
    itemView: GridRow,

    events: {
        'click th': 'doSort'
    },

    doSort: function(event) {
        var target = $(event.target);
        this.collection.sortByField(target.data('sortby'));

        target.parent('tr').find('.sort').removeAttr('class');
        target.addClass('sort ' + this.collection.sortdir);
        debugger; // NOTE: no change to elements in the DOM. WTH?

        target = this.$('[data-sortby=' + target.data('sortby') + ']');
        target.parent('tr').find('.sort').removeAttr('class');
        target.addClass('sort ' + this.collection.sortdir);
        debugger; // NOTE: DOM updated.
    },

    appendHtml: function(collectionView, itemView) {
        collectionView.$("tbody").append(itemView.el);
    }
});

The template is simple as well:

<script id="grid-template" type="text/template">
    <thead>        
        <tr>
            <th data-sortby="username">Username</th>
            <th data-sortby="fullname">Full Name</th>
        </tr>
    </thead>
    <tbody></tbody>
</script>

A fork of Derek's original demo Fiddle modified to demonstrate the problem can be found here: http://jsfiddle.net/ccamarat/9stvM/14/

I've meandered a bit. My question is, I appear to have a spawned a Zombie View of sorts; is this a jQuery/Backbone/Underscore bug, or am I simply going about this all wrong?

** EDIT ** Here's the console output showing the different parent hierarchies of the two elements:

console.log(target, targetb):
    [<th data-sortby="username" class="sort asc">Username</th>] [<th data-sortby="username">Username</th>]

console.log(target.parents(), targetb.parents()):
    [<tr>…</tr>, <thead>…</thead>] [<tr>…</tr>, <thead>…</thead>, <table>…</table>, <div id="grid">…</div>, <body>…</body>, <html>…</html>]

console.log(target === targetb):
    false
1
can you be more specific about the problem / problems? you've mentioned several potential issues in a very detailed manner, but then only ask one very short question with no detail, about a zombie view. which problem(s) are you trying to solve? why do you think you have a zombie view? what zombies are you seeing, where?Derick Bailey
also - i've run your example fiddle in the latest version of Chrome, and it works fine. the comment where you said the DOM has not been updated... it's been updated at that point, when I run it. what browser / version are you using?Derick Bailey
If it sounds like there's more than one problem than I've done a poor job explaining myself. The only issue is what I'm calling the Zombie View. The reason I call it that is because, in my setup at least, the element updated at the first breakpoint (using event.target) is not the same one as the element at the second (using this.$el.find()). I'm using the latest version of Chrome to test, and I've been able to replicate twice on two different machines.Chris Camaratta
I've updated my Fiddle to test whether the two elements are equal, and in my setup they're not. It's here: jsfiddle.net/ccamarat/9stvM/13Chris Camaratta
Also, I updated the post above with the the console output showing that the two elements have different parent hierarchies.Chris Camaratta

1 Answers

2
votes

I don't see any evidence of a zombie view.

I created a fork of the code and put in some additional logging to check view instances, and I don't see any zombies. The same GridView instance is being used every time. And the children views are being closed correctly every time it sorts.

http://jsfiddle.net/derickbailey/hadbf/4/

The issue your seeing is likely caused by the use of target = $(e.target) and targetb = this.$(...). These are two very different statements, working from two very different sources for the DOM elements.

The first use of target = $(e.target) is running directly from the DOM. You're running a selector on the target that was clicked, which is just wrapping that DOM element in a jQuery selector object.

The second use of targetB = this.$(...) is using the Backbone View's $el to do a find: this.$el.find("...") which is going to return results based on the view's EL. The view's $eL happens to contain elements that are part of the DOM. But the $el is not the same object as the e event, so you will never end up with target === targetb being true.

... unless I'm completely misunderstanding the issue (which seems very likely at this point), I don't think there is an issue here.