博客专区 > cyper 的博客 > 博客详情
cyper 发表于2年前
  • 发表于 2年前
  • 阅读 61
  • 收藏 0
  • 点赞 0
  • 评论 0


摘要: 删除元素使用.remove()和.detach()。两者的区别在于,前者不保留被删除元素的事件,后者保留,有利于重新插入文档时使用。 清空元素内容(但是不删除该元素)使用.empty()。


<!DOCTYPE html>
	<script src="https://code.jquery.com/jquery-1.11.3.js"></script>

	<meta charset="utf-8">
	<title>JS Bin</title>
		var el = $("<div>lol</div>");

		el.click(function() {






While fixing some bugs within my Backbone.LayoutManager plugin, I found an odd behavior in the way I encouraged users to attach a rendered layout into the DOM. Since you can reuse a layout and it's simply a DIV node created by Backbone, I figured it was sufficient to simply have a user call jQuery's html method to inject it into the correct container.

An example of what that code looks like:

// A callback function would fire with a DOM Node
callback(function(domNode) {

This code looks completely valid and not likely to cause any immediate problems, however this is not the case. The issue is that all events would be removed upon a second render. So the first call to $("body").html(el) would work exactly as expected with all events functioning, but the second call to $("body").html(el) would result in no events firing.

I brought this issue to the attention of jQuery committer Dave Methvin who insisted that this was not a bug and that inserting a DOM element into the DOM using the jQuery html function was not a supported signature in the API.

This was confusing to me as I have always seen and used the html function in this way. After learning from Dave that I should be using $("body").empty().append(el), I went back to my code and implemented swapping using empty.

Unfortunately, no dice.

The events were still disappearing, so I made a reduced test case that looked something like this:

var el = $("<div>lol</div>");
el.click(function() {

Since the following code yields missing events, this proved to me that something was happening inside the empty method. Sure enough digging into that function yields the following code:

// Remove element nodes and prevent memory leaks
if ( elem.nodeType === 1 ) {
  jQuery.cleanData( elem.getElementsByTagName("*") );

The call to jQuery.cleanData removes all events from the element, in this case the "body" element. Since this is a shared reference to the exact same DOM node, that means when you re-attach to the DOM a second time it's not going to get it's events back.

Problem detected and understood, but how to fix?

Luckily the fix is really simple and is now implemented inside of the LayoutManager plugin. Since the call to empty or html only remove events from elements inside the DOM we can easily "detach" using the jQuery detach method in between renders. The updated working code from the reduced test case looks like this:

var el = $( "<div>lol</div>");
el.click( function() {
   alert( "hi"); 
$( "body").empty().append(el);
$( "body").empty().append(el);

This may be a very obvious problem and solution to many developers, but it bit me and I've talked to many other developers lately who have been having this same issue while using Backbone.js. It may be that many client side developers do not reuse the same DOM node, avoiding this issue.

##评论中的进一步解释: detach works because it removes the element from the DOM completely, but retains any data set on it by jQuery, which includes events. Since its removed from the DOM, empty doesn't see the element and doesn't remove the events. If you used remove instead of detach events would be removed there as well.

  • 打赏
  • 点赞
  • 收藏
  • 分享
共有 人打赏支持
粉丝 56
博文 619
码字总数 143128
* 金额(元)
¥1 ¥5 ¥10 ¥20 其他金额
* 支付类型