Достаточно много браузеров (Opera 9.6, Google Chrome 2, Safari 4, Firefox 3.5) обзавелись поддержкой весьма удобного интерфейса ElementTraversal, который позволяет перемещаться по DOM-дереву, игнорируя текстовые узлы. В этих браузерах для каждого элемента стал доступен следующий набор новых getter'ов:
Уточним: элементами называются узлы DOM-дерева, имеющие свойство nodeType равное 1; элементам соответствуют теги.
Использование этих атрибутов должно повысить производительность JavaScript-приложений при перемещении по DOM-дереву, т. к. отпадает необходимость проверять в цикле nodeType узлов и использовать дополнительные функции-обертки. А атрибут childElementCount позволяет узнать, есть ли вообще дочерние элементы у текущего узла, в отличие от практически бесполезного метода hasChildNodes. Конечно, проверить наличие дочерних элементов можно следующим образом:
Все это конечно хорошо и интересно, но что же делать со старыми браузерами, которые не поддерживают интерфейс ElementTraversal? Очевидно, придется написать дополнительные функции для каждого атрибута. Их реализацию вы можете посмотреть в статье «Быстрый поиск DOM-элементов», хотя, думаю, для многих не составит труда написать такие функции самому, ничего сложного в этом нет.
А теперь самое интересное, в Internet Explorer 8 появилась возможность создавать эти самые getter'ы и setter'ы. Странно только, почему ни в одном обзоре о нововведениях IE8 не прозвучало об этом мощном и полезном механизме. В JScript создать getter или setter теперь можно с помощью метода defineProperty встроенного объекта Object. Вспоминаем, что подобная возможность, посредством методов __defineGetter__ и __defineSetter__, изначально была в браузерах на основе Gecko, как оказалось уже и в Safari 3, и в Opera 9.6 внедрили этот механизм.
Итак, имея возможность создавать getter'ы, мы можем внедрить поддержку интерфейса ElementTraversal в Internet Explorer 8, Mozilla Firefox 2+ и Safari 3+, ну а Opera 9.6 и так его поддерживает.
Осталось написать код:
Используются атрибуты нового интерфейса точно так же, как и старые firstChild, lastChild, nextSibling и т. д. Возьмем, к примеру, такой XHTML-код:
Архив с кодом из статьи: ElementTraversal.zip
Update: перенес в тематический блог, спасибо за карму :-)
- firstElementChild — первый дочерний элемент;
- lastElementChild — последний дочерний элемент;
- nextElementSibling — следующий соседний элемент;
- previousElementSibling — предыдущий соседний элемент;
- childElementCount — количество дочерних элементов.
The ElementTraversal interface is a set of read-only attributes…будем называть их далее атрибутами.
Уточним: элементами называются узлы DOM-дерева, имеющие свойство nodeType равное 1; элементам соответствуют теги.
Использование этих атрибутов должно повысить производительность JavaScript-приложений при перемещении по DOM-дереву, т. к. отпадает необходимость проверять в цикле nodeType узлов и использовать дополнительные функции-обертки. А атрибут childElementCount позволяет узнать, есть ли вообще дочерние элементы у текущего узла, в отличие от практически бесполезного метода hasChildNodes. Конечно, проверить наличие дочерних элементов можно следующим образом:
node.getElementsByTagName("*").length
Но такой способ слишком расточителен, встроенный атрибут childElementCount теоретически должен работать гораздо быстрее.Все это конечно хорошо и интересно, но что же делать со старыми браузерами, которые не поддерживают интерфейс ElementTraversal? Очевидно, придется написать дополнительные функции для каждого атрибута. Их реализацию вы можете посмотреть в статье «Быстрый поиск DOM-элементов», хотя, думаю, для многих не составит труда написать такие функции самому, ничего сложного в этом нет.
А теперь самое интересное, в Internet Explorer 8 появилась возможность создавать эти самые getter'ы и setter'ы. Странно только, почему ни в одном обзоре о нововведениях IE8 не прозвучало об этом мощном и полезном механизме. В JScript создать getter или setter теперь можно с помощью метода defineProperty встроенного объекта Object. Вспоминаем, что подобная возможность, посредством методов __defineGetter__ и __defineSetter__, изначально была в браузерах на основе Gecko, как оказалось уже и в Safari 3, и в Opera 9.6 внедрили этот механизм.
Итак, имея возможность создавать getter'ы, мы можем внедрить поддержку интерфейса ElementTraversal в Internet Explorer 8, Mozilla Firefox 2+ и Safari 3+, ну а Opera 9.6 и так его поддерживает.
Осталось написать код:
// Создаем новый элемент для дальнейших проверок<br>var element = document.createElement("div");<br><br>// Проверяем, что браузер не поддерживает ElementTraversal<br>if(typeof element.firstElementChild == "undefined") {<br><br> // Создаем объект с набором методов<br> var ElementTravrsal = {<br><br> // Поиск первого дочернего элемента<br> firstElementChild: function() {<br> // Получаем первый дочерний узел<br> var node = this.firstChild;<br> // Находим следующий соседний узел пока не встретили элемент<br> // или не получили значение null<br> while(node && node.nodeType != 1) node = node.nextSibling;<br> // Возвращаем найденный элемент или null<br> return node;<br> },<br><br> // Поиск последнего дочернего элемента<br> lastElementChild: function() {<br> // Получаем последний дочерний узел<br> var node = this.lastChild;<br> // Находим предыдущий соседний узел пока не встретили элемент<br> // или не получили значение null<br> while(node && node.nodeType != 1) node = node.previousSibling;<br> // Возвращаем найденный элемент или null<br> return node;<br> },<br><br> // Поиск следующего соседнего элемента<br> nextElementSibling: function() {<br> // Объявляем переменную и инициализируем<br> // ее ссылкой на текущий элемент<br> var node = this;<br> // Находим следующий соседний узел пока не встретили элемент<br> // или не получили значение null<br> do node = node.nextSibling<br> while(node && node.nodeType != 1);<br> // Возвращаем найденный элемент или null<br> return node;<br> },<br><br> // Поиск предыдущего соседнего элемента<br> previousElementSibling: function() {<br> // Объявляем переменную и инициализируем<br> // ее ссылкой на текущий элемент<br> var node = this;<br> // Находим предыдущий соседний узел пока не встретили элемент<br> // или не получили значение null<br> do node = node.previousSibling;<br> while(node && node.nodeType != 1);<br> // Возвращаем найденный элемент или null<br> return node;<br> },<br><br> // Определение количества дочерних элементов<br> // Проверяем, что браузер не поддерживает геттер children<br> childElementCount: typeof element.children == "undefined" ? function() {<br> // Браузер не поддерживает children,<br> // поэтому получаем список всех дочерних узлов<br> var list = this.childNodes,<br> // определяем их количество<br> i = list.length,<br> // заводим счетчик элементов<br> j = 0;<br> // Проходя в цикле по всем дочерним узлам,<br> while(i--)<br> // если встретился элемент,<br> if(list[i].nodeType == 1)<br> // увеличиваем счетчик<br> j++;<br> // Возвращаем количество дочерних узлов или 0<br> return j;<br> } : function() {<br> // Браузер поддерживает children,<br> // поэтому получаем список всех дочерних элементов<br> // и возвращаем их количество<br> return this.children.length;<br> }<br> };<br><br> // Создаем геттеры для IE8<br> if(Object.defineProperty)<br> for(var property in ElementTravrsal)<br> if(ElementTravrsal.hasOwnProperty(property))<br> Object.defineProperty(Element.prototype, property, {<br> get: ElementTravrsal[property]<br> });<br><br> // для Firefox 2+ и Safari 3+<br> if(Object.__defineGetter__)<br> for(var property in ElementTravrsal)<br> if(ElementTravrsal.hasOwnProperty(property))<br> HTMLElement.prototype.__defineGetter__(property, ElementTravrsal[property]);<br><br>}
Включив этот код в проект, уже сейчас можно пользоваться ElementTraversal в большинстве браузеров:- Internet Explorer 8;
- Mozilla Firefox 2+ и другие браузеры на основе Gecko 1.8+;
- Opera 9.6+ (может быть и 9.5+);
- Safari 3+ (может даже и 2? К сожалению, нет возможности проверить);
- Google Chrome 2+.
Используются атрибуты нового интерфейса точно так же, как и старые firstChild, lastChild, nextSibling и т. д. Возьмем, к примеру, такой XHTML-код:
<div id="test"><br> TextNode<br> <div>TextNode</div><br> TextNode<br> <p>TextNode</p><br> TextNode<br></div><br>TextNode<br><p>TextNode</p>
И выполним несколько перемещений по DOM-дереву в JavaScript:// Получим ссылку на элемент<br>// с идентифкатором "test"<br>var node = document.getElementById("test");<br><br>// Узнаем tagName следующего элемента<br>alert(node.nextElementSibling.tagName); // → "P"<br><br>// Найдем последний дочерний элемент<br>node = node.lastElementChild;<br><br>// И узнаем, есть у него дочерние элементы?<br>alert(node.childElementCount); // → "0"<br><br>// А вообще дочерние узлы?<br>alert(node.hasChildNodes()); // → "true"
Если было интересно, могу написать подробнее о применении getter'ов и setter'ов в JScript и JavaScript в следующей статье.Архив с кодом из статьи: ElementTraversal.zip
Update: перенес в тематический блог, спасибо за карму :-)