Лучший способ получить дочерние узлы


233

Мне было интересно, JavaScript предлагает множество методов для получения первого дочернего элемента из любого элемента, но какой из них лучший? Под лучшими я имею в виду: кросс-браузерную совместимость, быструю, наиболее полную и предсказуемую, когда дело доходит до поведения. Список методов / свойств, которые я использую как псевдонимы:

var elem = document.getElementById('container');
var child = elem.children[0];
var child = elem.firstElementChild; // == children[0]

Это работает для обоих случаев:

var child = elem.childNodes[0]; // or childNodes[1], see below

Это в случае форм или <div>итерации. Если я могу встретить текстовые элементы:

var child = elem.childNodes; // treat as NodeList
var child = elem.firstChild;

Насколько я могу понять, firstChildиспользует NodeList из childNodesи firstElementChildиспользует children. Я основываю это предположение на ссылке MDN:

childNodeявляется ссылкой на первый дочерний элемент узла элемента или, nullесли его нет.

Я предполагаю, что с точки зрения скорости, разница, если таковая имеется, будет почти нулевой, поскольку firstElementChildфактически является ссылкой children[0]на childrenобъект , и объект в любом случае уже находится в памяти.

Что бросает меня, это childNodesобъект. Я использовал его, чтобы взглянуть на форму, на элемент таблицы. Хотя childrenперечисляет все элементы формы, childNodesтакже, кажется, включает пробелы из кода HTML:

console.log(elem.childNodes[0]);
console.log(elem.firstChild);

Оба журнала <TextNode textContent="\n ">

console.log(elem.childNodes[1]);
console.log(elem.children[0]);
console.log(elem.firstElementChild);

Все бревно <input type="text"... >. Как придешь? Я бы понял, что один объект позволил бы мне работать с «сырым» HTML-кодом, в то время как другой придерживается DOM, но этот childNodesэлемент работает на обоих уровнях.

Возвращаясь к моему первоначальному вопросу, я могу предположить, что если я хочу получить наиболее полный объект, childNodesэто путь, но из-за его полноты он может быть не самым предсказуемым с точки зрения возврата элемента, который я хочу / ожидать в любой момент. Кросс-браузерная поддержка также может оказаться проблемой в этом случае, хотя я могу ошибаться.

Кто-нибудь может прояснить различие между предметами под рукой? Если есть разница в скорости, как бы она ни была незначительна, я бы тоже хотел знать. Если я вижу все это неправильно, не стесняйтесь обучать меня.


PS: Пожалуйста, пожалуйста, мне нравится JavaScript, так что да, я хочу иметь дело с такого рода вещами. Ответы типа «jQuery занимается этим для вас» - не то, что я ищу, поэтому нет тег.


50
>> Пожалуйста, пожалуйста, мне нравится JavaScript, так что да, я хочу иметь дело с такого рода вещами. ответы типа «jQuery справляется с этим для вас» - не то, что я ищу << upvotes для этого
david.barkhuizen

Ответы:


211

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

В каждой коллекции нет ничего непредсказуемого, хотя следует учитывать несколько моментов:

  • IE <= 8 не включает текстовые узлы только для пробелов в childNodesто время как другие браузеры делают
  • IE <= 8 включает узлы комментариев внутри, в childrenто время как другие браузеры имеют только элементы

children, firstElementChildИ друзья просто удобство, представляющее отфильтрованный вид DOM ограничивается только элементами.


Похоже, что меня по-прежнему беспокоит одно: текстовый узел пробела, на который указывает childNodes, на самом деле представляет собой просто новую строку и несколько вкладок в коде. Это связано с доктриной XHTML? это можно решить, заключив пробел в структуру кода внутри тегов?
Элиас Ван Отегем

@EliasVanOotegem: Это не имеет отношения к doctype. Пустые текстовые узлы всегда включаются в DOM (кроме IE <9) как для HTML, так и для XHTML, где бы они ни появлялись в источнике. Обычно это хорошая вещь, хотя она может вас поймать, если вы не готовы к этому.
Тим Даун

Я имел в виду, если это поможет написать мою разметку, как это <td\n\t>content</td>. Как вы могли бы сделать с XML, чтобы избежать лишних пробелов, рассматриваемых как часть данных (или DOM, в данном случае). Почему пробелы внутри тега должны быть включены в DOM?
Элиас Ван Отегем

@EliasVanOotegem: О, я вижу. Пробелы после имени тега внутри тега игнорируются, поэтому вы можете делать такие вещи. Я видел эту технику, используемую в точных макетах, чтобы браузеры не добавляли крошечные промежутки для пробелов между некоторыми видами элементов.
Тим Даун

2
@ Кристоф: Ах, честно, спасибо. Я добавлю примечание к моему ответу. Учитывая, что он childrenпоявился в IE 4 более десяти лет, прежде чем стал стандартом или был принят другими браузерами, вы можете утверждать, что IE <= 8 является правильным, а другие браузеры - неправильными.
Тим Даун

23

firstElementChild может быть недоступен в IE <9 (только firstChild)

в IE <9 firstChild является firstElementChild, потому что MS DOM (IE <9) не хранит пустые текстовые узлы. Но если вы сделаете это в других браузерах, они вернут пустые текстовые узлы ...

мое решение

child=(elem.firstElementChild||elem.firstChild)

это даст первому ребенку даже на IE <9


Если elem.firstChildэто не узел элемента, результаты в старых IE будут отличаться, потому что elem.firstElementChildэто всегда узел элемента.
Дури

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

1
firstElementChildтакже не обязательно безопасен в браузерах не IE: Firefox начал поддерживать его только в версии 3.5.
Тим Даун

это работает для chrome, IE9 и IE8, потому что, если у них есть firstElementChild, то другая проверка не выполняется, в противном случае (старый IE) у нас firstChild, и это узел элемента для браузера, у которого нет firstElementChild (IE <9 ) ... все же я интересуюсь FF
neu-rah

1
Тим правильно, когда я был прибегая к помощи вещи на эти объекты этого обнаружился, хороший сайт , чтобы проверить один раз в то время
Элиас Ван Ootegem

20

Кросс-браузерный способ - использовать childNodesдля получения NodeList, а затем создать массив всех узлов с nodeType ELEMENT_NODE .

/**
 * Return direct children elements.
 *
 * @param {HTMLElement}
 * @return {Array}
 */
function elementChildren (element) {
    var childNodes = element.childNodes,
        children = [],
        i = childNodes.length;

    while (i--) {
        if (childNodes[i].nodeType == 1) {
            children.unshift(childNodes[i]);
        }
    }

    return children;
}

http://jsfiddle.net/s4kxnahu/

Это особенно легко, если вы используете служебную библиотеку, такую ​​как lodash :

/**
 * Return direct children elements.
 *
 * @param {HTMLElement}
 * @return {Array}
 */
function elementChildren (element) {
    return _.where(element.childNodes, {nodeType: 1});
}

Будущее:

Можно использовать querySelectorAllв сочетании с :scopeпсевдо-класса (соответствует элементу , который является точкой отсчета селектора):

parentElement.querySelectorAll(':scope > *');

На момент написания статьи это :scopeподдерживается в Chrome, Firefox и Safari.


: область была удалена из спецификации . Должны ли мы удалить раздел будущего ?
Томас Марти

4

Просто чтобы добавить к другим ответам, здесь все еще есть заметные различия, особенно когда речь идет об <svg>элементах.

Я использовал оба .childNodesи .childrenи предпочел работать с HTMLCollectionдоставлено в .childrenпоглотителе.

Однако сегодня я столкнулся с проблемами сбоя IE / Edge при использовании .childrenна <svg>. Хотя .childrenподдерживается в IE для базовых элементов HTML, он не поддерживается для фрагментов документа / документа или элементов SVG .

Для меня я был в состоянии просто захватить необходимые элементы через, .childNodes[n]потому что у меня нет посторонних текстовых узлов, чтобы беспокоиться. Вы можете сделать то же самое, но, как уже упоминалось выше, не забывайте, что вы можете столкнуться с неожиданными элементами.

Надеюсь, что это полезно для того, чтобы кто-то почесал голову, пытаясь понять, почему он .childrenработает в других местах своих js на современном IE и не работает с элементами документа или SVG.


3

Эй, ребята, не позволяйте пустому пространству обмануть вас. просто проверьте это в консольном браузере. использовать родной JavaScript. Вот и пример с двумя наборами 'ul' с одним и тем же классом. Вам не нужно иметь список 'ul' в одной строке, чтобы избежать пробелов, просто используйте количество массивов, чтобы перепрыгнуть через пробелы.

Как обойти белое пространство , querySelector()то childNodes[]Js скрипки ссылки: https://jsfiddle.net/aparadise/56njekdo/

var y = document.querySelector('.list');
var myNode = y.childNodes[11].style.backgroundColor='red';

<ul class="list">
    <li>8</li>
    <li>9</li>
    <li>100</li>
</ul>

<ul class="list">
    <li>ABC</li>
    <li>DEF</li>
    <li>XYZ</li>
</ul>

Не мой отрицательный голос, но я думаю, что кто-то проголосовал против, потому что: а) Этот вопрос 4 года, тогда document.querySelectorего не поддержали. б) Вопрос, в основном, заключается в том, каковы различия между внутреннимchildren и childNodes внутренним , что более надежно, когда выбирать одно из другого. и в) Потому что, как demonstraded в вопросе: childNodes действительно включают пробельные узлы, childrenне ...
Элиас Ван Ootegem
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.