Mam funkcję Javascript, która akceptuje listę węzłów HTML, ale oczekuje tablicy Javascript (uruchamia na niej niektóre metody Array) i chcę przekazać jej wynik, Document.getElementsByTagName
który zwraca listę węzłów DOM.
Początkowo myślałem o użyciu czegoś prostego, takiego jak:
Array.prototype.slice.call(list,0)
I to działa dobrze we wszystkich przeglądarkach, z wyjątkiem oczywiście Internet Explorera, który zwraca błąd „Oczekiwano obiektu JScript”, ponieważ najwyraźniej lista węzłów DOM zwrócona przez Document.getElement*
metody nie jest wystarczającym obiektem JScript, aby być celem wywołania funkcji.
Ostrzeżenia: nie mam nic przeciwko pisaniu kodu specyficznego dla przeglądarki Internet Explorer, ale nie mogę używać żadnych bibliotek JavaScript, takich jak JQuery, ponieważ piszę widżet do osadzenia w witrynie internetowej innej firmy i nie mogę załadować bibliotek zewnętrznych, które stworzy konflikt dla klientów.
Ostatnim moim wysiłkiem jest iteracja listy węzłów DOM i samodzielne utworzenie tablicy, ale czy istnieje lepszy sposób na zrobienie tego?
źródło
Odpowiedzi:
NodeLists są obiektami hosta ,
Array.prototype.slice
nie ma gwarancji , że metoda na obiektach hosta będzie działać, specyfikacja ECMAScript stwierdza:Zalecałbym wykonanie prostej funkcji do iteracji po
NodeList
i dodania każdego istniejącego elementu do tablicy:function toArray(obj) { var array = []; // iterate backwards ensuring that length is an UInt32 for (var i = obj.length >>> 0; i--;) { array[i] = obj[i]; } return array; }
AKTUALIZACJA:
Jak sugerują inne odpowiedzi, możesz teraz używać w nowoczesnych środowiskach składni rozprzestrzeniania lub
Array.from
metody:const array = [ ...nodeList ] // or Array.from(nodeList)
Ale myśląc o tym, myślę, że najczęstszym przypadkiem użycia konwersji NodeList na Array jest iteracja po nim, a teraz
NodeList.prototype
obiekt maforEach
natywną metodę , więc jeśli jesteś w nowoczesnym środowisku, możesz go użyć bezpośrednio lub mieć pollyfill.źródło
array[i] = obj[i]
zamiast tego zrobićarray.push(obj[i])
?obj.length
to coś innego niż liczba całkowita?W es6 możesz po prostu użyć w następujący sposób:
Operator rozprzestrzeniania
var elements = [... nodelist]
Za pomocą
Array.from
var elements = Array.from(nodelist)
więcej informacji na https://developer.mozilla.org/en-US/docs/Web/API/NodeList
źródło
Array.from()
: DArray.from
działa tylko , ponieważ TS transponuje to nanodelist.slice
- co nie jest obsługiwane.Array.from
spread
zostanie użyte.Korzystanie ze spreadu (ES2015) jest tak proste, jak:
[...document.querySelectorAll('p')]
(opcjonalnie: użyj Babel, aby przetransponować powyższy kod ES6 do składni ES5)
Wypróbuj w konsoli przeglądarki i zobacz magię:
for( links of [...document.links] ) console.log(links);
źródło
Użyj tej prostej sztuczki
<Your array> = [].map.call(<Your dom array>, function(el) { return el; })
źródło
Array.prototype.slice
(lub[].slice
jak to ujęłeś)? Na marginesie chciałbym skomentować, że błąd specyficzny dla IE, który udokumentowałem w Q, występuje w IE 8 lub starszym, gdzie imap
tak nie jest zaimplementowany. W IE 9 („tryb standardów”) lub nowszym obaslice
i działająmap
w ten sam sposób.Chociaż nie jest to właściwa podkładka, ponieważ nie ma specyfikacji wymagającej pracy z elementami DOM, stworzyłem taką, aby umożliwić Ci użycie
slice()
w ten sposób: https://gist.github.com/brettz9/6093105AKTUALIZACJA : Kiedy podniosłem to z edytorem specyfikacji DOM4 (pytając, czy mogą dodać własne ograniczenia do obiektów hosta (tak, aby specyfikacja wymagała od implementatorów poprawnej konwersji tych obiektów, gdy są używane z metodami tablicowymi) poza specyfikację ECMAScript, która miała dopuszcza niezależność implementacji), odpowiedział, że „obiekty hosta są mniej więcej przestarzałe według ES6 / IDL”. Widzę na http://www.w3.org/TR/WebIDL/#es-array, że specyfikacje mogą używać tego IDL do definiowania „obiektów tablicy platformy”, ale http://www.w3.org/TR/domcore/ nie Wydaje się, że nie używa nowego IDL dla
HTMLCollection
(chociaż wygląda na to, że może to robić,Element.attributes
chociaż wyraźnie stwierdza, że używa WebIDL dla DOMString i DOMTimeStamp). Rozumiem[ArrayClass]
(który dziedziczy po Array.prototype) jest używany doNodeList
(iNamedNodeMap
jest obecnie przestarzały na rzecz jedynego elementu, który nadal by go używałElement.attributes
). W każdym razie wygląda na to, że ma stać się standardem. ES6Array.from
może być również wygodniejszy dla takich konwersji niż konieczność określeniaArray.prototype.slice
i bardziej przejrzysty semantycznie niż[].slice()
(a krótsza formaArray.slice()
(„rodzaj tablicy”), o ile wiem, nie stała się standardowym zachowaniem).źródło
Dziś, w 2018 roku, moglibyśmy skorzystać z ECMAScript 2015 (6. edycja) lub ES6, ale nie wszystkie przeglądarki go rozumieją (np. IE nie rozumie go w całości). Jeśli chcesz, możesz użyć ES6 w następujący sposób:
var array = [... NodeList];
( jako operator spreadu ) lubvar array = Array.from(NodeList);
.W innym przypadku (jeśli nie możesz użyć ES6) możesz użyć najkrótszej drogi do konwersji a
NodeList
naArray
:var array = [].slice.call(NodeList, 0);
.Na przykład:
var nodeList = document.querySelectorAll('input'); //we use "{}.toString.call(Object).slice(8, -1)" to find the class name of object console.log({}.toString.call(nodeList).slice(8, -1)); //NodeList var array = [].slice.call(nodeList, 0); console.log({}.toString.call(array).slice(8, -1)); //Array var result = array.filter(function(item){return item.value.length > 5}); for(var i in result) console.log(result[i].value); //credit, confidence
<input type="text" value="trust"><br><br> <input type="text" value="credit"><br><br> <input type="text" value="confidence">
Ale jeśli chcesz
DOM
łatwo iterować po liście węzłów, nie musisz konwertować aNodeList
naArray
. Możliwe jest zapętlenie elementów wNodeList
using:var nodeList = document.querySelectorAll('input'); // Calling nodeList.item(i) isn't necessary in JavaScript for(var i = 0; i < nodeList.length; i++) console.log(nodeList[i].value); //trust, credit, confidence
<input type="text" value="trust"><br><br> <input type="text" value="credit"><br><br> <input type="text" value="confidence">
Nie ulegaj pokusie
for...in
lubfor each...in
wyliczania elementów na liście, ponieważ spowoduje to również wyliczenie długości i właściwości elementuNodeList
oraz spowoduje błędy, jeśli twój skrypt zakłada, że ma do czynienia tylko z obiektami elementów. Niefor..in
ma również gwarancji, że odwiedzisz nieruchomości w określonej kolejności.for...of
pętle będą poprawnie zapętlać obiekty NodeList.Zobacz też:
źródło
var arr = new Array(); var x= ... get your nodes; for (i=0;i<x.length;i++) { if (x.item(i).nodeType==1) { arr.push(x.item(i)); } }
Powinno to zadziałać, przejść przez przeglądarkę i uzyskać wszystkie węzły „elementów”.
źródło