将事件绑定到视图 - 触发次数不均匀
Binding events to views - Firing uneven number of times
我有 UserModel
:UserView
和 UserCollection
:UserCollectionView
。有了这个,我试图将 click
事件绑定到 UserView
(我在 UserView
中这样做)。所以,这是我的代码:
var root = 'http://localhost:5000/api/v1';
var app = {};
// Backbone Model
app.UserModel = Backbone.Model.extend({
defaults: {
// urlRoot: root + '/users',
name: 'Default Name',
email: '30',
username: 'default_username'
},
initialize: function() {
this.set({
id: this.get('username')
});
console.log('User model \'' + this.id + '\' has been initialized.');
},
// parse: function(data) {
// console.log('Model parse funciton called');
// return data;
// }
});
// Backbone Model View
app.UserView = Backbone.View.extend({
// el: '#users-list',
// tagName: 'div',
el: '.user-box-wrapper',
events: {
'click .user-data': 'userClicked'
},
userClicked: function(ev) {
console.log("User selected");
// console.log(ev.currentTarget);
},
template: _.template($('#connections-user-template').html()),
initialize: function() {
this.render();
},
render: function() {
$('#users-list').append(this.template(this.model.toJSON()));
// this.$el.append( this.template( this.model.toJSON()));
console.log('User view is rendered');
}
});
// Backbone Collection
app.UserCollection = Backbone.Collection.extend({
model: app.UserModel,
url: root + '/users',
initialize: function() {
// this.fetch();
},
parse: function(data) {
// console.log(data.data);
return data.data;
}
});
// Backbone Collection View
app.UserCollectionView = Backbone.View.extend({
el: '#users-list',
template: _.template($('#connections-template').html()),
initialize: function() {
this.connections = new app.UserCollection();
var self = this;
this.connections.fetch().done(function() {
self.render();
});
},
render: function() {
console.log('User collection view is rendered');
this.$el.html(this.template());
// this.$el.append( this.template( this.model.toJSON()));
this.connections.each(function(user) {
console.log('User : ' + user.get('id'));
var userView = new app.UserView({
model: user
});
// userView.model.fetch();
// userView.render();
});
}
});
var connectionsView = new app.UserCollectionView();
JSON 数据实际上 returns 14 个对象(或本例中的 UserModels)。问题是,如果我点击第一个用户视图,它被触发了 13 次,第二个视图点击被触发了 12 次等等,最后一个视图点击事件在点击时根本没有被触发。
不过,每个 UserView 都渲染一次(至少我是这么认为的)。有人可以解释这里的问题是什么以及这里到底发生了什么吗?
P.S。 - 我知道在 CollectionView 中绑定事件的解决方法。
编辑 1
这是 DOM 结构:
<!doctype html>
<html>
<head>
<title>Hey there</title>
<link rel="stylesheet" href="../static/css/normalize.css">
<link rel="stylesheet" href="../static/css/index.css">
<script src="/static/js/jquery-2.2.0.js"></script>
<script src="/static/js/underscore-1.8.3.js"></script>
<script src="/static/js/backbone-1.2.3.js"></script>
</head>
<body>
<header id="top">
<div id="logo-wrapper">
<img src="../static/img/logo.png" alt="Logo" id="logo">
</div>
<div id="top-links">
<div id="top-profile-box" class="toplink">
<div id="top-profile-data-box">
<div id="top-profile-data-name">Kevin Isaac</div>
<div id="top-profile-data-passion">Writer</div>
</div>
<img id="top-profile-image" src="../static/img/user1.jpg" alt="">
</div>
<div id="notification-icon" class="toplink"></div>
<div id="top-message-icon" class="toplink"></div>
<div id="logout-icon" class="toplink"></div>
</div>
</header>
<div id="middle">
<nav id="side-nav">
<div id="side-nav-top">
<div class="side-nav-link" id="side-nav-home-link">
<div class="side-nav-link-img"></div>
<div class="side-nav-link-title">Home</div>
</div>
<div class="side-nav-link" id="side-nav-profile-link">
<div class="side-nav-link-img"></div>
<div class="side-nav-link-title">Profile</div>
</div>
<div class="side-nav-link" id="side-nav-messages-link">
<div class="side-nav-link-img"></div>
<div class="side-nav-link-title">Message</div>
</div>
<div class="side-nav-link" id="side-nav-account-link">
<div class="side-nav-link-img"></div>
<div class="side-nav-link-title">Account</div>
</div>
</div>
</nav>
<div id="main-content">
<!-- Start of page specific HTML -->
<div id="content-title">
<div class="content-subtitle" id="connections">Connections</div>
<div class="content-subtitle" id="followers">Followers</div>
<div class="content-subtitle" id="followings">Followings</div>
</div>
<div id="content-body">
<div id="users-box">
<div id="users-list">No connection</div>
<!-- Backbone Template Starts -->
<script type="text/template" id="connections-template"></script>
<script type="text/template" id="connections-user-template">
<div class="user-box-wrapper">
<div class="user-box">
<div class="user-pic-wrapper">
<img src="/static/img/user1.jpg" alt="">
</div>
<div class="user-data" id="boox">
<div class="user-name"><%= name %></div>
<div class="user-passion"><%= username %></div>
<div class="user-city"><%= email %></div>
</div>
</div>
</div>
</script>
<!-- Backbone Template Ends -->
<div id="users-side-box">
<div id="users-box-search">
<input id="user-search" type="text" name="">
</div>
<div id="user-metadata">
<div id="metadata-user-top-box">
<div id="metadata-user-image-wrapper">
<img src="/static/img/user1.jpg" alt="">
</div>
<div id="metadata-user-name-box">
<div id="metadata-name">Name's Bond</div>
<div id="metadata-passion">Assassin</div>
</div>
</div>
<div id="metadata-user-bottom-box">
<div class="metadata-user-attribute">
<span class="metadata-property">Studied at: </span>
<span class="metadata-value">Karunya University </span>
</div>
<div class="metadata-user-attribute">
<span class="metadata-property">Native City: </span>
<span class="metadata-value">London</span>
</div>
<div class="metadata-user-attribute">
<span class="metadata-property">Website: </span>
<span class="metadata-value">www.007.com</span>
</div>
</div>
</div>
</div>
</div>
</div>
<!-- End of page specific HTML -->
</div>
<aside id="main-aside">
Aside one two therr
</aside>
</div>
<script src="../static/js/index.min.js"></script>
</body>
</html>
主要问题是您在同一容器中呈现所有 UserView 视图:
$('#users-list').append(this.template(this.model.toJSON()));
这就是点击事件选择器 ('click .user-data') 选择其他 UserView 按钮并触发它的点击的原因。
你没有问,但是...这里有一些建议:
- 不要从 initialize 函数调用渲染;
- 避免定义 el;
- 避免使用 $ 在视图外添加视图内容。采用
this.$el 改为;
- 永远不要忘记 return 这个; 在你渲染函数时。
这是一个 jsfiddle,其中包含我认为您想要的内容:
https://jsfiddle.net/Neviton/n52j873u/
你的代码有很多问题。
主要问题是,您所有的 userView 都指向同一个选择器,并且与该选择器匹配的元素 .user-box-wrapper
位于视图模板内 - 因此无论何时您创建一个新的 userView,它都会添加一个.user-box-wrapper
的新事件侦听器存在于所有现有的用户视图中,但不存在于其自身。因此,您的第一个 userView 将注册 n-1
个事件处理程序,而最后一个注册 none (n
是 userView 的总数)。视图元素不应该是模板的一部分,模板应该添加到视图元素。
当您的视图将拥有多个自身副本时,请不要定义 el
选项,让 backbone 为视图的每个实例创建一个新元素。您可以自定义此元素的属性。
另一个问题是您没有将 userView 模板附加到您的 userView 元素,而是附加到它之外的其他东西($('#users-list')
。因此单击模板不会触发事件处理程序(在您的情况下是为什么你是最后一个 userView 没有触发它的事件。它绑定到所有其他现有视图,因为公共选择器由 el
)
提供
您应该尽量避免在视图中使用 $('#users-list')
这样的全局选择器。在这种情况下,您可以从 el 指向 #users-list
.
的 userCollectionView 中将 userView 附加到 #users-list
您的代码应该是:
var root = 'http://localhost:5000/api/v1';
var app = {};
// Backbone Model
app.UserModel = Backbone.Model.extend({
defaults: {
// urlRoot: root + '/users',
name: 'Default Name',
email: '30',
username: 'default_username'
},
initialize: function() {
this.set({
id: this.get('username')
});
console.log('User model \'' + this.id + '\' has been initialized.');
}
});
// Backbone Model View
app.UserView = Backbone.View.extend({
className: 'user-box-wrapper',
/*--^-- creates a new div with this class for each user*/
template: _.template($('#connections-user-template').html()),
events: {
'click .user-data': 'userClicked'
},
initialize: function() {
this.render();
},
render: function() {
this.$el.append( this.template( this.model.toJSON()));
console.log('User view is rendered');
},
userClicked: function(ev) {
console.log("User selected");
// console.log(ev.currentTarget);
}
});
// Backbone Collection
app.UserCollection = Backbone.Collection.extend({
model: app.UserModel,
url: root + '/users',
initialize: function() {
// this.fetch();
},
parse: function(data) {
// console.log(data.data);
return data.data;
}
});
// Backbone Collection View
app.UserCollectionView = Backbone.View.extend({
el: '#users-list',
template: _.template($('#connections-template').html()),
initialize: function() {
this.connections = new app.UserCollection();
var self = this;
this.connections.fetch().done(function() {
self.render();
});
},
render: function() {
this.$el.html(this.template());
console.log('User collection view is rendered');
this.connections.each(function(user) {
console.log('User : ' + user.get('id'));
var userView = new app.UserView({
model: user
});
this.$el.append(userView.el); /*append child view here*/
},this);
}
});
var connectionsView = new app.UserCollectionView();
我有 UserModel
:UserView
和 UserCollection
:UserCollectionView
。有了这个,我试图将 click
事件绑定到 UserView
(我在 UserView
中这样做)。所以,这是我的代码:
var root = 'http://localhost:5000/api/v1';
var app = {};
// Backbone Model
app.UserModel = Backbone.Model.extend({
defaults: {
// urlRoot: root + '/users',
name: 'Default Name',
email: '30',
username: 'default_username'
},
initialize: function() {
this.set({
id: this.get('username')
});
console.log('User model \'' + this.id + '\' has been initialized.');
},
// parse: function(data) {
// console.log('Model parse funciton called');
// return data;
// }
});
// Backbone Model View
app.UserView = Backbone.View.extend({
// el: '#users-list',
// tagName: 'div',
el: '.user-box-wrapper',
events: {
'click .user-data': 'userClicked'
},
userClicked: function(ev) {
console.log("User selected");
// console.log(ev.currentTarget);
},
template: _.template($('#connections-user-template').html()),
initialize: function() {
this.render();
},
render: function() {
$('#users-list').append(this.template(this.model.toJSON()));
// this.$el.append( this.template( this.model.toJSON()));
console.log('User view is rendered');
}
});
// Backbone Collection
app.UserCollection = Backbone.Collection.extend({
model: app.UserModel,
url: root + '/users',
initialize: function() {
// this.fetch();
},
parse: function(data) {
// console.log(data.data);
return data.data;
}
});
// Backbone Collection View
app.UserCollectionView = Backbone.View.extend({
el: '#users-list',
template: _.template($('#connections-template').html()),
initialize: function() {
this.connections = new app.UserCollection();
var self = this;
this.connections.fetch().done(function() {
self.render();
});
},
render: function() {
console.log('User collection view is rendered');
this.$el.html(this.template());
// this.$el.append( this.template( this.model.toJSON()));
this.connections.each(function(user) {
console.log('User : ' + user.get('id'));
var userView = new app.UserView({
model: user
});
// userView.model.fetch();
// userView.render();
});
}
});
var connectionsView = new app.UserCollectionView();
JSON 数据实际上 returns 14 个对象(或本例中的 UserModels)。问题是,如果我点击第一个用户视图,它被触发了 13 次,第二个视图点击被触发了 12 次等等,最后一个视图点击事件在点击时根本没有被触发。
不过,每个 UserView 都渲染一次(至少我是这么认为的)。有人可以解释这里的问题是什么以及这里到底发生了什么吗?
P.S。 - 我知道在 CollectionView 中绑定事件的解决方法。
编辑 1
这是 DOM 结构:
<!doctype html>
<html>
<head>
<title>Hey there</title>
<link rel="stylesheet" href="../static/css/normalize.css">
<link rel="stylesheet" href="../static/css/index.css">
<script src="/static/js/jquery-2.2.0.js"></script>
<script src="/static/js/underscore-1.8.3.js"></script>
<script src="/static/js/backbone-1.2.3.js"></script>
</head>
<body>
<header id="top">
<div id="logo-wrapper">
<img src="../static/img/logo.png" alt="Logo" id="logo">
</div>
<div id="top-links">
<div id="top-profile-box" class="toplink">
<div id="top-profile-data-box">
<div id="top-profile-data-name">Kevin Isaac</div>
<div id="top-profile-data-passion">Writer</div>
</div>
<img id="top-profile-image" src="../static/img/user1.jpg" alt="">
</div>
<div id="notification-icon" class="toplink"></div>
<div id="top-message-icon" class="toplink"></div>
<div id="logout-icon" class="toplink"></div>
</div>
</header>
<div id="middle">
<nav id="side-nav">
<div id="side-nav-top">
<div class="side-nav-link" id="side-nav-home-link">
<div class="side-nav-link-img"></div>
<div class="side-nav-link-title">Home</div>
</div>
<div class="side-nav-link" id="side-nav-profile-link">
<div class="side-nav-link-img"></div>
<div class="side-nav-link-title">Profile</div>
</div>
<div class="side-nav-link" id="side-nav-messages-link">
<div class="side-nav-link-img"></div>
<div class="side-nav-link-title">Message</div>
</div>
<div class="side-nav-link" id="side-nav-account-link">
<div class="side-nav-link-img"></div>
<div class="side-nav-link-title">Account</div>
</div>
</div>
</nav>
<div id="main-content">
<!-- Start of page specific HTML -->
<div id="content-title">
<div class="content-subtitle" id="connections">Connections</div>
<div class="content-subtitle" id="followers">Followers</div>
<div class="content-subtitle" id="followings">Followings</div>
</div>
<div id="content-body">
<div id="users-box">
<div id="users-list">No connection</div>
<!-- Backbone Template Starts -->
<script type="text/template" id="connections-template"></script>
<script type="text/template" id="connections-user-template">
<div class="user-box-wrapper">
<div class="user-box">
<div class="user-pic-wrapper">
<img src="/static/img/user1.jpg" alt="">
</div>
<div class="user-data" id="boox">
<div class="user-name"><%= name %></div>
<div class="user-passion"><%= username %></div>
<div class="user-city"><%= email %></div>
</div>
</div>
</div>
</script>
<!-- Backbone Template Ends -->
<div id="users-side-box">
<div id="users-box-search">
<input id="user-search" type="text" name="">
</div>
<div id="user-metadata">
<div id="metadata-user-top-box">
<div id="metadata-user-image-wrapper">
<img src="/static/img/user1.jpg" alt="">
</div>
<div id="metadata-user-name-box">
<div id="metadata-name">Name's Bond</div>
<div id="metadata-passion">Assassin</div>
</div>
</div>
<div id="metadata-user-bottom-box">
<div class="metadata-user-attribute">
<span class="metadata-property">Studied at: </span>
<span class="metadata-value">Karunya University </span>
</div>
<div class="metadata-user-attribute">
<span class="metadata-property">Native City: </span>
<span class="metadata-value">London</span>
</div>
<div class="metadata-user-attribute">
<span class="metadata-property">Website: </span>
<span class="metadata-value">www.007.com</span>
</div>
</div>
</div>
</div>
</div>
</div>
<!-- End of page specific HTML -->
</div>
<aside id="main-aside">
Aside one two therr
</aside>
</div>
<script src="../static/js/index.min.js"></script>
</body>
</html>
主要问题是您在同一容器中呈现所有 UserView 视图:
$('#users-list').append(this.template(this.model.toJSON()));
这就是点击事件选择器 ('click .user-data') 选择其他 UserView 按钮并触发它的点击的原因。
你没有问,但是...这里有一些建议:
- 不要从 initialize 函数调用渲染;
- 避免定义 el;
- 避免使用 $ 在视图外添加视图内容。采用 this.$el 改为;
- 永远不要忘记 return 这个; 在你渲染函数时。
这是一个 jsfiddle,其中包含我认为您想要的内容: https://jsfiddle.net/Neviton/n52j873u/
你的代码有很多问题。
主要问题是,您所有的 userView 都指向同一个选择器,并且与该选择器匹配的元素 .user-box-wrapper
位于视图模板内 - 因此无论何时您创建一个新的 userView,它都会添加一个.user-box-wrapper
的新事件侦听器存在于所有现有的用户视图中,但不存在于其自身。因此,您的第一个 userView 将注册 n-1
个事件处理程序,而最后一个注册 none (n
是 userView 的总数)。视图元素不应该是模板的一部分,模板应该添加到视图元素。
当您的视图将拥有多个自身副本时,请不要定义 el
选项,让 backbone 为视图的每个实例创建一个新元素。您可以自定义此元素的属性。
另一个问题是您没有将 userView 模板附加到您的 userView 元素,而是附加到它之外的其他东西($('#users-list')
。因此单击模板不会触发事件处理程序(在您的情况下是为什么你是最后一个 userView 没有触发它的事件。它绑定到所有其他现有视图,因为公共选择器由 el
)
您应该尽量避免在视图中使用 $('#users-list')
这样的全局选择器。在这种情况下,您可以从 el 指向 #users-list
.
#users-list
您的代码应该是:
var root = 'http://localhost:5000/api/v1';
var app = {};
// Backbone Model
app.UserModel = Backbone.Model.extend({
defaults: {
// urlRoot: root + '/users',
name: 'Default Name',
email: '30',
username: 'default_username'
},
initialize: function() {
this.set({
id: this.get('username')
});
console.log('User model \'' + this.id + '\' has been initialized.');
}
});
// Backbone Model View
app.UserView = Backbone.View.extend({
className: 'user-box-wrapper',
/*--^-- creates a new div with this class for each user*/
template: _.template($('#connections-user-template').html()),
events: {
'click .user-data': 'userClicked'
},
initialize: function() {
this.render();
},
render: function() {
this.$el.append( this.template( this.model.toJSON()));
console.log('User view is rendered');
},
userClicked: function(ev) {
console.log("User selected");
// console.log(ev.currentTarget);
}
});
// Backbone Collection
app.UserCollection = Backbone.Collection.extend({
model: app.UserModel,
url: root + '/users',
initialize: function() {
// this.fetch();
},
parse: function(data) {
// console.log(data.data);
return data.data;
}
});
// Backbone Collection View
app.UserCollectionView = Backbone.View.extend({
el: '#users-list',
template: _.template($('#connections-template').html()),
initialize: function() {
this.connections = new app.UserCollection();
var self = this;
this.connections.fetch().done(function() {
self.render();
});
},
render: function() {
this.$el.html(this.template());
console.log('User collection view is rendered');
this.connections.each(function(user) {
console.log('User : ' + user.get('id'));
var userView = new app.UserView({
model: user
});
this.$el.append(userView.el); /*append child view here*/
},this);
}
});
var connectionsView = new app.UserCollectionView();