在单页应用程序中取消绑定事件侦听器和删除子元素的正确方法是啥?

Posted

技术标签:

【中文标题】在单页应用程序中取消绑定事件侦听器和删除子元素的正确方法是啥?【英文标题】:What is the proper way to unbind event listeners and remove child elements in single page applications?在单页应用程序中取消绑定事件侦听器和删除子元素的正确方法是什么? 【发布时间】:2016-05-29 18:22:08 【问题描述】:

我一直在构建一个大型单页应用程序,最近开始探索 JS 中的内存泄漏。而且我认为我有内存泄漏,因为 - 当我在 Chrome 中使用配置文件(快照)功能时 - 我看到我有很多分离的 DOM 元素。

这是我的设置的简化视图:

<div id="container">
  <div class="buttons">
    <a class=".btn" href="/someurl/"> Button A</a>
    <a class=".btn" href="/someurl/"> Button B</a>
    <a class=".btn" href="/someurl/"> Button C</a>
  </div>

  <div class="ajaxHolder"></div>
</div>

因此,例如,如果用户单击按钮 A,我将使用 AJAX 调用将内容加载到 .ajaxHolder。像这样的:

//This is the content...
<div class="contentA">
  <p>some text...</p>
  <input type="checkbox" class="checkbox">
  <input type="checkbox" class="checkbox">
  <input type="checkbox" class="checkbox">
  <input type="checkbox" class="checkbox">
</div>

我的 MAIN 脚本文件 中还有两个函数。一个是这样的:

//Click event bound to a.btn which tigger the ajax call
$(.buttons).on('click', '.btn', function() 
  //Do ajax here...
  //on success...
  var holder = $(".ajaxHolder");
  holder.children().off().remove(); // Is this the way to do this??
  holder.off().empty(); //I don't think I need .off() here, but I guess it can't hurt...
  holder.append(ajaxSuccessData);
);

到目前为止一切顺利。但是现在加载了内容,我的 MAIN 脚本文件中也有这个函数:

//So here, I am binding an event to the checkboxes...
$(".ajaxHolder").on('change', '.checkbox', function()
  //Do something...
);

所以现在如果用户按下按钮 B,例如,.ajaxHolder 被清空,但所有与我的复选框相关联的事件似乎仍然存在,因为它们出现在我分离的 DOM 树中。

我在这里缺少什么?如何确保在这样运行的单页应用程序中没有任何分离的 DOM 元素?

奖励问题:我有很多这样的事件:

$(".ajaxHolder").on('someEvent...','.someClass....', someFunction());

也就是说,所有东西总是附加到我的.ajaxHolder,因为我必须使用事件委托,因为.ajaxHolder会一直存在——而其他加载的项目不会一直存在,所以我不能简单地做@ 987654332@等

那么这也是正确的方法吗?我可以附加到这个.ajaxHolder 的数量是否有限制,或者这样做没有问题吗?

编辑:这是我的控制台的图像,如果有帮助的话。

【问题讨论】:

使用 jQuery 方法删除元素足以删除所有关联数据,只要数据是使用 jQuery 关联的。您无需手动取消绑定事件。 您可以随心所欲地在 ajaxHolder 上进行委托,但是,随着项目的发展,我发现这很容易失控。如果您有许多相同类型的事件处理程序绑定到它,它也可能会开始影响性能,但是在您开始遇到性能问题之前很久就可能会遇到可维护性问题。 @KevinB 那么,为什么会出现分离的 dom 元素?任何其他原因会保留对已删除项目的引用?而且,对于第二部分,除了将其委托给 ajaxHolder 之外,我还有什么其他选择......我想我可以将它委托给文档,但这只是更多的旅行,并不能解决问题。你的解决方案是什么? jQuery removeempty 都按照 Kevin 所说的进行,它们还删除了所有相关的数据和事件,并很好地清理了您使用 jQuery 等附加的任何内容。 这个问题确实没有很好的解决方案(除了使用 react/angular 等)。每次加载子页面时绑定事件可能难以管理,全局绑定委托事件也是如此,两者都有缺陷。您想要做的是在您为每个子页面加载的 html 中包含 js。 【参考方案1】:

不确定这是否仍然相关,但从控制台图像中我收集到您正在使用引导工具提示,它们似乎持有对 DOM 节点的引用。我建议你通过API调用destroy方法

【讨论】:

【参考方案2】:

jQuery 事件解除绑定和移除元素可以通过以下方法进行:

$.off()

$.remove()

【讨论】:

1) 不要e; 2)这不是一个真正的答案。它是指向网络上两个链接的指针。如果您想改进这一点,请解释 OP 如何使用这些功能来实现其目标。请注意,问题中显示的代码已经同时具有.off().remove()【参考方案3】:

执行此操作的最佳方法是使用 $(selector).off(); 取消绑定所有事件侦听器;方法,然后使用 $(selector).remove(); 删除元素

它将阻止由于事件处理程序而发生的所有内存泄漏。

【讨论】:

OP 的代码中已经有off()remove();这不会增加任何东西。

以上是关于在单页应用程序中取消绑定事件侦听器和删除子元素的正确方法是啥?的主要内容,如果未能解决你的问题,请参考以下文章

如何删除某些元素的所有事件侦听器而不使用其子元素

从 DOM 中移除的元素中取消绑定事件

Flex中,删除事件侦听器,并结合二传手

事件侦听和删除事件——event对象——按钮精灵——默认事件——取消冒泡事件——事件委托——默认触发——onload 图片预加载四个迭代版本

前端面试之js篇

H5_0038:父元素有touch事件,子元素有点击事件,如何实现点击事件