2017-03-17 45 views
0

我想寫一個書籤在兩個不同的頁面上工作。我可以成功迭代並處理元素,但爲了容納這兩個不同的頁面,我想用兩個不同的類名稱編譯DIV列表。我開始與此:如何在純Javascript中連接getElementsByClassName的結果?

traces= 
    [].concat.apply(document.getElementsByClassName('fullStacktrace'), 
        document.getElementsByClassName('msg')) 

,但在第一頁上它的結果是:

> traces=[].concat.apply(document.getElementsByClassName('fullStacktrace'), 
         document.getElementsByClassName('msg')) 
[HTMLCollection[2]] 
> traces[0] 
[div.fullStacktrace, div.fullStacktrace] 
> traces[1] 
undefined 
> traces[0][0] 
<div class=​"fullStacktrace" style=​"height:​ auto;​">​…​</div>​ 

而其他頁面

> traces=[].concat.apply(document.getElementsByClassName('fullStacktrace'), 
         document.getElementsByClassName('msg')) 
[HTMLCollection[0], div#msg_2.msg.l1, ... div#msg_6460.msg.l1] 
> traces[0] 
[] 
> traces[1] 
<div class=​"msg l1" id=​"msg_2">​…​</div>​ 

所以getElementsByClassName方法既適用於網頁上,但它看起來像concat.apply不會迭代它的第一個參數,但會迭代它的第二個參數?!

所以我嘗試使用CONCAT兩次,並用括號全力以赴:

traces=(
    (
    [].concat.apply(document.getElementsByClassName('fullStacktrace')) 
) 
    .concat.apply(document.getElementsByClassName('msg')) 
) 

,但它會變得陌生:第一頁說:

Array[1] 
> 0: HTMLCollection[0] 
    length: 0 
    > __proto__: HTMLCollection 
length: 1 
> __proto__: Array[0] 

另:

Array[1] 
> 0: HTMLCollection[283] // Lots of div#msg_3.msg.l1 sort of things in here 
    length: 1 
>__proto__: Array[0] 

得到它的完整divs。因爲這兩組元素只在一個或另一個頁面上,所以我可以在我的特定情況下使用一個條件,但上面的行爲對我來說是非常令人驚訝的,所以我想理解它。有任何想法嗎?

以供參考,這是Mac的Chrome版本56.0.2924.87(64位)

回答

2

要使用CONCAT,集合將需要的參數是數組。任何其他論點都不會變平。您可以使用.slice()此:

traces= [].concat([].slice.call(document.getElementsByClassName('fullStacktrace')), 
        [].slice.call(document.getElementsByClassName('msg'))) 

現代語法將使相當多的更好。這裏使用了「傳播」語法來創建兩個集合的分配到它的內容的新數組:

traces = [...document.getElementsByClassName('fullStacktrace'), 
      ...document.getElementsByClassName('msg')] 

還是要使用的東西更容易polyfilled,您可以使用Array.from()到收藏轉換:

traces = Array.from(document.getElementsByClassName('fullStacktrace')) 
       .concat(Array.from(document.getElementsByClassName('msg')))) 

總體來說,我不會首先使用getElementsByClassName。我會用.querySelectorAll。你得到更好的瀏覽器的支持和更強大的選擇能力:

traces = document.querySelectorAll('.fullStacktrace, .msg') 

這使用了CSS使用相同的選擇,因此上述選擇實際上是通過一組兩個選擇,每個選擇與它的相應類別的元素。


詳細解釋

第一個例子:

我上面的問題的解釋太簡潔。這是你的第一次嘗試:

traces = [].concat.apply(document.getElementsByClassName('fullStacktrace'), 
       document.getElementsByClassName('msg')) 

的方式.concat()作品是採取一切值以它的this價值和它的參數,並將它們合併成一個單一的陣列。但是,當this值或任何參數都是實際的Array時,它會將其內容平展到結果的一個級別。因爲HTMLCollection不是一個數組,所以它被視爲只是要添加的任何其他值,而不是展平的。

.apply()可讓您設置要調用的方法的this值,然後將其第二個參數的成員作爲單獨參數傳播到該方法。因此,鑑於上面的示例,HTMLCollection作爲第一個參數傳遞給.apply()不會變成結果,但作爲第二個參數傳遞給.apply()確實會這樣做,因爲它是.apply()做傳播,而不是.concat()。從.concat()的角度來看,第二個DOM選擇從來就不存在;它只看到了具有msg類的單個DOM元素。


第二示例:

traces=(
    (
    [].concat.apply(document.getElementsByClassName('fullStacktrace')) 
) 
    .concat.apply(document.getElementsByClassName('msg')) 
) 

這是有點不同。這部分[].concat.apply(document.getElementsByClassName('fullStacktrace'))遭遇與第一個例子相同的問題,但您注意到HTMLCollection不會在結果中結束。原因在於,當您將.apply.concat(...鏈接到最後時,您確實放棄了該呼叫的結果。

舉一個更簡單的例子。當你這樣做:

[].concat.apply(["bar"], ["baz", "buz"]) 

...的[]實際上是一種浪費陣列。這只是到達.concat()方法的簡短方法。裏面.concat()["bar"]將是this值,"baz""buz"將作爲單獨的參數傳遞,因爲再次,.apply()展開第二個參數作爲單個參數的方法。

你可以看到這個更清楚,如果你改變了簡單的例子,這樣的:

["foo"].concat.apply(["bar"], ["baz", "buz"]) 

...注意到"foo"不在結果。這是因爲.concat()不知道它;它只知道是否["bar"]"baz""buz"

所以,當你這樣做:

[].concat.apply(collection).concat.apply(collection) 

你做同樣的事情。第二個.concat.apply基本上是第一個並且只提供給.apply()的數據進行,所以第一個收集沒有出現。如果你沒有使用.apply進行第二次調用,那麼你最後會得到一個陣列,其中有兩個未平坦的HTMLCollection s。

[].concat.apply(collection).concat(collection) 
// [HTMLCollection, HTMLCollection] 
+1

贊成票提供了'querySelectorAll'解決方案 – Pineda

+0

我對querySelectorAll的投票也一樣,但我仍然不明白爲什麼完全括號表達式沒有導致[HTMLCollection [],HTMLCollection []],也不知道爲什麼第一個表達式,第二個HTMLCollection被迭代(在第一種情況下,不存在,在第二個所有的divs中被追加),而第一個HTMLCollection被簡單地注入。 –

+0

@ android.weasel:'.concat()'只會將一個集合放到結果中,如果它是一個實際的Array,所以'HTMLCollection'被視爲其他值。在你的第一個例子中使用'.apply()'應該添加'.msg'元素,所以如果有'msg'元素,那麼'traces [1]'不應該是'undefined'。如果沒有'msg',那麼它將是'undefined',因爲沒有什麼要添加的。 – 2017-03-17 13:29:45

0

因爲調用document.getElementByClassName返回HTMLCollection而非Array你得到你所看到的奇怪的結果。我們可以做些什麼來解決這個問題是的HTMLCollection轉換爲數組,然後Concat的他們,就像這樣:

traces= [].slice.call(document.getElementsByClassName('fullStacktrace')).concat([].slice.call(document.getElementsByClassName('msg'))) 

如果瀏覽器的兼容性是一個問題,雖然,看看一些來自here that may help for earlier versions of IE其他解決方案。

+0

感謝您的解決方法,但我仍然不清楚爲什麼第二頁的數組獲取[HTMLCollection,div,div,div]而不是[HTMLCollection [0],HTMLCollection [234]]或類似的東西 - 特別是因爲我沒有試圖將一個HTMLCollection連接到另一個HTMLCollection中,而是將它們都轉換爲[]其中*是一個數組? –

0

說明

Document.getElementsByClassName返回匹配提供的類名的元素的現場的HTMLCollection。這是一個類似數組的對象,但是不是的一個數組。

因此,您最初嘗試合併兩個HTML元素的實時列表,而不是合併元素數組。

建議的解決方案

使用Array#slice()創建固體(非活陣列),然後就可以直接在兩個數組合並:

var fullStacktrace = [].slice.call(document.getElementsByClassName('fullStacktrace'), 0), 
    msg = [].slice.call(document.getElementsByClassName('msg'), 0), 

    mergedDivs = fullStacktrace.concat(msg); 
+0

我還不清楚爲什麼concat對第一個和第二個getElement調用的工作方式不同:第一個插入,第二個迭代。在第二個括號表達式中,爲什麼最終數組中只有一個HTMLCollection? –

相關問題