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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/8747086/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-24 07:19:07  来源:igfitidea点击:

most efficient way to iterate over all DOM elements?

javascriptjqueryperformancedomoptimization

提问by kevzettler

Unfortunately I need to iterate over all the DOM elements of a page and i'm wondering what the most efficient technique is. I could probably benchmark these myself and might if I have the time but i'm hoping someone has already experienced this or has some options I hadn't considered.

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

currently i'm using jQuery and doing this:

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

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

While it works, It seems to cause some lag on the client. It could also be tweaked with a more specific jQuery context like $('body', '*')It occurred to me that native javascript is usually faster than jQuery and I found this.

虽然它有效,但似乎对客户端造成了一些滞后。它也可以使用更具体的 jQuery 上下文进行调整,例如$('body', '*')我发现原生 javascript 通常比 jQuery 快,我发现了这一点。

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

I'm assuming the native option is faster. Wondering if there are other options I hadn't considered. Maybe a recursive option that iterates over child nodes in parallel.

我假设本机选项更快。想知道是否还有其他我没有考虑过的选择。也许是并行迭代子节点的递归选项。

采纳答案by Paul

The Vanilla Javascript way you posted is the fastest. It will be faster than the jQuery solution you posted (See my comment on the question). If you're not removing or adding anything to the DOM in your loop and order of traversal doesn't matter, you can also speed it up ever so slightly by iterating in reverse:

您发布的 Vanilla Javascript 方式是最快的。它将比您发布的 jQuery 解决方案更快(请参阅我对问题的评论)。如果您没有在循环中删除或添加任何内容到 DOM 并且遍历顺序无关紧要,您还可以通过反向迭代稍微加快速度:

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

Edit: check this benchmark to see how much time you can save by using the native code: http://jsben.ch/#/Ro9H6

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

回答by Briguy37

UPDATE:

更新:

Don't use $('body *')to iterate over the elements. It will be much quicker to use $('*')if you go for the JQuery method (see comments for details).

不要$('body *')用于迭代元素。$('*')如果您使用JQuery 方法,使用起来会快得多(有关详细信息,请参阅评论)。



Plain ol' JavaScript is much faster, relatively speaking.

相对而言,普通的 ol' JavaScript 要快得多。

Using a test fiddle, I get about 30ms to process 13000 elements with JQuery, and 8ms to process 23000 elements using JavaScript (both tested on Chrome):

使用测试小提琴,我用 JQuery 处理 13000 个元素需要大约 30 毫秒,使用 JavaScript 处理 23000 个元素需要 8 毫秒(均在 Chrome 上测试):

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

Difference:  664% in favor of plain ol' JavaScript

Note:Unless you have an incredibly large amount of elements on your page, this isn't going to make much of a difference. Also, you probably should time the logic in your loop, as that might be the limiting factor in all this.

注意:除非你的页面上有大量的元素,否则这不会有太大的不同。此外,您可能应该为循环中的逻辑计时,因为这可能是所有这些的限制因素。

Update:

更新:

Hereis the updated results when considering much more elements (about 6500 per loop), I get about 648000 elements in 1500ms with JQuery, and 658000 elements in 170ms with JavaScript. (both tested on Chrome):

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

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

Difference:  895% in favor of plain ol' JavaScript

Looks like JavaScript sped up while JQuery stayed about the same.

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

回答by Petar Sabev

It's not a good idea generally but this should work:

这通常不是一个好主意,但这应该有效:

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);

Not including 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;
}

Edited!

已编辑!

回答by Camilo Martin

The fastest way seems to be document.all(note that it's a property, not a method).

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

I've modified the fiddle of Briguy's answer to log these instead of jQuery, and it's consistently faster (than document.getElementsByTagName('*')).

我已经修改了 Briguy 的答案的小提琴,以记录这些而不是 jQuery,并且它始终比 更快(比document.getElementsByTagName('*'))。

The fiddle.

小提琴

回答by Jamie Treworgy

This is a solution to the problem as described in the comments (though not the actual question). I think it would be much faster the use elementFromPointto test the area where you want to put your fixed-position element, and only worry about elements in that area. An example is here:

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

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

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

Basically, just set some minimum possible size of an element you're looking for, and scan the entire area that your new fixed position element wants to occupy. Build a list of unique elements found there, and only worry about checking the style of those elements.

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

Note that this technique assumes that the element you're looking for has the highest z-index (which seems a reasonable assumption for fixed position). If this is not good enough, then this could be adjusted to hide (or assign a minimum z-index) to each element after it's been discovered and test the point again, until nothing more is found (to be sure), and then restore them afterwards. This ought to happen so fast as to be imperceptible.

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

HTML:

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:

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