Быстрый поиск элементов DOM

В новых браузерах для перемещения по DOM-дереву появилась возможность использовать интерфейс <Element Traversal>, который позволяет искать элементы, исключая текстовые узлы так, как это происходит в Internet Explorer при использовании стандартных firstChild, lastChild, nextSibling и previousSibling, что увеличивает скорость поиска элементов.

В браузерах, поддерживающих "Element Traversal", доступны новые методы:

  • firstElementChild - первый дочерний элементы;
  • lastElementChild - последний дочерний элементы;
  • nextElementSibling - следующий элементы;
  • previousElementSibling - предыдущий элементы;
  • childElementCount - количество дочерних элементов.

Эти методы работают с узлами, у которых nodeType == 1, например, метод childElementCount показывает не сколько всего дочерних узлов, а количество дочерних элементов, т.е. узлов с nodeType == 1.

Все конечно хорошо и удобно, но нельзя забывать о огромной армии пользователей старых браузеров, поэтому напишем функции, которые позволят кросс-браузерно выполнять поиск дочерних элементов.

Для начала проверим, поддерживает ли браузер интерфейс "Element Traversal".

var traversal = typeof document.createElement('div').childElementCount != 'undefined';

В итоге переменная traversal будет иметь значение true или false, опираясь на которое мы будем выбирать, какой из способов перемещения по DOM-дереву использовать.

Следующая функция будет искать первый дочерний элемент:

var firstChild = traversal ? function(node) {
    // для новых браузеров достаточно
    // воспользоваться встроенным методом
    return node.firstElementChild;
} : function(node) {
    // для старых браузеров
    // находим первый дочерний узел
    node = node.firstChild;
    // ищем в цикле следующий узел,
    // пока не встретим элемент с nodeType == 1
    while(node && node.nodeType != 1) node = node.nextSibling;
    // возвращаем результат
    return node;
};

Рассмотрим простой пример использования:

<div id="test">
    text node
    <div>First</div>
    text node
    <div>Last</div>
    text node
</div>
<script>
    var node = document.getElementById('test');
    var text = firstChild(node).innerHTML;
    alert(text); //  First
</script>

Аналогично будет выглядет функция lastChild:

var lastChild = traversal ? function(node) {
    return node.lastElementChild;
} : function(node) {
    node = node.lastChild;
    while(node && node.nodeType != 1) node = node.previousSibling;
    return node;
};

Используем:

text = lastChild(node).innerHTML;
alert(text); //  Last

Назовем функции поиска следующего и предыдущего элемента next и previous соответственно:

var next = traversal ? function(node) {
    return node.nextElementSibling;
} : function(node) {
    while(node = node.nextSibling) if(node.nodeType == 1) break;
    return node;
};
var previous = traversal ? function(node) {
    return node.previousElementSibling;
} : function(node) {
    while(node = node.previousSibling) if(node.nodeType == 1) break;
    return node;
};

И рассмотрим их работу на простеньком примере:

<div>
    text node
    <div id="first">First</div>
    text node
    <div id="last">Last</div>
    text node
</div>

<script>
var text = next(node).innerHTML;
alert(text); //  Last
node = document.getElementById('last');
text = previous(node).innerHTML;
alert(text); //  First
</script>

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

Проверим, поддерживает ли браузер метод children?

var children = typeof document.createElement('div').children != 'undefined';

И напишем функцию поиска:

var child = children ? function(node) {
    return node.children;
} : function(node) {
    var list = node.childNodes,
    length = list.length,
    i = -1,
    array = [];
    while(++i < length)
        if(list[i].nodeType == 1)
            array.push(list[i]);
    return array;
};

Ну и конечно, рассмотрим работу на примере:

<div id="test">
    text node
    <div>First</div>
    text node
    <div>Last</div>
    text node
</div>
<script language=JavaScript>
var node = document.getElementById('test');
var count = child(node).length;
alert(count); //  2
</script>

Все эти методы, но в более функциональном виде, используются в новых версиях JavaScript фреймворка js-core. А также для поиска всех дочерних элементов по имени CSS-класса, атрибутам или просто по имени тега, когда это возможно, используется "Selectors API", что позволяет добиться хороших результатов в тестах на производительность.


.