2013-02-02 41 views
9

Sto tentando di unire due matrici di oggetti in modo da poter convalidare un modulo. Il solito metodo di concat non sembra funzionare in questa circostanza. Concat funziona con ordinari array di numeri e stringhe, ma non con matrici di oggetti. La riga var allTags = allInputs.concat(allSelects); non funziona.Unione di due matrici di oggetti (NodeList) in JavaScript

var allInputs = document.getElementsByTagName("input"); 
alert("Inputs: " + allInputs.length); 

var allSelects = document.getElementsByTagName("select"); 
alert("Selects: " + allSelects.length); 

var allTags = allInputs.concat(allSelects); 
alert("allTags: " + allTags.length); 
+1

questo non è matrice sua matrice come oggetto (NodeList) – salexch

+0

È, infatti, un NodeList, e quindi non ha il metodo concat. – THEtheChad

risposta

19

Concat funziona con normali array numerici e di stringa, ma non lo fa con gli array di oggetti.

In realtà, lo fa, ma NodeList casi non hanno un metodo concat, e Array#concat non dispone di un mezzo di identificazione che si desidera appiattire quelle (perché non sono array).

Ma è ancora abbastanza facile fare (vedere l'avvertenza qui sotto, però). Modificare questa linea:

var allTags = allInputs.concat(allSelects); 

a

var allTags = []; 
allTags.push.apply(allTags, allInputs); 
allTags.push.apply(allTags, allSelects); 

Live Example | Source

che funziona utilizzando un piccolo trucco: Array#push accetta un numero variabile di elementi da aggiungere alla matrice, e Function#apply chiama la funzione utilizzando il valore espressa this (nel nostro caso, allTags) e ogni nell'edificio- come oggetto come gli argomenti da passare ad esso. Dal momento che le istanze NodeList sono di tipo array, push spinge felicemente tutti gli elementi della lista sull'array.

Questo comportamento di Function#apply (non richiede il secondo argomento sia realmente un array) è molto chiaramente definito nella specifica, ed è ben supportato nei browser moderni.

Purtroppo, IE6 e 7 non supportano quanto sopra (penso che sia specificamente utilizzando oggetti host   — NodeLists   — per Function#apply 's secondo argomento), ma poi, non dovrebbe essere di supporto loro, sia . :-) Anche IE8 no, il che è più problematico. IE9 è soddisfatto.

Se è necessario sostenere IE8 e precedenti, purtroppo, penso che sei bloccato con un vecchio ciclo noioso:

var allInputs = document.getElementsByTagName('input'); 
var allSelects = document.getElementsByTagName('select'); 
var allTags = []; 

appendAll(allTags, allInputs); 
appendAll(allTags, allSelects); 

function appendAll(dest, src) { 
    var n; 

    for (n = 0; n < src.length; ++n) { 
    dest.push(src[n]); 
    } 

    return dest; 
} 

Live Example | Source

Questo fa funziona su IE8 e precedenti (e altri).

+0

Grazie mille per l'assistenza. Sono un principiante. – user2035797

+0

@ user2035797: Non preoccuparti, ho anche aggiunto un esempio che funziona su IE8 e versioni precedenti. –

+0

+1 wow, grazie per tutte le informazioni sul supporto del browser! – Christophe

0

Ha cercato in underscore.js affatto? Si potrebbe fare

var allSelectsAndInputs = _.union(_.values(document.getElementsByTagName("input")),_.values(document.getElementsByTagName("select"))); 

Per ulteriori informazioni si veda: http://underscorejs.org/#extend

La funzione è destinato per gli oggetti, ma funziona in questa impostazione.

+0

in realtà, ci sto giocando ... sembra un po 'spento. pausa. –

+2

Non suggerire di utilizzare tale libreria per una sola volta ... – MaxArt

+0

Verificherò il segno di sottolineatura grazie – user2035797

2

document.get[something] restituisce una NodeList, che sembra molto simile a un Array, ma ha numerose caratteristiche distintive, due dei quali sono:

  • non contiene il metodo concat
  • L'elenco degli elementi è dal vivo. Ciò significa che cambiano quando il documento cambia in tempo reale.

Se non avete problemi con trasformare i vostri NodeLists in array attuali, si potrebbe fare quanto segue per ottenere l'effetto desiderato:

var allInputs = document.getElementsByTagName("input") 
    , allSelects = document.getElementsByTagName("select") 
;//nodeLists 

var inputList = makeArray(allInputs) 
    , selectList = makeArray(allSelects) 
;//nodeArrays 

var combined = inputList.concat(selectList); 

function makeArray(list){ 
    return Array.prototype.slice.call(list); 
} 

si perdono tutte e tutti i comportamenti del NodeList originale, inclusa la sua capacità di aggiornamento in tempo reale per riflettere le modifiche al DOM, ma la mia ipotesi è che non è davvero un problema.

+0

Sappi che, come la soluzione simile che ho fornito nella prima parte della mia risposta, non funzionerà in IE8 o versioni precedenti. (Provalo qui: http://jsbin.com/iledok/1) * sigh * Almeno IE9 è decente. Quindi per IE8 e precedenti, è necessario un vecchio ciclo noioso. –

1

cosa hai a che fare con sono HTMLCollection s, e sono vivo collezioni (per esempio, quando si aggiunge un nodo al DOM, è automaticamente aggiunti alla collezione.

E purtroppo, doesn 't hanno un metodo concat ... per un motivo Quello che dovete fare è quello di convertirlo in un array, perdendo il suo comportamento in diretta:.

var allInputsArray = [].slice.call(allInputs), 
    allSelectsArray = [].slice.call(allSelects), 
    allFields = allInputsArray.concat(allSelectsArray); 
0

ho trovato un modo semplice per raccogliere gli oggetti DOM nell'ordine appaiono in una pagina o in un modulo In primo luogo, crea una classe fittizia stile 'reqd' sull'oggetto quindi utilizzare il seguente che crea una lista nell'ordine in cui appaiono sulla pagina. Basta aggiungere la classe a qualsiasi oggetto che desideri raccogliere.

var allTags = document.querySelectorAll("input.reqd, select.reqd");