Лучший способ получить дочерние узлы
Мне было интересно, 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;
}
Это особенно легко, если вы используете служебную библиотеку, такую как 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>