10

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

我目前的方法是通过以下方式联系父母:

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

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

4

3 回答 3

7

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

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;
}​

性能测试

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

随意使用文档而不是“正文”,反之亦然或添加更多测试,但它应该给你一个大致的想法。

编辑

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

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

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

好一个MrOBrian

编辑

这是带有所有漂亮图表 的 jsPerf性能测试。

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

从评论中添加了更多的测试结果,这个真的很好:
jsPerf性能测试:dom-tree-test-exists

于 2012-08-13T23:31:10.813 回答
7

现代的香草答案是使用Node.isConnected

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

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

于 2020-09-08T12:42:24.717 回答
4

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

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

这是一个性能比较,包括 François 的建议,比较了附加和分离元素节点的每种方法。以下是仅针对现有节点的测试用例,以便更好地了解速度差异。

测试结果:

显然,使用 测试分离节点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;
}

但最终,浏览器之间总会存在差异,因此您必须做出妥协。

于 2012-08-13T23:31:31.997 回答