如何判断节点是在内存中还是在 dom 中?

Posted

技术标签:

【中文标题】如何判断节点是在内存中还是在 dom 中?【英文标题】:How can I tell if a node is in memory or in the dom? 【发布时间】:2012-08-10 05:19:17 【问题描述】:

在将节点附加到主页的 DOM 之前,我对它们进行了很多工作;我需要根据给定节点是否包含在主文档中来执行一些工作。

我目前的方法是通过以下方式向父母走去:

if this.el$.closest("body").length > 0

有没有更合适的方法来做到这一点? (最好不需要遍历所有节点的祖先?)

【问题讨论】:

嗯,这会因我在树中的深度而异,我不知道我为这个项目准备了什么变化:) 看看是否有更合适的测试方法肯定会很有趣,而不是每次都必须遍历节点的祖先 .parent() 怎么样?我刚刚做了一个快速测试,内存中的一个元素没有父元素。或者更快的是javascript.parentNode @MrOBrian 我相当肯定,当您在内存节点上调用appendChild 时,它会正常设置附加节点的parent。如果 OP 在内存中构建树,那还不够。 见this very similar question,我认为那里的解决方案会更快 【参考方案1】:

您有几个选项可以以几种不同的速度执行。

var $document = $(document);
var $element = $("#jq-footer");
var exists;

// Test if the element is within a body
exists = $element.closest("body").length;

// Test if the document contains an element
// wrong syntax, use below instead --> exists = $.contains($document, $element);
exists = $.contains(document.documentElement, $element[0]);

// Test if the element is within a body
$($element).parents().is("body");

// Manually loop trough the elements
exists = elementExists($element[0]);

// Used for manual loop
function elementExists(element) 
    while (element) 
        if (element == document) 
            return true;
        
        element = element.parentNode;
    
    return false;
​

Performance Test

对于这个测试,我复制了大量的 html 进行遍历,我将其中一个 jQuery 页面的源代码复制到一个小提琴中,剥离了所有脚本标签,只留下正文和 html。

您可以随意使用文档而不是“正文”,反之亦然,或者添加更多测试,但它应该可以为您提供总体思路。

编辑

我更新了性能测试以使用 contains 的正确语法,因为前一个不正确并且即使元素不存在也始终返回 true。如果元素存在,则 blow 现在返回 true,但如果您指定不存在的选择器,它将返回 false。

exists = $.contains(document.documentElement, $element[0]);

我还在问题的 cmets 中添加了 MrOBrian 提到的建议替代方案,它再次比包含的速度略快。

不错的一个MrOBrian

编辑

这里是带有所有漂亮图表的 jsPerf performance test

感谢 Felix Kling 发现问题并帮助我修复 jsPerf 测试。

添加了更多来自 cmets 的测试结果,这个真的很好: jsPerf 性能测试:dom-tree-test-exists

【讨论】:

太棒了! $.contains 看起来不错 您可能对这个 jsPerf 比较感兴趣:jsperf.com/dom-tree-test @FelixKling:我尝试设置 jsPerf,但在尝试执行包含代码时得到了TypeError: Object [object Object] has no method 'compareDocumentPosition'.。这是失败测试的链接:http://jsperf.com/closest-vs-contains 不确定如何修复它。所以我改用小提琴中的时间戳:) 问题是您将 jQuery 对象传递给 $.contains,而不是 DOM 节点... @FelixKling:太棒了,我现在修复了测试并将它们添加到答案中,信息越多越好。谢谢,发现很好。【参考方案2】:

现代的普通答案是使用Node.isConnected

let test = document.createElement('p');
console.log(test.isConnected); // Returns false
document.body.appendChild(test);
console.log(test.isConnected); // Returns true

(直接取自 MDN 文档的示例)

【讨论】:

【参考方案3】:

您可以为元素分配一个 ID,然后搜索它:

var id = element.id || generateRandomId(); // some function generating a random string
if(document.getElementById(id) !== null) 
    // element in tree

这是一个performance comparison,其中包括 François 的建议,比较了每个附加和分离元素节点的方法。 Here are the test cases 仅适用于现有节点,以便更好地了解速度差异。

测试结果:

显然使用while 测试分离节点更快,因为它几乎立即终止(在第二次迭代时)。但是如果节点有一个(可能)分离的祖先,那么为节点分配一个 ID 并寻找它似乎是 Chrome 21 中最快的方法。

有趣的是,在 Firefox 14 中,Node#contains [MDN] 方法似乎比其他任何方法都快得多。

由于 Firefox 中从 ID 查找到原生 .contains 的速度提升似乎高于 Chrome 中的性能损失,因此快速函数可能如下所示:

function in_tree(element) 
    if(!element || !element.parentNode)  // fail fast
        return false;
    
    if(element.contains) 
        return document.body.contains(element);
    
    var id = element.id || generateRandomId();
    element.id = id;
    return document.getElementById(id) !== null;

但最终,浏览器之间总会存在差异,所以你必须做出妥协。

【讨论】:

以上是关于如何判断节点是在内存中还是在 dom 中?的主要内容,如果未能解决你的问题,请参考以下文章

如何判断表达式是在编译时还是运行时评估的?

如何判断我的代码是在控制台还是桌面应用程序中运行?

iOS如何判断应用程序是在前台运行还是后台运行?

js如何判断用户是在pc端和还是移动端访问

如何判断是插入还是更新

如何判断一个点是在一条线的右侧还是左侧