使用jQuery托管事件对阻止冒泡的影响

使用jQuery托管事件后,所有事件会冒泡到最上面,于是在子节点阻止冒泡就不行了,需要修改部分代码。

列表范例
移动App中常见的列表

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');
});

这样便万事大吉了。

作者: meathill

爱编程,爱旅游,爱吐槽。 今年的第一目标是成为一名优秀的讲师,做够 25 场直播,收集 1000 位听众! (12/25) 《Electron + Vue 实战开发》创作中……

《使用jQuery托管事件对阻止冒泡的影响》有一个想法

欢迎吐槽,请勿装死