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

Мне было интересно, 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. Im основывая это предположение на ссылке MDN:

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

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

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

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

Оба log <TextNode textContent="\n ">

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

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

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

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


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

Ответ 1

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

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

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

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

Ответ 2

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

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

мое решение

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

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

Ответ 3

Способ кросс-браузера - использовать 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

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

Как обойти пустое пространство с помощью querySelector(), затем childNodes[] js fiddle link: 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>

Ответ 5

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

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

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

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

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