Pętla przez childNodes

85

Próbuję przejść przez childNodes w ten sposób:

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

Jednak wyjście Uncaught TypeError: undefined is not a functionze względu na forEachfunkcję. Próbuję też użyć childrenzamiast, childNodesale nic się nie zmieniło.

Czy ktoś wie, co się dzieje?

user3828771
źródło

Odpowiedzi:

124

Zmienna childrenjest NodeListinstancją i NodeLists nie są prawdziwe Arrayi dlatego nie dziedziczą forEachmetody.

Niektóre przeglądarki faktycznie to obsługują nodeList.forEach


ES5

Możesz użyć slicefrom, Arrayaby przekonwertować NodeListplik na właściwy Array.

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

Możesz również po prostu użyć go calldo wywołania forEachi przekazania go NodeListjako kontekstu.

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


ES6

Możesz użyć tej frommetody, aby przekonwertować NodeListplik na plik Array.

var array = Array.from(children);

Albo można też użyć składni spread... jak tak

let array = [ ...children ];


Hack, którego można użyć, jest NodeList.prototype.forEach = Array.prototype.forEachi można go następnie użyć forEachz dowolnym NodeListbez konieczności ich każdorazowej konwersji.

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

Zobacz obszerne omówienie NodeLists, Arrays, konwersji NodeLists i zrozumienia DOM aby uzyskać dobre wyjaśnienie i inne sposoby zrobienia tego.

GillesC
źródło
Jak mogę przekonwertować NodeList na czystą tablicę?
user3828771,
Zaktualizowany przykładem, ale przeczytaj link, który zamieściłem, wyjaśnia wszystko :)
GillesC
2
Alternatywnie możesz to zrobić:[].forEach.call(element.childNodes, child => console.log(child))
XåpplI'-I0llwlg'I -
2
Jeszcze fajniejszy sposób ES6: let items = [ ...children ]zamieni go w tablicę
zackify
2
Jest poważny problem z zastosowaniem metod Array do NodeLists: NodeLists, takie jak node.childNodes to aktywne listy, a jeśli manipulujesz DOM podczas pętli, NodeList może ulec zmianie, co oznacza, że ​​wywołanie zwrotne do forEach () nie jest wywoływane każdy element listy - lub więcej elementów niż pierwotnie na liście - prowadzi do nieprzewidywalnych wyników. Zaleca się przekształcenie NodeList w tablicę przed wykonaniem nad nią pętli.
stephband
30

Jestem bardzo spóźniony na imprezę, ale od tego element.lastChild.nextSibling === nullczasu następująca opcja wydaje mi się najprostsza:

for(var child=element.firstChild; child!==null; child=child.nextSibling) {
    console.log(child);
}
Mrówka
źródło
1
Najprostszą opcją jest użycie zwykłej pętli „for”. Ale twoja opcja jest interesująca.
Kirill Reznikov
Podoba mi się to najbardziej, planowałem wdrożyć to samo .. logiczne i nie wymagające konwersji
Ujjwal Singh
23

Oto, jak możesz to zrobić za pomocą for-inpętli.

var children = element.childNodes;

for(child in children){
    console.log(children[child]);
}
AdityaParab
źródło
14
Zapomniałeś o sprawdzeniu: if (children.hasOwnProperty (child)) {// kod tutaj} albo będziesz iterował po niechcianych właściwościach, takich jak „length” itp.!
Kirill Reznikov
8
Nawet lepiej: użyj for ... of ..., jest to składnia ES6.
Jespertheend
5

Nie mogłem się oprzeć, aby dodać kolejną metodę, używając childElementCount. Zwraca liczbę węzłów elementów podrzędnych z danego rodzica, więc możesz nad nim zapętlić.

for(var i=0, len = parent.childElementCount ; i < len; ++i){
    ... do something with parent.children[i]
    }
Michel
źródło
4

Spróbuj z forpętlą. Daje błąd, forEachponieważ jest to zbiór węzłównodelist .

Lub powinno to przekonwertować listę węzłów na tablicę

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

Lub możesz tego użyć

var array = Array.prototype.slice.call(obj);
Mritunjay
źródło
3
const results = Array.from(myNodeList.values()).map(parser_item);

NodeList nie jest Array, ale NodeList.values ​​() zwraca Iterator tablicy, więc można go przekonwertować na Array.

Xu Qinghan
źródło
2

Spróbuj tego [przechodzenie w odwrotnej kolejności]:

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

LUB [w celu przejścia]

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

źródło
Prosta pętla for jest bardziej czytelna niż pętla while. Autor nie prosi o przechodzenie w odwrotnej / odwrotnej kolejności.
Kirill Reznikov
2

Oto funkcjonalny sposób iteracji w ES6 po pliku NodeList. Metoda ta wykorzystuje Array„s forEachtak:

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

Gdzie fjest funkcją iteratora, która otrzymuje węzły potomne jako pierwszy parametr i indeks jako drugi.

Jeśli potrzebujesz iterować po NodeLists więcej niż raz, możesz z tego utworzyć małą funkcjonalną metodę narzędziową:

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)

(Możesz zrobić tę samą sztuczkę dla map()i innych funkcji Array).

F Lekschas
źródło
0

Jeśli robisz dużo tego typu rzeczy, warto samemu zdefiniować funkcję.

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 );
        }
    };
}
P Hemans
źródło