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

Мне было интересно, 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 занимается этим для вас" - не то, что я ищу, поэтому нет тега jquery.

5 ответов

Решение

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

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

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

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

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

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

мое решение

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

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

Кросс-браузерный способ это использовать 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.

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

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

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

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

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

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

Как обойти пустое пространство с querySelector() затем childNodes[] Ссылка js fiddle: 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>
Другие вопросы по тегам