самый эффективный способ перебора всех элементов DOM? - PullRequest
62 голосов
/ 05 января 2012

К сожалению, мне нужно перебрать все 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
    }

Я предполагаю, что нативный вариант быстрее. Интересно, есть ли другие варианты, которые я не рассматривал. Может быть, рекурсивная опция, которая перебирает дочерние узлы параллельно.

Ответы [ 5 ]

45 голосов
/ 05 января 2012

Ванильный Javascript, как вы разместили, является самым быстрым. Это будет быстрее, чем решение jQuery, которое вы разместили (см. Мой комментарий к вопросу). Если вы не удаляете или не добавляете что-либо в DOM в своем цикле и порядок обхода не имеет значения, вы также можете немного ускорить его, выполнив итерацию в обратном порядке:

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

Редактировать : проверьте этот тест, чтобы увидеть, сколько времени вы можете сэкономить, используя собственный код: http://jsben.ch/#/Ro9H6

16 голосов
/ 05 января 2012

ОБНОВЛЕНИЕ:

Не используйте $('body *') для перебора элементов. Будет гораздо быстрее использовать $('*'), если вы выберете метод JQuery (подробности см. В комментариях).


Обычный JavaScript намного быстрее, условно говоря.

Используя тестовую скрипку , я получаю около 30 мс для обработки 13000 элементов с помощью JQuery и 8 мс для обработки 23000 элементов с помощью JavaScript (оба протестированы в Chrome):

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

Difference:  664% in favor of plain ol' JavaScript

Примечание: Если у вас на странице невероятно большое количество элементов, это не будет иметь большого значения. Кроме того, вам, вероятно, следует рассчитать логику в вашем цикле, поскольку это может быть ограничивающим фактором во всем этом.

Обновление:

Здесь - это обновленные результаты при рассмотрении гораздо большего количества элементов (около 6500 на цикл), я получаю около 648000 элементов за 1500 мс с JQuery и 658000 элементов за 170 мс с JavaScript. (оба проверены на Chrome):

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

Difference:  895% in favor of plain ol' JavaScript

Похоже, JavaScript ускорился, а JQuery остался примерно таким же.

14 голосов
/ 05 января 2012

Обычно это не очень хорошая идея, но она должна работать:

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

Отредактировано!

6 голосов
/ 16 мая 2013

Самый быстрый способ - document.all (обратите внимание, что это свойство, а не метод).

Я изменил скрипту ответа Briguy, чтобы записывать их вместо jQuery, и это постоянно быстрее (чем document.getElementsByTagName('*')).

Скрипка .

3 голосов
/ 05 января 2012

Это решение проблемы, как описано в комментариях (хотя не фактический вопрос). Я думаю, что было бы намного быстрее использовать elementFromPoint для проверки области, в которую вы хотите поместить элемент с фиксированным положением, и беспокоиться только об элементах в этой области. Пример здесь:

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

По сути, просто установите минимально возможный размер элемента, который вы ищете, и отсканируйте всю область, которую хочет занять ваш новый элемент с фиксированной позицией. Создайте список уникальных элементов, найденных там, и вам нужно только проверить стиль этих элементов.

Обратите внимание, что этот метод предполагает, что элемент, который вы ищете, имеет самый высокий z-индекс (что кажется разумным предположением для фиксированной позиции). Если этого недостаточно, то его можно настроить, чтобы скрыть (или назначить минимальный 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});
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...