2014-09-25 105 views
8

如果我使用getElementsByClassName方法VS querySelectorAll

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

一切工作正常。所有節點都改變它們的類。 但是,與gEBCN:

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

我得到錯誤。代碼在某些時候跳出循環,而不是用msg完成作業「無法設置null的類名」。
我知道這是靜態vs活動nodelist問題(我認爲),但由於gEBCN更快,我需要遍歷節點(樹)的巨大列表,我真的很想使用getElementsByClassName。
有什麼我可以做的堅持gEBCN,而不是被迫使用querySelectorAll?

+0

你能張貼演示,重現該問題? – elclanrs 2014-09-25 20:50:01

回答

8

將列表向上移動,那麼元素將從最後消失(您不再看到的地方)。

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

注意,但是,IE 8支持querySelectorAll但不getElementsByClassName,所以你可能希望傾向於querySelectorAll更好的瀏覽器支持。


另外,請不要刪除現有的類:

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

謝謝。向後循環工作正常:)。我會在5分鐘內接受答案。 – 2014-09-25 20:59:11

14

這是因爲通過getElementsByClassName返回HTMLCollection是活的。

這意味着如果您將"class"添加到某個元素的classList,它將奇蹟般地出現在temp中。

反之亦然:如果您刪除temp中某個元素的"class"類別,它將不再存在。

因此,更改類會重新集合索引並更改其長度。所以問題在於你事先重複捕捉它的長度,而不考慮索引的變化。

爲了避免這個問題,您可以:

  • 使用非現場採集。例如,

    var temp = document.querySelectorAll(".class"); 
    
  • 轉換實況HTMLCollection到一個數組。例如,其中有一個

    temp = [].slice.call(temp); 
    temp = Array.from(temp); // EcmaScript 6 
    
  • 向後迭代迭代。例如,請參閱@Quentin's answer

  • 考慮到指數的變化。例如,

    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

(在編輯之前,仍然與聲明問題是緩存的'max'相關):這是行不通的。它會阻止它結束運行,但它會跳過元素。即它將修改索引'0'中的第一個元素(導致它從NodeList中移除),然後修改* third *元素,它剛剛*移動*到索引'1'(跳過移動的第二個元素索引'0')。 – Quentin 2014-09-25 20:58:42

+0

@Quentin是的,我越來越奇怪的結果。一些節點改變了類,一些沒有改變。 – 2014-09-25 21:04:49

+0

@Oriol我認爲反向循環是克服活動列表問題的最優雅和最快速的方法。我已經接受了昆汀的回答(他更快)。關於querySelectorAll,處理起來要慢得多,我有treeView,有一千個節點。 gEBCN規則。 – 2014-09-25 21:13:02

相關問題