Backbone.js视图事件委托 #
一、事件委托基础 #
1.1 什么是事件委托 #
事件委托是利用事件冒泡机制,在父元素上统一处理子元素的事件。
text
传统方式:每个子元素绑定事件处理器
事件委托:在父元素上统一处理,通过target判断来源
优势:
├── 减少事件处理器数量
├── 动态元素自动支持
└── 内存占用更低
1.2 events对象 #
Backbone.js 使用 events 对象声明式绑定事件:
javascript
var UserView = Backbone.View.extend({
events: {
'click .btn-edit': 'editUser',
'click .btn-delete': 'deleteUser',
'click': 'select'
},
editUser: function(e) {
console.log('编辑用户');
},
deleteUser: function(e) {
console.log('删除用户');
},
select: function(e) {
console.log('选中用户');
}
});
1.3 事件格式 #
text
'事件名 选择器': '处理函数名'
| 部分 | 说明 | 示例 |
|---|---|---|
| 事件名 | DOM事件类型 | click, keypress, submit |
| 选择器 | 子元素选择器 | .btn, #submit, input |
| 处理函数 | 方法名或函数 | ‘onClick’, function() {} |
二、事件绑定 #
2.1 基本绑定 #
javascript
var TodoView = Backbone.View.extend({
events: {
'click': 'toggle',
'click .delete': 'clear'
},
toggle: function() {
this.model.toggle();
},
clear: function(e) {
e.stopPropagation();
this.model.destroy();
}
});
2.2 绑定多个事件 #
javascript
var FormView = Backbone.View.extend({
events: {
'click .btn-save': 'save',
'click .btn-cancel': 'cancel',
'keypress input': 'onKeyPress',
'submit': 'onSubmit'
}
});
2.3 同一处理函数 #
javascript
var InputView = Backbone.View.extend({
events: {
'keypress input': 'update',
'change input': 'update',
'input input': 'update'
},
update: function(e) {
var $target = $(e.target);
this.model.set($target.attr('name'), $target.val());
}
});
2.4 直接绑定函数 #
javascript
var UserView = Backbone.View.extend({
events: {
'click .btn': function(e) {
console.log('按钮被点击');
}
}
});
2.5 函数形式events #
javascript
var UserView = Backbone.View.extend({
events: function() {
var events = {
'click .btn-edit': 'edit'
};
if (this.options.deletable) {
events['click .btn-delete'] = 'delete';
}
return events;
}
});
三、事件类型 #
3.1 鼠标事件 #
javascript
var UserView = Backbone.View.extend({
events: {
'click': 'onClick',
'dblclick': 'onDoubleClick',
'mouseenter': 'onMouseEnter',
'mouseleave': 'onMouseLeave',
'mousedown': 'onMouseDown',
'mouseup': 'onMouseUp'
},
onClick: function(e) {
console.log('单击');
},
onDoubleClick: function(e) {
console.log('双击');
},
onMouseEnter: function(e) {
this.$el.addClass('hover');
},
onMouseLeave: function(e) {
this.$el.removeClass('hover');
}
});
3.2 键盘事件 #
javascript
var InputView = Backbone.View.extend({
events: {
'keypress': 'onKeyPress',
'keydown': 'onKeyDown',
'keyup': 'onKeyUp'
},
onKeyPress: function(e) {
if (e.which === 13) {
this.submit();
}
},
onKeyDown: function(e) {
if (e.which === 27) {
this.cancel();
}
}
});
3.3 表单事件 #
javascript
var FormView = Backbone.View.extend({
events: {
'submit': 'onSubmit',
'change input': 'onChange',
'input input': 'onInput',
'focus input': 'onFocus',
'blur input': 'onBlur'
},
onSubmit: function(e) {
e.preventDefault();
this.save();
},
onChange: function(e) {
var $input = $(e.target);
this.model.set($input.attr('name'), $input.val());
},
onInput: function(e) {
this.validate($(e.target));
},
onFocus: function(e) {
$(e.target).addClass('focused');
},
onBlur: function(e) {
$(e.target).removeClass('focused');
}
});
3.4 其他事件 #
javascript
var View = Backbone.View.extend({
events: {
'scroll': 'onScroll',
'resize': 'onResize',
'contextmenu': 'onContextMenu'
}
});
四、事件对象 #
4.1 事件参数 #
javascript
var UserView = Backbone.View.extend({
events: {
'click .btn': 'onClick'
},
onClick: function(e) {
console.log('事件类型:', e.type);
console.log('目标元素:', e.target);
console.log('当前元素:', e.currentTarget);
console.log('鼠标位置:', e.pageX, e.pageY);
console.log('按键码:', e.which);
}
});
4.2 阻止默认行为 #
javascript
var LinkView = Backbone.View.extend({
events: {
'click a': 'onClick'
},
onClick: function(e) {
e.preventDefault();
console.log('链接被点击,但不会跳转');
}
});
4.3 阻止冒泡 #
javascript
var TodoView = Backbone.View.extend({
events: {
'click': 'toggle',
'click .delete': 'delete'
},
toggle: function() {
console.log('切换完成状态');
},
delete: function(e) {
e.stopPropagation();
console.log('删除任务');
}
});
4.4 获取数据 #
javascript
var UserView = Backbone.View.extend({
events: {
'click .item': 'select'
},
render: function() {
this.$el.html(
'<div class="item" data-id="1">项目1</div>' +
'<div class="item" data-id="2">项目2</div>'
);
return this;
},
select: function(e) {
var id = $(e.currentTarget).data('id');
console.log('选中:', id);
}
});
五、动态事件绑定 #
5.1 delegateEvents #
javascript
var UserView = Backbone.View.extend({
events: {
'click .btn': 'onClick'
},
addDynamicEvent: function() {
this.events['click .new-btn'] = 'onNewClick';
this.delegateEvents();
},
removeEvent: function() {
delete this.events['click .btn'];
this.delegateEvents();
}
});
5.2 undelegateEvents #
javascript
var UserView = Backbone.View.extend({
disableEvents: function() {
this.undelegateEvents();
},
enableEvents: function() {
this.delegateEvents();
}
});
5.3 临时禁用事件 #
javascript
var UserView = Backbone.View.extend({
events: {
'click .btn': 'onClick'
},
performAction: function() {
this.undelegateEvents();
this.doSomething();
this.delegateEvents();
}
});
六、事件上下文 #
6.1 this指向 #
事件处理函数中的 this 指向视图实例:
javascript
var UserView = Backbone.View.extend({
events: {
'click .btn': 'onClick'
},
onClick: function(e) {
console.log(this === view);
console.log(this.model.get('name'));
}
});
6.2 保持上下文 #
javascript
var UserView = Backbone.View.extend({
events: {
'click .btn': function(e) {
this.onClick(e);
}.bind(this)
},
onClick: function(e) {
console.log(this.model.get('name'));
}
});
七、实用示例 #
7.1 可编辑视图 #
javascript
var EditableView = Backbone.View.extend({
events: {
'dblclick .title': 'startEdit',
'blur .edit-input': 'endEdit',
'keypress .edit-input': 'saveOnEnter'
},
template: _.template(
'<span class="title"><%= title %></span>'
),
render: function() {
this.$el.html(this.template(this.model.toJSON()));
return this;
},
startEdit: function(e) {
var $title = $(e.currentTarget);
var value = $title.text();
$title.hide();
this.$el.append(
'<input class="edit-input" value="' + value + '">'
);
this.$('.edit-input').focus();
},
endEdit: function(e) {
var $input = $(e.currentTarget);
var value = $input.val().trim();
if (value) {
this.model.set('title', value);
}
$input.remove();
this.$('.title').show();
},
saveOnEnter: function(e) {
if (e.which === 13) {
$(e.currentTarget).blur();
}
}
});
7.2 拖拽视图 #
javascript
var DraggableView = Backbone.View.extend({
events: {
'mousedown': 'startDrag',
'touchstart': 'startDrag'
},
initialize: function() {
this.dragging = false;
$(document).on('mousemove.draggable', this.onDrag.bind(this));
$(document).on('mouseup.draggable', this.endDrag.bind(this));
},
startDrag: function(e) {
e.preventDefault();
this.dragging = true;
this.startX = e.pageX || e.originalEvent.touches[0].pageX;
this.startY = e.pageY || e.originalEvent.touches[0].pageY;
this.startLeft = this.$el.position().left;
this.startTop = this.$el.position().top;
},
onDrag: function(e) {
if (!this.dragging) return;
var pageX = e.pageX || e.originalEvent.touches[0].pageX;
var pageY = e.pageY || e.originalEvent.touches[0].pageY;
var left = this.startLeft + (pageX - this.startX);
var top = this.startTop + (pageY - this.startY);
this.$el.css({ left: left, top: top });
},
endDrag: function() {
this.dragging = false;
},
remove: function() {
$(document).off('.draggable');
Backbone.View.prototype.remove.call(this);
}
});
7.3 表单验证视图 #
javascript
var FormValidationView = Backbone.View.extend({
events: {
'submit': 'onSubmit',
'input input': 'validateField',
'blur input': 'validateField'
},
validateField: function(e) {
var $input = $(e.currentTarget);
var name = $input.attr('name');
var value = $input.val();
var error = this.validate(name, value);
if (error) {
this.showError($input, error);
} else {
this.clearError($input);
}
},
validate: function(name, value) {
var rules = {
name: function(v) {
if (!v) return '姓名不能为空';
if (v.length < 2) return '姓名至少2个字符';
},
email: function(v) {
if (!v) return '邮箱不能为空';
if (!v.match(/^[\w-]+@[\w-]+\.[a-z]+$/i)) return '邮箱格式不正确';
}
};
return rules[name] && rules[name](value);
},
showError: function($input, error) {
$input.addClass('error');
$input.siblings('.error-message').text(error).show();
},
clearError: function($input) {
$input.removeClass('error');
$input.siblings('.error-message').hide();
},
onSubmit: function(e) {
e.preventDefault();
var hasError = false;
var self = this;
this.$('input').each(function() {
var $input = $(this);
var error = self.validate($input.attr('name'), $input.val());
if (error) {
self.showError($input, error);
hasError = true;
}
});
if (!hasError) {
this.save();
}
}
});
八、总结 #
8.1 事件方法 #
| 方法 | 说明 |
|---|---|
| delegateEvents() | 绑定events中定义的事件 |
| undelegateEvents() | 解绑所有事件 |
| $(selector) | 在视图内查找元素 |
8.2 常用事件 #
| 事件 | 说明 |
|---|---|
| click | 单击 |
| dblclick | 双击 |
| keypress | 按键 |
| submit | 表单提交 |
| change | 值变化 |
| focus/blur | 聚焦/失焦 |
8.3 最佳实践 #
- 使用
events对象声明事件 - 事件处理函数中
this指向视图 - 使用
e.stopPropagation()阻止冒泡 - 使用
e.preventDefault()阻止默认行为 - 及时解绑不再需要的事件
最后更新:2026-03-28