2017-03-17 21 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? –

相关问题