Backbone.js: undelegateEvents 不删除事件
Backbone.js: undelegateEvents not removing events
在以下代码中:
HTML
<div id="myView">
<button id="test_button">
Test Button
</button>
<ul id="output"></ul>
</div>
JavaScript
var myView = Backbone.View.extend({
initialize: function() {
// why doesn't this remove the previously delegated events?
this.undelegateEvents();
this.delegateEvents({
'click #test_button': 'buttonClicked'
});
},
// this event fires twice for one button click
buttonClicked: function() {
$("#output").append('<li>Button was clicked</li>');
}
});
$(document).ready(function(){
new myView({el: "#myView"});
// instantiate view again
new myView({el: "#myView"});
});
为什么
this.undelegateEvents();
在 Backbone View 的 initialize() 方法中没有从 View 的先前实例化中删除先前委托的事件?
以上代码的JSFiddle示例:https://jsfiddle.net/billb123/o43zruea/28/
我会尽量不大喊大叫,但请停止尝试将视图绑定到现有元素。让视图创建并拥有自己的 el
,然后在替换它之前调用 view.remove()
将其杀死。这个简单的更改解决了视图事件的许多问题,如果您不这样做,您应该三思而后行。
在你的情况下,你会 HTML 像这样:
<script id="t" type="text/x-underscore">
<div id="myView">
<button id="test_button">
Test Button
</button>
</div>
</script>
<div id="container">
</div>
<ul id="output"> <!-- This is outside the container because we're going to empty and refill it -->
</ul>
你的 JavaScript 看起来像这样:
var myView = Backbone.View.extend({
events: {
'click #test_button': 'buttonClicked'
},
render: function() {
this.$el.html($('#t').html());
return this;
},
buttonClicked: function() {
$("#output").append('<li>Button was clicked</li>');
}
});
$(document).ready(function(){
var v = new myView();
$('#container').append(v.render().el);
v.remove(); // <----------------- Clean things up before adding a new one
v = new myView();
$('#container').append(v.render().el);
});
兴趣点:
- 创建视图然后渲染它然后将它放在页面上。
- 完成视图后调用
remove
。
- 视图进入容器内部。调用者拥有容器,视图拥有其
el
.
- 任何地方都没有
delegateEvents
或 undelegateEvents
呼叫。这些的存在几乎总是指向您的应用程序 IMO 中的结构问题。
- 每个视图都是自包含的:外部世界不会玩弄视图内部的任何东西,视图也不会影响自己。
已更新 fiddle:https://jsfiddle.net/bp8fqdgm/
但为什么您的尝试 undelegateEvents
没有任何作用? undelegateEvents
looks like this:
undelegateEvents: function() {
if (this.$el) this.$el.off('.delegateEvents' + this.cid);
return this;
},
cid
每个视图实例都是唯一的,因此每个视图实例都使用自己唯一的命名空间来处理 delegateEvents
绑定的事件。这意味着:
this.undelegateEvents();
this.delegateEvents();
是说:
- 删除此视图实例绑定的事件。这些事件将在
'.delegateEvents' + this.cid
命名空间中找到,其中 cid
对于每个视图实例都是唯一的。
- 绑定此视图实例定义的事件(或
delegateEvents
调用中的事件)。这些事件将使用 '.delegateEvents' + this.cid
命名空间附加。
因此您的 undelegateEvents
调用正在删除事件但不是全部,只有视图 实例 添加的特定事件绑定被删除。
您的 this.undelegateEvents()
调用实际上没有完成任何事情,因为它在错误的地点和错误的时间调用。如果 new View
呼叫者进行了 undelegateEvents
呼叫:
var v = new myView({el: "#myView"});
v.undelegateEvents();
new myView({el: "#myView"});
那么它就会在正确的时间正确的地点发生。当然,这意味着您的路由器需要跟踪当前视图,以便它可以在正确的时间currentView.undelegateEvents()
;但如果你这样做,那么你最好(IMO)采用我在答案顶部概述的方法。
在以下代码中:
HTML
<div id="myView">
<button id="test_button">
Test Button
</button>
<ul id="output"></ul>
</div>
JavaScript
var myView = Backbone.View.extend({
initialize: function() {
// why doesn't this remove the previously delegated events?
this.undelegateEvents();
this.delegateEvents({
'click #test_button': 'buttonClicked'
});
},
// this event fires twice for one button click
buttonClicked: function() {
$("#output").append('<li>Button was clicked</li>');
}
});
$(document).ready(function(){
new myView({el: "#myView"});
// instantiate view again
new myView({el: "#myView"});
});
为什么
this.undelegateEvents();
在 Backbone View 的 initialize() 方法中没有从 View 的先前实例化中删除先前委托的事件?
以上代码的JSFiddle示例:https://jsfiddle.net/billb123/o43zruea/28/
我会尽量不大喊大叫,但请停止尝试将视图绑定到现有元素。让视图创建并拥有自己的 el
,然后在替换它之前调用 view.remove()
将其杀死。这个简单的更改解决了视图事件的许多问题,如果您不这样做,您应该三思而后行。
在你的情况下,你会 HTML 像这样:
<script id="t" type="text/x-underscore">
<div id="myView">
<button id="test_button">
Test Button
</button>
</div>
</script>
<div id="container">
</div>
<ul id="output"> <!-- This is outside the container because we're going to empty and refill it -->
</ul>
你的 JavaScript 看起来像这样:
var myView = Backbone.View.extend({
events: {
'click #test_button': 'buttonClicked'
},
render: function() {
this.$el.html($('#t').html());
return this;
},
buttonClicked: function() {
$("#output").append('<li>Button was clicked</li>');
}
});
$(document).ready(function(){
var v = new myView();
$('#container').append(v.render().el);
v.remove(); // <----------------- Clean things up before adding a new one
v = new myView();
$('#container').append(v.render().el);
});
兴趣点:
- 创建视图然后渲染它然后将它放在页面上。
- 完成视图后调用
remove
。 - 视图进入容器内部。调用者拥有容器,视图拥有其
el
. - 任何地方都没有
delegateEvents
或undelegateEvents
呼叫。这些的存在几乎总是指向您的应用程序 IMO 中的结构问题。 - 每个视图都是自包含的:外部世界不会玩弄视图内部的任何东西,视图也不会影响自己。
已更新 fiddle:https://jsfiddle.net/bp8fqdgm/
但为什么您的尝试 undelegateEvents
没有任何作用? undelegateEvents
looks like this:
undelegateEvents: function() {
if (this.$el) this.$el.off('.delegateEvents' + this.cid);
return this;
},
cid
每个视图实例都是唯一的,因此每个视图实例都使用自己唯一的命名空间来处理 delegateEvents
绑定的事件。这意味着:
this.undelegateEvents();
this.delegateEvents();
是说:
- 删除此视图实例绑定的事件。这些事件将在
'.delegateEvents' + this.cid
命名空间中找到,其中cid
对于每个视图实例都是唯一的。 - 绑定此视图实例定义的事件(或
delegateEvents
调用中的事件)。这些事件将使用'.delegateEvents' + this.cid
命名空间附加。
因此您的 undelegateEvents
调用正在删除事件但不是全部,只有视图 实例 添加的特定事件绑定被删除。
您的 this.undelegateEvents()
调用实际上没有完成任何事情,因为它在错误的地点和错误的时间调用。如果 new View
呼叫者进行了 undelegateEvents
呼叫:
var v = new myView({el: "#myView"});
v.undelegateEvents();
new myView({el: "#myView"});
那么它就会在正确的时间正确的地点发生。当然,这意味着您的路由器需要跟踪当前视图,以便它可以在正确的时间currentView.undelegateEvents()
;但如果你这样做,那么你最好(IMO)采用我在答案顶部概述的方法。