Близкие отношения узлов в DOM всегда были проблематичными, потому что большинство интерпретаций DOM включают в себя текстовые узлы пробелов , о которых сценарии обычно не заботятся.
Конечно, они должны быть включены, потому что не от реализации зависит, важен ли тот или иной узел. Тем не менее, пробельные текстовые узлы обычно не важны, они просто мешают, усложняя то, что должно быть простыми отношениями, такими как firstChild
и nextSibling
.
Вот простой пример разметки для демонстрации:
<ul> <li>list-item 1</li> <li>list-item 2</li> <li>list-item 3</li> </ul>
Таким образом, firstChild
этого элемента <ul>
— это не первый элемент <li>
, это пробел (то есть разрыв строки и табуляция) между тегами <ul>
и <li>
. Аналогично, nextSibling
этого первого элемента списка — это не второй элемент списка, а промежуточный текстовый узел.
Классическое решение
В этом нет ничего нового, и традиционно у нас было три основных способа борьбы с этим. Во-первых, использовать ссылку на коллекцию, например:
var item = list.getElementsByTagName('li')[0];
Второй подход состоит в том, чтобы выполнить итерации нежелательных узлов , используя тест nodeType
чтобы определить, когда у нас есть нужный нам узел:
var item = list.firstChild; while(item.nodeType != 1) { item = item.nextSibling; }
Третье и наиболее грубое решение состоит в том, чтобы просто полностью удалить ненужные узлы , используя рекурсивную функцию, подобную этой (которая также удаляет узлы комментариев):
function clean(element) { for(var x = 0; x < element.childNodes.length; x ++) { var child = element.childNodes[x]; if(child.nodeType == 8 || (child.nodeType == 3 && !/S/.test(child.nodeValue))) { element.removeChild(element.childNodes[x --]); } if(child.nodeType == 1) { clean(child); } } }
Решение для обхода элемента
Все эти решения работают, но есть гораздо более простой и удобный способ получения ссылок на элементы, которые мы хотим, используя удивительно малоизвестный набор ссылок, определенный в DOM3 Element Traversal .
Спецификация обхода элемента определяет четыре новых ссылки, которые относятся только к узлам элемента, фактически игнорируя все другие типы:
-
firstElementChild
-
lastElementChild
-
nextElementSibling
-
previousElementSibling
Так что теперь мы можем получить эти ссылки на элементы списка гораздо более простым способом, и не имеет значения, сколько пробельных текстовых узлов (или чего-либо еще) находится между ними:
var item = list.firstElementChild; var item2 = item.nextElementSibling;
Спецификация также определяет свойство childNodes.length
, которое эквивалентно childNodes.length
когда все childNodes.length
узлы не учитываются.
Реальное решение?
Так можем ли мы полагаться на эти свойства, будут ли они работать в браузерах, для которых мы кодируем? Ответ «да» по большей части. Старые версии IE — это обычная история, но для IE9 или более поздней версии или любой достаточно свежей версии любого другого крупного браузера мы обнаруживаем, что все эти свойства поддерживаются и уже довольно давно.
Таблицы совместимости DOM PPK дают нам представление о том, что нам не нужно беспокоиться об отсутствии поддержки браузера — если только нам не нужна поддержка IE8 .
Так что я думаю, что это одна из тех вещей, как это было раньше с запросами селекторов — если старые браузеры представляют собой проблему, библиотеки могут предоставить запасной вариант, или вы можете продолжать использовать традиционные решения, на которые мы всегда опирались. Но если вам повезет, что вам не придется задумываться об этих старых браузерах, то свойства Element Traversal, несомненно, облегчат жизнь.
Я также мог бы отметить, что более ранние версии IE имеют другое представление о DOM — в отличие от всех других браузеров, они не включают пробельные текстовые узлы. Таким образом, в крайнем случае, вы всегда можете сделать что-то вроде этого:
function firstChild(element) { //using pre-defined browser variable if(isie) { return element.firstChild; } return element.firstElementChild; }
Для этого подходит тест браузера, а не просто проверка того, определен ли firstElementChild
, поскольку отсутствие поддержки этого свойства не обязательно указывает на реализацию, в которой пробелы не включены. Разница уникальна для IE , поэтому нам нужно проверить IE .
Здравый смысл DOM
Для меня эти свойства обхода элементов — это что-то легкое из здравого смысла в спецификациях W3C — они подтверждают в стандартах практическую точку зрения, присущую большинству DOM . Они, безусловно, намного более доступны, чем DOM2 Traversal когда-либо (кто-нибудь здесь, используя TreeWalker
? Нет, я так не думал!). Основная проблема, которую пытался решить DOM Traversal, заключается в том, что реализации не могут знать, о каких типах узлов будет заботиться скрипт, однако он попытался решить эту проблему, продолжая рассматривать все типы узлов как равные.
Но все узлы не равны — это элементы, которые учитываются — и спецификация обхода элементов ставит их в центр внимания.