我是backbone.js 的新手,我的第一个项目是实现一个可以在整个项目中轻松重用的弹出视图。我定义了以下要求。
要求
弹出框应该引用到 DOM 中的另一个元素,以便能够计算弹出框的位置并打开/关闭弹出框。引用充当弹出框切换按钮
一个新的弹出框被附加到正文中,关闭时弹出框被销毁并从 DOM 中删除
弹出框外的任何点击都会强制弹出框关闭并被销毁
弹出框是一个主干视图,它应该独立于它的父/创建者视图,应该使用事件执行诸如打开/关闭之类的通信
Popovers 内容可能是另一个视图
实施:
首先,我创建一个将作为弹出框引用的视图:
My.Views.Toggle = Backbone.View.extend({
tagName: 'a',
events: {
'click': 'toggle'
},
serialize: function() {
return { model: this.model };
},
initialize: function() {
this.listenTo(this.model, 'change', this.render);
},
afterRender: function() {
//here we should add the popover, right?
},
toggle: function(){
app.vent.trigger('my:navbar:toggle');
}
});
在上述实现中,我们尊重要求#4。现在我们必须考虑#1。为了能够给 papover 其父视图的引用,我们必须等到父视图完成渲染,对吗?所以我们必须在 afterRender 函数中创建弹出框:
var popover = new Popover.Views.Default({
content: "Hey there, I am a popover",
reference: this.$el
});
这样做的问题是它违反了要求#2。因为 afterRender 函数被调用了两次,在模型改变之前和之后。并且每当模型再次改变时,这个函数也会被调用。所以DOM中会有很多popover的实例。
我的问题是,如何保证 DOM 中只有一个 popover 实例?
My.Views.Popover = Backbone.View.extend({
className: 'popover',
initialize: function(options) {
this.visible = false;
this.content = options.content;
this.reference = options.reference;
app.vent.on('member:navbar:toggle', this.toggle, this);
this.render();
},
afterRender: function() {
$('body').append(this.$el.append(this.content));
},
show: function() {
this.visible = true;
this.$el.show();
},
hide: function() {
this.visible = false;
this.$el.hide();
},
toggle: function() {
this.visible ? this.hide() : this.show();
}
});
看起来像这样,但是有许多底层弹出窗口: