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

Javascript 迭代所有DOM元素的最有效方法,javascript,jquery,performance,dom,optimization,Javascript,Jquery,Performance,Dom,Optimization,不幸的是,我需要迭代一个页面的所有DOM元素,我想知道最有效的技术是什么。我可能会自己做基准测试,如果我有时间的话也可能,但我希望有人已经经历过,或者有一些我没有考虑过的选择 目前我正在使用jQuery并执行以下操作: $('body*')。每个(函数(){ var$this=$(this); //做事 }); 虽然它可以工作,但它似乎会对客户端造成一些延迟。还可以使用更具体的jQuery上下文进行调整,如$('body','*')。我突然想到,本机Javascript通常比jQuery快,我

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

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

$('body*')。每个(函数(){
var$this=$(this);
//做事
});
虽然它可以工作,但它似乎会对客户端造成一些延迟。还可以使用更具体的jQuery上下文进行调整,如
$('body','*')
。我突然想到,本机Javascript通常比jQuery快,我发现:

var items=document.getElementsByTagName(“*”);
对于(变量i=0;i

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

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

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

编辑:检查此基准测试,看看使用本机代码可以节省多少时间:

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

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);
不包括文本节点:

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

编辑

更新:

不要使用
$('body*')
来迭代元素。如果使用JQuery方法,使用
$('*')
会快得多(有关详细信息,请参见注释)


相对而言,纯ol'JavaScript速度要快得多

使用a,我用JQuery处理13000个元素需要30毫秒,用JavaScript处理23000个元素需要8毫秒(都是在Chrome上测试的):

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

更新:

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


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

这是一个解决问题的方法,如评论中所述(虽然不是实际问题)。我认为使用
elementFromPoint
来测试要放置固定位置元素的区域要快得多,只需担心该区域中的元素。例如:

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

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

HTML:

我已经修好了
占位符
两行
JS:

var w=$(窗口).width(),h=$(窗口).height(),
最小宽度=10,
最小高度=10,x,y;
var newFloat=$(“#浮动”),
maxHeight=newFloat.height(),
埃尔,
Uniqueles=[],
我

对于(x=0;x最快的方法似乎是
document.all
(请注意,它是一个属性,而不是一个方法)

我修改了Briguy的答案,将其记录到日志中,而不是jQuery中,而且它的速度始终比
document.getElementsByTagName('*')

.

效率最高:

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

对于(i=0;iYes)来说,普通的DOM方法会更快。但是为什么需要迭代所有元素呢?缓存items.length,这样您就不会在循环的每次迭代中都计算它,但是是的,对于使用DOM调用的循环,会快于。each@jamietre另外,jQuery不能很好地优化“body*”,只优化“body”就可以了但如果您使用“body*”它使用sizzle JS,这意味着它最终会调用
document.queryselectoral('body*')
,但它必须先运行大约200行Javascript代码,然后才能决定这样做,包括正则表达式测试和其他事情。这看起来可能不多,但与
document.body.getElementsByTagName('*')相比,这是一个很好的选择
很多。@jamietre我相信您发现jQuery更快的原因是因为您使用的是
document.queryselectoral('*')
而不是
document.queryselectoral('*')
document.body.queryselectoral('*'))
。我相应地更新了您的测试:@jamietre我正在编写一个包含在用户页面中的第三方库。我不知道他们的设计或标记是什么样的。我们的库注入了一个固定元素(标题栏)这可能会导致与它们的设计重叠。为了防止这种情况,我们移动它们的任何位置:相应地固定元素
JQuery:      432  elements/ms
JavaScript:  3870 elements/ms

Difference:  895% in favor of plain ol' JavaScript
<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>
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});
const allDom = document.all || document.querySelectorAll("*");
const len = allDom.length;
for(let i=0; i<len; i++){
    let a = allDom[i];
}