jQuery托管事件是个好东西,减少侦听器的数量,还能降低内存泄露的风险,尤其在列表类的应用比较常见。
想象一个如右图所示的下载列表,点击各列表项会展开详情,用户可以在里面查看详细信息;不展开的话也可以直接点击右边的下载按钮,就会直接下载应用。使用jQuery来写代码大概是这样的:
$('#app-list li').on('click', function (event) {
// 显示/隐藏详情
$(this).toggleClass('active');
});
$('#app-list .download-button').on('click', function (event) {
// 下载通过超链进行
// 这时为了不让详情展开,需要阻止事件冒泡
event.stopPropagation();
});
假如列表里有20个选项,那么这样就会添加40个侦听器。在PC浏览器里这样做问题不大,但是移动设备内存比较少的话可能会引发问题。另外,如果我们还引入了“上拉读取更多”和“下拉刷新”的功能,那么就要为内容的更新添加更多的代码,以避免内存泄露。所以这时,就应当将事件托管到ul去处理。( 关于jQuery事件托管可以参考其文档(http://api.jquery.com/delegate/)。)
$('#app-list').on('click', 'li', function (event) {
$(this).toggleClass('active');
});
$('#app-list').on('click', '.download-button', function (event) {
event.stopPropagation();
});
可以看到,我没有修改事件处理函数。这样做,我们只添加了2个侦听器,并且不管#app-list
里的内容如何变化,都不需要重新注册侦听器。这给代码减了不少负。
不过接下来我发现,点击按钮后,详情仍然会被展开收起,似乎阻止冒泡的代码没有生效。仔细一想,对了,我已经把事件注册到#app-list
上了,所有的事件其实都是冒泡上来的,所以点击按钮,冒泡必然经过上层节点。于是继续修改代码,改变事件处理函数的逻辑:
$('#app-list').on('click', 'li', function (event) {
// 如果事件始于下载按钮,则退出
if ($(event.target).is('.download-button')) {
return;
}
$(this).toggleClass('active');
});
这样便万事大吉了。
欢迎吐槽,共同进步