Цикл через дочерние узлы


85

Я пытаюсь перебрать дочерние узлы следующим образом:

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

Однако он выводится Uncaught TypeError: undefined is not a functionиз-за forEachфункции. Тоже пытаюсь использовать childrenвместо childNodesно ничего не изменилось.

Кто-нибудь знает, что происходит?

Ответы:


124

Переменная childrenявляется NodeListэкземпляром, а NodeLists не являются истинными, Arrayпоэтому они не наследуют forEachметод.

Также некоторые браузеры поддерживают его nodeList.forEach


ES5

Вы можете использовать slicefrom 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);
});

См. Подробное описание списков узлов, массивов, преобразования списков узлов и понимания модели 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)) {// здесь код}, иначе вы будете перебирать нежелательные реквизиты, такие как «длина» и т. Д.!
Кирилл Резников

8
Еще лучше: используйте for ... of ...синтаксис ES6.
Jespertheend

5

Не удержался и добавил еще метод, используя childElementCount. Он возвращает количество узлов дочерних элементов от данного родителя, поэтому вы можете перебрать его.

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

4

Попробуйте с forпетлей. Это дает ошибку, forEachпотому что это набор узлов nodelist.

Или это должно преобразовать список узлов в массив

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);


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);

Простой цикл for более читабелен, чем цикл while. Автор не требует обхода в обратном / обратном порядке.
Кирилл Резников

2

Вот функциональный способ перебора файла 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()и с другими функциями массива.)


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.