Переглядайте дочірні вузли


83

Я намагаюся прокрутити дитячі вузли так:

var children = element.childNodes;
children.forEach(function(item){
    console.log(item);
});

Однак він виводиться Uncaught TypeError: undefined is not a functionзавдяки forEachфункції. Я також намагаюся використовувати childrenзамість, childNodesале нічого не змінилося.

Хтось знає, що відбувається?

Відповіді:


122

Змінна childrenє NodeListекземпляром, і NodeLists не відповідають дійсності, Arrayі тому вони не успадковують forEachметод.

Також деякі браузери насправді його підтримують nodeList.forEach


ES5

Ви можете використовувати sliceз, Arrayщоб перетворити NodeListна належний Array.

var array = Array.prototype.slice.call(children);

Ви також можете просто використовувати, callщоб викликати forEachта передати це NodeListяк контекст.

[].forEach.call(children, function(child) {});


ES6

Ви можете використовувати fromметод, щоб перетворити ваш файл NodeListна Array.

var array = Array.from(children);

Або ви також можете використовувати синтаксис поширення приблизно... так

let array = [ ...children ];


Хакер, який можна використати, є, NodeList.prototype.forEach = Array.prototype.forEachа потім можна використовувати forEachз будь-яким NodeListбез необхідності кожного разу їх конвертувати.

NodeList.prototype.forEach = Array.prototype.forEach
var children = element.childNodes;
children.forEach(function(item){
    console.log(item);
});

Дивіться всебічне занурення у NodeLists, Arrays, перетворення NodeLists та розуміння DOM для гарного пояснення та інших способів зробити це.


Як я можу перетворити NodeList на чистий масив?
user3828771

Оновлено прикладом, але прочитайте посилання, яке я розмістив, це все пояснює :)
GillesC

2
Як варіант, ви можете зробити це:[].forEach.call(element.childNodes, child => console.log(child))
XåpplI'-I0llwlg'I -

2
Ще крутіший спосіб es6: let items = [ ...children ]перетворить його на масив
zackify

2
Існує основна помилка із застосуванням методів Array до NodeLists: NodeLists, такі як node.childNodes - це активні списки, і якщо ви маніпулюєте DOM під час циклу, NodeList може бути змінений, тобто зворотний виклик forEach () мій не викликається кожен елемент списку - або більше елементів, ніж було спочатку у списку - призводить до непередбачуваних результатів. Бажано перетворити NodeList в масив, перш ніж цикл над ним.
stephband

30

Я дуже запізнююсь на вечірку, але оскільки element.lastChild.nextSibling === null, мені здається, це найпростіший варіант:

for(var child=element.firstChild; child!==null; child=child.nextSibling) {
    console.log(child);
}

1
Найпростішим варіантом є використання звичайного циклу "for". Але ваш варіант цікавий.
Кирило Резніков

Мені це подобається найкраще, я планував здійснити те саме ... логічне і не потребує перетворень
Уджвал Сингх,

23

Ось як ви можете це зробити за допомогою for-inциклу.

var children = element.childNodes;

for(child in children){
    console.log(children[child]);
}

14
Ви забули про перевірку: if (children.hasOwnProperty (child)) {// code here}, або ви будете перебирати небажані реквізити, такі як "length" тощо!
Кирило Резніков

7
Ще краще: використовуйте for ... of ..., це синтаксис ES6.
Jespertheend

4

Спробуйте за допомогою forпетлі. Це дає помилку, forEachоскільки це сукупність вузлів nodelist.

Або це повинно перетворити node-list у масив

function toArray(obj) {
  var array = [];
  for (var i = 0; i < obj.length; i++) { 
    array[i] = obj[i];
  }
return array;
}

Або ви можете використовувати це

var array = Array.prototype.slice.call(obj);

4

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

for(var i=0, len = parent.childElementCount ; i < len; ++i){
    ... do something with parent.children[i]
    }


2

Спробуйте це [обхід зворотного порядку]:

var childs = document.getElementById('parent').childNodes;
var len = childs.length;
if(len --) do {
    console.log('node: ', childs[len]);
} while(len --);

АБО [в порядку обходу]

var childs = document.getElementById('parent').childNodes;
var len = childs.length, i = -1;
if(++i < len) do {
    console.log('node: ', childs[i]);
} while(++i < len);

Цикл Simple for є більш читабельним, ніж цикл while. Автор не вимагає обернення зворотного / зворотного порядку.
Кирило Резніков

2

Ось функціональний спосіб ітерації ES6 над a NodeList. Цей метод використовує Array«S forEachнаступним чином:

Array.prototype.forEach.call(element.childNodes, f)

Де fфункція ітератора, яка приймає дочірні вузли як перший параметр, а індекс - як другий.

Якщо вам потрібно переглядати NodeLists більше одного разу, ви можете створити з цього невеликий функціональний метод утиліти:

const forEach = f => x => Array.prototype.forEach.call(x, f);

// For example, to log all child nodes
forEach((item) => { console.log(item); })(element.childNodes)

// The functional forEach is handy as you can easily created curried functions
const logChildren = forEach((childNode) => { console.log(childNode); })
logChildren(elementA.childNodes)
logChildren(elementB.childNodes)

(Ви можете зробити той же трюк для map()інших функцій Array.)


0

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

if (typeof NodeList.prototype.forEach == "undefined"){
    NodeList.prototype.forEach = function (cb){
        for (var i=0; i < this.length; i++) {
            var node = this[i];
            cb( node, i );
        }
    };
}
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.