2014-09-25 1 views
8

se usogetElementsByClassName vs querySelectorAll

var temp = document.querySelectorAll(".class"); 
for (var i=0, max=temp.length; i<max; i++) { 
temp[i].className = "new_class"; 
} 

tutto funziona bene. Tutti i nodi cambiano le loro classi. Ma, con gEBCN:

var temp = document.getElementsByClassName("class"); 
for (var i=0, max=temp.length; i<max; i++) { 
temp[i].className = "new_class"; 
} 

ottengo l'errore. Il codice salta fuori dal ciclo ad un certo punto, non terminando il lavoro con msg "non è possibile impostare className di null".
Capisco che questo sia statico rispetto al problema di nodelist dal vivo (credo), ma dal momento che gEBCN è molto più veloce e ho bisogno di attraversare un enorme elenco di nodi (albero), mi piacerebbe molto usare getElementsByClassName.
C'è qualcosa che posso fare per restare con gEBCN e non essere costretto a usare querySelectorAll?

+0

Puoi pubblicare una demo per riprodurre il problema? – elclanrs

risposta

8

Loop sulla lista indietro, quindi gli elementi svaniranno dalla fine (dove non si guarda più).

for (var i = temp.length - 1; i >= 0; i--) { 
    temp[i].className = "new_class"; 
} 

Si noti, tuttavia, che IE 8 supporta querySelectorAll ma non getElementsByClassName, così si potrebbe desiderare di preferire querySelectorAll per un migliore supporto del browser.


In alternativa, non rimuovere la classe esistente:

for (var i=0, max=temp.length; i<max; i++) { 
    temp[i].className += " new_class"; 
} 
+0

Grazie. Il ciclo a ritroso funziona bene :). Accetterò la risposta in 5 minuti. –

14

Questo perché HTMLCollection restituito da getElementsByClassName è vivo.

Ciò significa che se si aggiunge "class" all'elenco delle classi di un elemento, questo verrà magicamente visualizzato in temp.

L'oposite è anche vero: se si rimuove la classe "class" di un elemento all'interno temp, non sarà più lì.

Pertanto, la modifica delle classi reindice la raccolta e ne modifica la lunghezza. Quindi il problema è che lo iterate raggiungendo la sua lunghezza in anticipo e senza prendere in considerazione i cambiamenti degli indici.

Per evitare questo problema, è possibile:

  • Utilizzare una collezione non dal vivo. Ad esempio,

    var temp = document.querySelectorAll(".class"); 
    
  • Convertire il vivo HTMLCollection a un array. Per esempio, con uno di questi

    temp = [].slice.call(temp); 
    temp = Array.from(temp); // EcmaScript 6 
    
  • Iterate all'indietro. Ad esempio, vedere @Quentin's answer.

  • Prendere in considerazione le variazioni degli indici. Ad esempio,

    for (var i=0; i<temp.length; ++i) { 
    temp[i].className = "new_class"; 
    --i; // Subtract 1 each time you remove an element from the collection 
    } 
    
    while(temp.length) { 
    temp[0].className = "new_class"; 
    } 
    
+0

(Prima delle tue modifiche, e ancora rilevante per l'affermazione che il problema è il 'max' nella cache): Non funzionerà. L'interrompe la corsa, ma salterà gli elementi man mano che procede. Vale a dire che modificherà il primo elemento dell'indice '0' (causandone la rimozione da NodeList), quindi modificherà l'elemento * third * che ha appena * spostato * all'indice' 1' (saltando il secondo elemento che si è spostato indicizzare '0'). – Quentin

+0

@Quentin Sì, stavo ottenendo risultati strani. Alcuni nodi hanno cambiato classe, altri no. –

+0

@Oriol Penso che il ciclo arretrato sia il modo più elegante e veloce per superare il problema del livelist. Ho già accettato la risposta di Quentin (era più veloce). A proposito di querySelectorAll, è molto più lento lavorare, ho treeView con migliaia di nodi. gEBCN rulez. –