迭代所有DOM元素的最有效方法?

时间:2012-01-05 17:42:45

标签: javascript jquery performance dom optimization

不幸的是我需要遍历页面的所有DOM元素,我想知道最有效的技术是什么。我可以自己对这些进行基准测试,如果我有时间可能,但我希望有人已经经历过这个或有一些我没有考虑的选择。

目前我正在使用jQuery并执行此操作:

$('body *').each(function(){                                                                                                                            
      var $this = $(this);                                                                                                                                
      //do stuff                                                                                                                                         
});

虽然它有效,但它似乎会导致客户端出现一些延迟。它也可以使用更具体的jQuery上下文进行调整,例如$('body', '*') 在我看来,本机javascript通常比jQuery更快,我发现了这一点。

var items = document.getElementsByTagName("*");
    for (var i = 0; i < items.length; i++) {
        //do stuff
    }

我假设原生选项更快。想知道是否有其他选择我没有考虑过。也许是一个递归选项,可以并行迭代子节点。

6 个答案:

答案 0 :(得分:45)

您发布的Vanilla Javascript方式最快。它会比您发布的jQuery解决方案更快(请参阅我对该问题的评论)。如果你没有在你的循环中删除或添加任何东西,并且遍历的顺序无关紧要,你也可以通过反向迭代来加速它:

var items = startElem.getElementsByTagName("*");
for (var i = items.length; i--;) {
    //do stuff
}

修改:检查此基准测试,了解使用本机代码可以节省多少时间:http://jsben.ch/#/Ro9H6

答案 1 :(得分:16)

更新:

不要使用$('body *')迭代元素。如果你选择JQuery方法,那么使用$('*')要快得多(详见评论)。


相对来说,相对来说,JavaScript很快。

使用test fiddle,使用JQuery处理13000个元素大约需要30ms,使用JavaScript处理23000个元素需要8ms(两者都在Chrome上测试):

JQuery:      433  elements/ms
JavaScript:  2875 elements/ms

Difference:  664% in favor of plain ol' JavaScript

注意:除非您的网页上有大量的元素,否则这不会产生太大的影响。此外,您可能应该在循环中计算逻辑,因为这可能是所有这些中的限制因素。

<强>更新

Here是考虑更多元素(每个循环大约6500)的更新结果,我使用JQuery在1500ms内获得大约648000个元素,在使用JavaScript的170ms中获得658000个元素。 (均在Chrome上测试过):

JQuery:      432  elements/ms
JavaScript:  3870 elements/ms

Difference:  895% in favor of plain ol' JavaScript

看起来JavaScript加快了,而JQuery保持不变。

答案 2 :(得分:14)

一般来说这不是一个好主意,但这应该有效:

function walkDOM(main) {
    var arr = [];
    var loop = function(main) {
        do {
            arr.push(main);
            if(main.hasChildNodes())
                loop(main.firstChild);
        }
        while (main = main.nextSibling);
    }
    loop(main);
    return arr;
}
walkDOM(document.body);

不包括textnodes:

function walkDOM(main) {
    var arr = [];
    var loop = function(main) {
        do {
            if(main.nodeType == 1)
                arr.push(main);
            if(main.hasChildNodes())
                loop(main.firstChild);
        }
        while (main = main.nextSibling);
    }
    loop(main);
    return arr;
}

编辑!

答案 3 :(得分:6)

最快的方式似乎是document.all(注意它是属性,而不是方法)。

我已经修改了Briguy的回答来解决这些问题,而不是jQuery,并且它一直更快(比document.getElementsByTagName('*'))。

The fiddle

答案 4 :(得分:3)

这是评论中描述的问题的解决方案(尽管不是实际问题)。我认为使用elementFromPoint来测试你想要放置固定位置元素的区域要快得多,并且只担心该区域中的元素。这里有一个例子:

http://jsfiddle.net/pQgwE/4/

基本上,只需设置您正在寻找的元素的最小可能大小,并扫描新固定位置元素想要占用的整个区域。构建一个在那里找到的独特元素的列表,并且只担心检查这些元素的样式。

请注意,此技术假设您要查找的元素具有最高的z-index(这似乎是固定位置的合理假设)。如果这还不够好,那么可以调整它以便在每个元素被发现后隐藏(或指定最小的z-index)并再次测试该点,直到找不到更多(确定),然后恢复他们之后。这应该发生得如此之快以至于难以察觉。

HTML:

<div style="position:fixed; left: 10px; top: 10px; background-color: #000000; 
    color: #FF0000;">I Am Fixed</div>
<div id="floater">OccupyJSFiddle!<br>for two lines</div>

JS:

var w = $(window).width(), h=$(window).height(),
    minWidth=10,
    minHeight=10, x,y;

var newFloat = $('#floater'), 
    maxHeight = newFloat.height(),
    el, 
    uniqueEls=[],
    i;

for (x=0;x<w;x+=minWidth) {
    for (y=0;y<h&& y<maxHeight;y+=minHeight) {
        el = document.elementFromPoint(x,y);
        if (el && $.inArray(el,uniqueEls)<0) {
            uniqueEls.push(el);
        }
    }
}
// just for the fiddle so you can see the position of the elements 
// before anything's done
// alert("click OK to move the floater into position.");
for (i=0;i<uniqueEls.length;i++) {
    el = $(uniqueEls[i]);
    if (el.css("position")==="fixed") {
        el.css("top",maxHeight+1);
    }
}

newFloat.css({'position': 'fixed',
             'top': 0,
             'left': 0});

答案 5 :(得分:1)

最有效:

const allDom = document.all || document.querySelectorAll("*");
const len = allDom.length;
for(let i=0; i<len; i++){
    let a = allDom[i];
}

https://jsben.ch/FwPzW