События Backbone 1.0 js все еще прикреплены после .html([content])

Проблема, с которой я сталкиваюсь, заключается в том, что события кликов продолжают накапливаться (все еще прикреплены после изменения представления). Я исправил проблему, имея только один экземпляр представления (показан ниже). Я думал, что backbone избавился от событий при изменении разметки. У меня не было этой проблемы с другими представлениями.

НЕИСПРАВНЫЙ КОД: события кликов продолжают накапливаться на loadPlayerCard по мере создания новых представлений.

//Player Thumb View
PgaPlayersApp.PlayerThumbView = Backbone.View.extend({
    events: {
        'click': 'loadPlayerCard'
    },
    tagName: 'li',
    template: _.template( $('#player_thumb').html()),    
    render: function()
    {
        this.$el.html(this.template(this.model.toJSON()));
        return this;
    },
    loadPlayerCard: function()
    {
        new PlayerCardView({model: this.model}).render();
        return false;
    }
});

//Router
var Router = Backbone.Router.extend({
    routes:{
      '': 'loadPlayers'
    },

    loadPlayers: function()
    {
        PgaPlayersApp.Players.fetch({reset: true, success: function()
        {
            //When players is first fetched, we want to render the first player into the card area
            new PlayerCardView({model: PgaPlayersApp.Players.first()}).render();

        }});
    }
});

PgaPlayersApp.Router = new Router();
Backbone.history.start();

ИСПРАВЛЕННЫЙ КОД: Код, устраняющий проблему:

PgaPlayersApp.CurrentPlayerCard = new PlayerCardView();

//Player Thumb View
PgaPlayersApp.PlayerThumbView = Backbone.View.extend({
    events: {
        'click': 'loadPlayerCard'
    },
    tagName: 'li',
    template: _.template( $('#player_thumb').html()),    
    render: function()
    {
        this.$el.html(this.template(this.model.toJSON()));
        return this;
    },
    loadPlayerCard: function()
    {
        PgaPlayersApp.CurrentPlayerCard.model = this.model;
        PgaPlayersApp.CurrentPlayerCard.render();
        return false;
    }
});

//Router
var Router = Backbone.Router.extend({
    routes:{
      '': 'loadPlayers'
    },

    loadPlayers: function()
    {
        PgaPlayersApp.Players.fetch({reset: true, success: function()
        {
            //When players is first fetched, we want to render the first player into the card area
            PgaPlayersApp.CurrentPlayerCard.model = PgaPlayersApp.Players.first();
            PgaPlayersApp.CurrentPlayerCard.render();
        }});
    }
});

PgaPlayersApp.Router = new Router();
Backbone.history.start();

PlayerCardView (для справки):

var PlayerCardView = PgaPlayersApp.PlayerCardView = Backbone.View.extend({
    events: {
        'click': 'flipCard'
    },
    el: '#pga_player_card',
    template: _.template( $('#player_card').html()),
    render: function()
    {
        this.$el.html(this.template(this.model.toJSON()));
        return this;
    },
    flipCard: function()
    {
        this.$("#player_card_container").toggleClass('flip');
    }
});

person Chris Muench    schedule 14.06.2013    source источник


Ответы (1)


В вашем маршрутизаторе вы продолжаете создавать новые PlayerCardView:

new PlayerCardView({model: PgaPlayersApp.Players.first()}).render();

Все эти представления имеют одни и те же el:

el: '#pga_player_card'

Итак, вы продолжаете создавать новые PlayerCardView, и каждый из них привязывается к #pga_player_card.

Каждый раз, когда вы это делаете, вы привязываете новое представление к одному и тому же элементу DOM, и каждое из этих представлений будет вызывать delegateEvents для привязки обработчиков событий. Обратите внимание, что delegateEvents связывается с el и что метод jQuery html:

удаляет другие конструкции, такие как данные и обработчики событий, из дочерних элементов перед заменой этих элементов новым содержимым.

Таким образом, html ничего не делает с el, но удаляет обработчики событий из дочерних элементов. Рассмотрим этот простой пример с <div id="d"></div>:

$('#d').on('click', function() {
    console.log('Before .html');
});
$('#d').html('<p>Where is pancakes house?</p>');
$('#d').on('click', function() {
    console.log('After .html');
});

Если вы затем нажмете на #d, вы увидите сообщения до и после в консоли.

Демонстрация: http://jsfiddle.net/ambiguous/ftJtS/

Этот простой пример более или менее эквивалентен тому, что вы делаете.

Вам будет лучше, если вы:

  1. Поместите представление внутри #pga_player_card и позвольте маршрутизатору сделать $('#pga_player_card').append(view.render().el).
  2. Отслеживайте уже существующее представление и view.remove() перед добавлением нового.

Не пытайтесь повторно использовать элементы DOM для нескольких экземпляров представления и не пытайтесь повторно использовать представления, ни то, ни другое не стоит хлопот.

person mu is too short    schedule 14.06.2013