似乎无法清理分离的DOM元素

时间:2013-08-29 21:27:23

标签: javascript jquery jquery-ui backbone.js marionette

我正在使用jquery-ui Tabs,我遇到了删除标签时出现的问题。该选项卡似乎与其内容div一起被删除,但当您查看Chrome DevTools配置文件中的堆(删除选项卡后)时,您将看到选项卡li和div元素仍然存在,但已分离。随着时间的推移,重复添加/移除突片导致这些元件累积。例如,如果添加一个选项卡10次,则会在堆快照中显示10个分离的div元素和10个分离的li元素:

Heap snapshot with detached elements

我有以下观点:

TabLabel = Marionette.ItemView.extend({
    template: "#tab-label",
    tagName: "li",
    events: {
        "click .ui-icon-close": "closeTab"
    },  
    closeTab: function(e) {
        this.$el.contents().remove();
        this.model.collection.remove(this.model);
        $("#main-container").tabs("refresh");
        this.close();
    }   
}); 

TabContainer = Marionette.ItemView.extend({
    template: "#tab-container",
    tagName: "div",
    onBeforeRender: function() {
        this.$el.attr("id", "div-" + this.id);
    },  
    onClose: function() {
        // This removes the region that contains the container
        App.layout.removeRegion(this.containerRegion);
    }   
});

TabLabels = Marionette.CollectionView.extend({
    tagName: "ul"
});

TabContainers = Marionette.CollectionView.extend({
    tagName: "div"
});

视图实例化如下:

tabs = new TabsCollection(); // Create a new collection instance

var tabLabelView = new TabLabels({
    itemView: TabLabel,
    collection: tabs
}); 

var tabContainerView = new TabContainers({
    itemView: TabContainer,
    collection: tabs
}); 

正如您所看到的,视图都引用了相同的集合;集合中的每个模型都可以说代表一个标签(只是模型包含满足jquery-ui标签的必要信息)。这些景观通过Marionette在一个地区展出。布局......相当标准。通过单击选项卡容器中的链接来添加选项卡;所有这一切都是为集合添加另一个模型,然后在主容器上调用选项卡(“刷新”),这将使新选项卡出现。通过单击选项卡右上角的“X”图标可以删除选项卡。

我花了很多时间试图追踪这个泄漏,我无法弄清楚我的代码是否存在问题(我正在关闭视图/模型/等等的方式?)或者如果它是jquery-ui tabs插件中的一个问题。

思考?提前谢谢!

更新#1 根据要求,这里有一个jsfiddle来证明问题 - 只需关闭标签页,您就会看到分离的元素被遗忘。

另外,截图:

enter image description here

更新#2 这似乎是jquery-ui标签小部件中的泄漏。在jquery-ui网站上的widget demonstration中会出现相同的行为。我添加了一些标签,然后关闭它们,果然,它们仍然存在:

enter image description here

我已经使用jQuery UI(1.10.3)和之前版本(1.10.2)的最新版本(在撰写本文时)对此进行了测试。

2 个答案:

答案 0 :(得分:3)

您是否有理由使用this.$el.contents().remove()代替this.$el.empty()

在你的jsFiddle中使用this.$el.empty()似乎可以解决分离的NodeList。

一些记录内存分析:

  • 观看http://www.youtube.com/watch?v=L3ugr9BJqIs
  • 使用3快照方法,2是不够的。那是什么意思?
    • 开始新鲜,隐身模式并刷新
    • 拍摄快照(每次拍摄快照时都会发生强制GC)
    • 做点什么,拍快照(gc)
    • 做类似的事情,拍快照(gc)
    • 将快照2与快照1进行比较,使用+
    • 查找增量
    • 为快照3选择摘要快照1和2之间分配的对象
    • 寻找你发现的比较2和1不应该存在的东西。这些是韭菜

我发现jQuery似乎很惹事的情况,因为他们在执行诸如调用.prevObject之类的操作时会在.add()中保存当前的jQuery对象。也许打电话给.contents()做一些时髦的魔术。

答案 1 :(得分:3)

所以我最终通过做两件事来解决这个问题(很久以前):

  1. 放弃jQueryUI而转而使用Bootstrap
  2. 将closeTab函数(请参阅原始jsFiddle)更改为以下内容:

    closeTab: function (e) {
        e.stopPropagation();
        e.preventDefault();
        this.model.collection.remove(this.model);
        this.close();
    }
    
  3. 在该片段中,stopPropagation和preventDefault实际上是阻止此元素在DOM中保持分离(并在后续添加/删除时累积)的原因。总结一下:这个问题是通过在触发事件对象后不调用stopPropagation / preventDefault创建的。我更新了jsFiddle,以便正确删除标签元素,后代,这是结果的屏幕截图:

    Updated jsFiddle

    如您所见,单击“X”关闭它们后,没有任何标签元素保持分离状态。唯一的分离元素是来自jsFiddle界面的元素。