web-dev-qa-db-ja.com

すべてのDOM要素を反復処理する最も効率的な方法は?

残念ながら、ページのすべてのDOM要素を反復処理する必要があり、最も効率的な手法は何だろうと思っています。私はおそらく自分でこれらのベンチマークを行うことができ、時間があれば可能性がありますが、誰かがすでにこれを経験したか、私が検討していなかったオプションを持っていることを望んでいます。

現在、私はjQueryを使用してこれをしています:

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

動作しますが、クライアントに若干の遅延を引き起こすようです。 $('body', '*')のような、より具体的なjQueryコンテキストで調整することもできます。通常、ネイティブjavascriptはjQueryよりも高速であることがわかりました。

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

ネイティブオプションの方が速いと思います。私が考慮していなかった他のオプションがあるかどうか疑問に思う。子ノードを並列に反復する再帰オプションかもしれません。

62
kevzettler

投稿したVanilla Javascriptの方法は最速です。投稿したjQueryソリューションよりも高速になります(質問に関する私のコメントを参照してください)。ループ内のDOMを削除したり追加したりせず、走査の順序が問題にならない場合は、逆方向に反復することで、速度を少しでも上げることができます。

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

Edit:このベンチマークを確認して、ネイティブコードを使用することでどれだけの時間を節約できるかを確認してください: http://jsben.ch/#/Ro9H6

45
Paulpro

UPDATE:

$('body *')を使用して要素を反復しないでください。 JQueryメソッドを使用する場合は、$('*')を使用する方がはるかに高速です(詳細についてはコメントを参照してください)。


平凡なJavaScriptは比較的高速です。

テストフィドル を使用すると、JQueryで13000要素を処理するのに約30ミリ秒、JavaScript(両方ともChromeでテスト済み)を使用して23000要素を処理するのに8ミリ秒かかります。

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

Difference:  664% in favor of plain ol' JavaScript

注:ページに非常に多くの要素がある場合を除き、これは大きな違いにはなりません。また、ループ内のロジックのタイミングを調整する必要があります。これがすべての制限要因になる可能性があるためです。

更新:

ここ は、より多くの要素(ループごとに約6500)を考慮した場合の更新結果です。JQueryでは1500msで約648000要素、JavaScriptでは170msで658000要素を取得します。 (両方ともChromeでテスト済み):

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

Difference:  895% in favor of plain ol' JavaScript

JQueryがほぼ同じである間にJavaScriptが高速化されたように見えます。

16
Briguy37

一般的には良い考えではありませんが、これはうまくいくはずです:

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

編集済み!

14
Petar Sabev

最速の方法は_document.all_のようです(メソッドではなくプロパティであることに注意してください)。

JQueryの代わりにこれらを記録するようにBriguyの回答のフィドルを変更しましたが、一貫して高速です(document.getElementsByTagName('*')よりも)。

フィドル

6
Camilo Martin

これは、コメントに記載されている問題の解決策です(実際の質問ではありません)。 elementFromPointを使用して固定位置要素を配置する領域をテストし、その領域の要素のみを心配する方がはるかに高速になると思います。例はこちらです:

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

基本的に、探している要素の最小サイズを設定し、新しい固定位置要素が占有する領域全体をスキャンします。そこにあるユニークな要素のリストを作成し、それらの要素のスタイルをチェックすることだけを心配します。

この手法は、探している要素が最も高いz-indexを持っていることを前提としていることに注意してください(これは固定位置の合理的な仮定のようです)。これで十分でない場合は、各要素が検出された後に非表示(または最小Zインデックスを割り当てる)に調整し、(確実に)何も検出されなくなるまでポイントを再度テストし、復元しますその後。これは、感知できないほど高速で発生するはずです。

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});
3
Jamie Treworgy