Найкращий спосіб отримати дочірні вузли


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 займається цим для вас" - це не те, що я шукаю, отже, ні тег.


49
>> будь ласка, будь ласка, мені подобається JavaScript, так що так, я хочу займатися подібними справами. відповіді на кшталт "jQuery займається цим для вас" - це не те, що я шукаю << оновлень для цього
david.barkhuizen

Відповіді:


211

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

Немає нічого непередбачуваного в будь-якій колекції, хоча слід знати кілька проблем:

  • IE <= 8 не включає текстові вузли, що містять пробіл, у childNodesінших браузерах
  • IE <= 8 включає вузли коментарів всередині, childrenтоді як інші браузери мають лише елементи

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


Зрозуміло, що все одно, але все-таки мене помиляє: текстовий вузол пробілів, який вибирає childNodes, - це фактично новий рядок і пара вкладок у коді. Це пов'язано з доктрипом XHTML? чи можна це вирішити, додавши пробіл для структурування коду всередині тегів?
Elias Van Ootegem

@EliasVanOotegem: Це не стосується дотипу. Текстові вузли з пробілом завжди включаються в DOM (за винятком IE <9) як для HTML, так і для XHTML, де б вони не з’являлися у джерелі. Це взагалі гарна річ, хоча може вас наздогнати, якщо ви не готові до цього.
Тім Даун

Я мав на увазі, якщо це допоможе написати мою розмітку так <td\n\t>content</td>. Як ви можете зробити з XML, щоб уникнути надмірного пробілу як частини даних (або DOM, в даному випадку). Чому пробіл всередині тегу буде включений до DOM?
Elias Van Ootegem

@EliasVanOotegem: О, бачу. Білий пробіл після назви тегу всередині тегу ігнорується, тому ви можете зробити таку річ. Я бачив цю техніку, що використовується в точних схемах для запобігання браузерам додавати невеликі проміжки для пробілу між деякими видами елементів.
Тім Даун

2
@Christophe: А, справедлива справа, дякую. Я додам записку до своєї відповіді. Зважаючи на те, що 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це не елемент вузла, результати будуть різними у старих ІЕ, оскільки elem.firstElementChildце завжди елемент вузла.
дурі

Вибачте, але я знаю, як отримати першу дитину у всіх основних браузерах. Я хочу знати, як структуруються різні об’єкти, щоб краще зрозуміти схожість та відмінності між ними. Я відредагую своє питання пізніше, щоб зробити це зрозумілішим, вибачте за
змішання

1
firstElementChildНе обов’язково безпечний і в браузерах, які не є IE: Firefox лише почав підтримувати його у версії 3.5.
Тім Даун

це працює для chrome, IE9 та IE8, тому що якщо вони мають firstElementChild, то інша перевірка не виконується, інакше (старий IE) у нас є firstChild, і це елемент елемента для браузера, у якого немає firstElementChild (IE <9 ) ... все ще мені цікаво про FF
neu-rah

1
Тім правильно, коли я гуляв речі на цих об’єктах, це з’явилося, хороший сайт, щоб перевірити його раз
Elias Van 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 внутрішньо , що є більш надійним, коли вибрати один над іншим. і c) Тому що, як показано у запитанні: childNodes чи включаються вузли пробілів, childrenчи не ...
Elias Van Ootegem
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.