2010-04-08 8 views
64

che mi bug che non posso fare document.querySelectorAll(...).map(...) anche in Firefox 3.6, e non riesco ancora a trovare una risposta, così ho pensato di cross-post su Quindi la domanda da questo blog:Perché document.querySelectorAll restituisce uno StaticNodeList anziché una matrice reale?

http://blowery.org/2008/08/29/yay-for-queryselectorall-boo-for-staticnodelist/

Qualcuno sa di un motivo tecnico per cui non si ottiene una matrice? O perché uno StaticNodeList non eredita da una matrice in modo tale da poter utilizzare map, concat, ecc.?

(BTW se è solo una funzione che si desidera, si può fare qualcosa di simile NodeList.prototype.map = Array.prototype.map; ... ma ancora una volta, perché è questa funzionalità (intenzionalmente?) Bloccato in primo luogo?)

+0

In realtà anche getElementsByTagName non restituisce un array, ma un insieme, e se vuoi usarlo come una matrice (con metodi come concat ecc.) devi convertire tale raccolta in una matrice facendo un ciclo e copia ogni elemento della collezione in una matrice. Nessuno si è mai lamentato di questo. –

risposta

54

Credo che sia una decisione filosofica del W3C. Il design del W3C DOM [spec] è alquanto ortogonale rispetto al design di JavaScript, in quanto il DOM è che significa che la piattaforma è neutrale.

decisioni come "getElementsByFoo() restituisce un ordinato NodeList" o "querySelectorAll() restituisce un StaticNodeList" sono molto intenzionale, in modo che le implementazioni non devono preoccuparsi di allineare la struttura dei dati restituiti in base a implementazioni dipendenti dalla lingua (come .map essere disponibile su Array in JavaScript e Ruby, ma non su elenchi in C#).

Il W3C mirano bassa: diranno un NodeList dovrebbe contenere una readonly .length property of type unsigned long perché credono ogni implementazione può almeno sostegno che, ma non diranno esplicitamente che l'operatore [] indice dovrebbe essere sovraccaricato di sostenere sempre elementi posizionali, perché non vogliono ostacolare un po 'di scarso linguaggio che vuole implementare getElementsByFoo() ma non supportare l'overloading dell'operatore. È una filosofia prevalente presente in gran parte delle specifiche.

John Resig ha voiced a similar option come il vostro, al quale he adds:

mio argomento non è tanto che NodeIterator non è molto DOM-come è che non è molto simile a JavaScript. Essa non approfitta delle caratteristiche presenti nel linguaggio Javascript e utilizzarli al meglio delle sue capacità ...

faccio un po 'empatia. Se il DOM è stato scritto specificamente con le funzionalità JavaScript, sarebbe molto meno complicato e più intuitivo da usare. Allo stesso tempo, capisco le decisioni progettuali del W3C.

+0

Grazie, questo mi aiuta a dare un senso alla situazione. – Kev

+0

@Kev: ho visto il tuo commento sulla pagina dell'articolo del blog in cui si chiedeva come si sarebbe convertito lo 'StaticNodeList' in un array. Sostengo la risposta di @ mck89 come la strada da percorrere per convertire un 'NodeList' /' StaticNodeList' in un array nativo, ma questo fallirà in IE (8 obv) con un errore JScript, dato che questi oggetti sono ospitati/"speciali" . –

+0

Vero, è per questo che l'ho svalutato. Qualcun altro ha però cancellato il mio +1. Cosa intendi per hosted/special? – Kev

34

Non lo so perché restituisce una lista di nodi invece di una matrice, forse perché come getElementsByTagName aggiornerà il risultato quando si aggiorna il DOM. In ogni caso un metodo molto semplice per trasformare quel risultato in un semplice array è:

Array.prototype.slice.call(document.querySelectorAll(...)); 

e quindi si può fare:

Array.prototype.slice.call(document.querySelectorAll(...)).map(...); 
+3

In realtà non aggiorna il risultato quando aggiorni il DOM - da qui 'statico'. È necessario chiamare manualmente qSA di nuovo per aggiornare il risultato. +1 per la linea 'slice' però. – Kev

+0

Sì, come ha detto Kev: il set di risultati qSA è statico, il setet getElementsByTagName() è dinamico. –

+1

è questo ie8 sicuro? – SuperUberDuper

9

Giusto per aggiungere a quanto detto Crescent,

se è solo una funzione che si desidera, si può fare qualcosa di simile NodeList.prototype.map = Array.prototype.map

Don Fallo! Non è affatto garantito il funzionamento.

No JavaScript o DOM/standard di BOM specifica che il costruttore-funzione di NodeList esiste anche come/window proprietà globale, o che il NodeList restituito da querySelectorAll erediterà da essa, o che la sua prototipo è scrivibile, o che la funzione Array.prototype.map funzionerà effettivamente su un NodeList.

Un NodeList può essere un 'oggetto host' (ed è uno, in IE e alcuni browser meno recenti). I metodi Array sono definiti come autorizzati a operare su qualsiasi "oggetto nativo" JavaScript che espone le proprietà numeriche e length, ma non sono obbligati a lavorare sugli oggetti host (e in IE, non lo fanno).

È fastidioso che non si ottengano tutti i metodi dell'array sugli elenchi DOM (tutti, non solo StaticNodeList), ma non esiste un modo affidabile per aggirarlo. Dovrete convertire ogni lista DOM si torna a una matrice manualmente:

Array.fromList= function(list) { 
    var array= new Array(list.length); 
    for (var i= 0, n= list.length; i<n; i++) 
     array[i]= list[i]; 
    return array; 
}; 

Array.fromList(element.childNodes).forEach(function() { 
    ... 
}); 
+0

Sparare, non ci ho pensato. Grazie! – Kev

+0

Accetto +1. Solo un commento, penso che fare "var array = []" invece di "var array = new Array (list.length)" renderà il codice ancora più breve. Ma sono interessato se sai che ci potrebbe essere un problema nel farlo. –

+0

@MarcoDemaio: No, nessun problema. 'new Array (n)' dà al JS terp un suggerimento su quanto a lungo l'array andrà a finire. Ciò potrebbe consentire di allocare in anticipo quella quantità di spazio, il che comporterebbe potenzialmente un aumento della velocità poiché alcune riallocazioni di memoria potrebbero essere evitate man mano che l'array cresce. Non so se effettivamente aiuti nei browser moderni però ... sospetterei non in modo misurabile. – bobince

70

È possibile utilizzare ES2015 (ES6) spread operator:

[...document.querySelectorAll('div')]

convertirà StaticNodeList a matrice di elementi.

Ecco un esempio su come utilizzarlo.

[...document.querySelectorAll('div')].map(x => console.log(x.innerHTML))
<div>Text 1</div> 
 
<div>Text 2</div>

0

Penso che si possa fare semplicemente seguendo

Array.prototype.map.call(document.querySelectorAll(...), function(...){...}); 

Funziona perfetto per me

Problemi correlati