JavaScript: document.getElementById низкая производительность? - PullRequest
15 голосов
/ 11 ноября 2009

Я многократно использую document.getElementById много на общих элементах CSS.

Был бы значительный выигрыш в производительности, если бы я создал global array для хранения всего моего элемента document.getElementById вместо того, чтобы каждый раз обновлять элемент?

Пример, вместо:

document.getElementById("desc").setAttribute("href", "#");
document.getElementById("desc").onclick = function() {...};
document.getElementById("desc").style.textDecoration = "none"
document.getElementById("asc").setAttribute("href", "#");
document.getElementById("asc").onclick = function() {...};
document.getElementById("asc").style.textDecoration = "none"
...

Чтобы просто сделать:

var GlobalElementId = [];
GlobalElementId ["desc"] = document.getElementById("desc");
GlobalElementId ["asc"] = document.getElementById("asc");
GlobalElementId [...] = document.getElementById(...);

GlobalElementId ["desc"].setAttribute("href", "#");
GlobalElementId ["desc"].onclick = function() {...};
GlobalElementId ["desc"].style.textDecoration = "none"
...

Ответы [ 10 ]

21 голосов
/ 11 ноября 2009

Так что все ответы "да" доставляли мне неприятности, поэтому я на самом деле рассчитал это, чтобы увидеть, было ли getElementById медленным! Вот результаты ( для страницы с 10 000 элементов на ней ):

IE8 getElementById: 0,4844 мс
Поиск в массиве идентификаторов IE8: 0,0062 мс

Chrome getElementById: 0,0039 мс
Поиск в массиве идентификаторов Chrome: 0,0006 мс

Firefox 3.5 был сопоставим с хромом.

Полмисекунды на вызов функции не заставят меня использовать массив;) Но, может быть, это хуже на IE6, который я не установил.

Вот мой сценарий:

<html>
<head>
<script type="text/javascript">
    var numEles = 10000;
    var idx = {};

    function test(){
        generateElements();
        var t0 = (new Date()).getTime();
        var x = selectElementsById();
        var t1 = (new Date()).getTime();
        var time = t1 - t0;
        generateIndex();
        var t2 = (new Date()).getTime();
        var x = selectElementsWithIndex();
        var t3 = (new Date()).getTime();
        var idxTime = t3 - t2;

        var msg = "getElementById time = " + (time / numEles) + " ms (for one call)\n"
            + "Index Time = " + (idxTime/ numEles) + " ms (for one call)";
        alert(msg);
    }

    function generateElements(){
        var d = document.getElementById("mainDiv");
        var str = [];
       for(var i=0;i<numEles;i++){
           str.push("<div id='d_" + i + "' >" + i + "</div>");
        }
        d.innerHTML = str.join('');
    }

    function selectElementsById(){
        var eles = [];
        for(var i=0;i<numEles;i++){
            var id = ((i * 99) % numEles);
            eles.push(document.getElementById("d_" + id));
        }
        return eles;
    }

    function generateIndex(){
        for(var i=0;i<numEles;i++){
            var id = "d_" + i;
           idx[id] = document.getElementById(id);
        }
    }

    function selectElementsWithIndex(){
        var eles = [];
        for(var i=0;i<numEles;i++){
            var id = ((i * 99) % numEles);
            eles.push(idx["d_" + id]);
        }
        return eles;
    }   
</script>
</head>
<body onload="javascript:test();" >
<div id="mainDiv" />
</body>
</html>
7 голосов
/ 11 ноября 2009

Поскольку вы говорите «элементы CSS», я подозреваю, что большая часть вашей низкой производительности связана не с повторяющимся использованием document.getElementById() (которого вам все равно следует избегать), а скорее с тем, сколько раз вы изменяете объект style для данный узел.

Каждый раз, когда вы изменяете свойство в style, вы заставляете браузер перерисовывать этот элемент и, возможно, многие другие на странице.

var elem = document.getElementById( 'desc' );
elem.style.textDecoration = "none"; // browser re-draw
elem.style.borderWidth    = "2px";  // browser re-draw
elem.style.paddingBottom  = "5px";  // browser re-draw

Здесь лучшим решением является использование классов CSS и переключение или добавление / удаление имени класса из узла. Это позволяет упаковать столько изменений стиля, сколько вы хотите, за счет одного перерисовки.

var elem = document.getElementById( 'desc' );
elem.className = "whatever"; // Only one browser re-draw!
3 голосов
/ 11 ноября 2009

Для меня это было бы более уместно и хорошо для производительности:

var desc = document.getElementById("desc");
var asc = document.getElementById("asc");
desc.setAttribute("href","#");
asc.onclick = function() { ... }
...

Пересмотрев сказанное ChaosPandion, я думаю, вы могли бы сделать это одним способом:

var elements = ["desc", "asc", ...];
for(var i = 0; i < elements.length; i++) {
  GlobalElementId[elements[i]] = document.getElementById(elements[i]);
}
2 голосов
/ 11 ноября 2009

Да!

Не так давно была ситуация, когда я получал элементы, модифицирующие низкую производительность. Решением было создать словарь, подобный вашему примеру. Я буквально улучшил производительность в 1000 раз (по крайней мере, в IE6).

var elementCache = {};
function buildElementCache() {
    elementCache[id] = {
        element1: document.getElementById(id + "1"),
        element2: document.getElementById(id + "2")
    } 
    // Etc...   
}
1 голос
/ 11 ноября 2009

Короткий ответ: да, в любое время вы можете сделать переменную Javascript или ссылку на объект локальной, это поможет с производительностью.

Если вы хотите глубже понять управление областью действия и его влияние на производительность для Javascript, техническая беседа Speed ​​Up Your Javascript содержит действительно хорошую информацию. Настоятельно рекомендуется просмотр.

1 голос
/ 11 ноября 2009

Зависит от определения «значительный». Доступ к массиву GlobalElementId.asc намного быстрее, чем вызов getElementById(). Но getElementById все еще очень быстр по сравнению с большинством других манипуляций с DOM, которые, вероятно, будет выполнять ваш скрипт, и, по всей вероятности, это лишь очень малая доля времени выполнения вашего скрипта.

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

Примечание: не используйте setAttribute, оно содержит ошибки в IE и менее читабельно, чем просто использование свойств HTML DOM Level 1, таких как element.href= '...';.

0 голосов
/ 22 июня 2011

В браузерах IE ответ ДА!

Я сделал тест (по аналогии с Майком Бландфордом) и обнаружил, что когда вы вызываете document.getElementById () в браузере IE, он пересекает DOM до тех пор, пока не найдет элемент с желаемым идентификатором, вместо того, чтобы сохранить карту / хэш-таблицу идентификатора элемента. (отвратительно, я знаю).

Таким образом, создание массива, как вы предложили, будет КРАЙНИМ улучшением производительности.

0 голосов
/ 11 ноября 2009

Нет, значительного прироста производительности не будет. Ваши проблемы с производительностью лежат в другом месте. Браузер имеет собственный индекс для элемента id -> element element.

Если вы хотите выяснить, почему ваш код медленный, очень важно рассчитать время, потому что медленная часть, вероятно, не та, которую вы ожидаете (я нашел это трудным путем). Вы можете сделать это так:

var t0 = (new Date()).getTime();
var t1 = (new Date()).getTime();
var time = t1 - t0;

Хотя важно отметить, что точность здесь составляет 15 мс, а это означает, что если что-то занимает 14 мс, в некоторых браузерах оно может быть равно 0 мс.

Вот как будет выглядеть ваш код в jQuery:

$("#desc").attr("href", "#")
    .click(function(){})
    .css("text-decoration", "none");
0 голосов
/ 11 ноября 2009

Это называется кешированием объектов, и это повысит производительность вашего скрипта.
Подробнее см. http://www.javascriptkit.com/javatutors/efficientjs.shtml.
Кроме того, если вы часто меняете CSS, я бы предложил использовать jQuery, как предлагает @ Fahad.

0 голосов
/ 11 ноября 2009

Да, но использование массива перестарается.

См. Ответ Суфианы Хассу о том, как это сделать.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...