如何在普通Javascript中连接getElementsByClassName的结果?

如何在普通Javascript中连接getElementsByClassName的结果?,javascript,concatenation,elements,Javascript,Concatenation,Elements,我正在尝试写一个书签,它可以在两个不同的页面上工作。一旦我掌握了元素,我就可以成功地迭代并处理它们,但是为了适应两个不同的页面,我想通过两个不同的类名编译一个div列表。我从这个开始: traces= [].concat.apply(document.getElementsByClassName('fullStacktrace'), document.getElementsByClassName('msg')) 但在第一页,它的结果是: > tra

我正在尝试写一个书签,它可以在两个不同的页面上工作。一旦我掌握了元素,我就可以成功地迭代并处理它们,但是为了适应两个不同的页面,我想通过两个不同的类名编译一个div列表。我从这个开始:

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>​
但它变得更加奇怪:第一页说:

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]
获取其完整的div补码

由于这两组元素只在一个或另一个页面上,我可以在我的特定情况下使用条件,但是上面的行为让我非常惊讶,所以我想理解它。有什么想法吗


作为参考,这是在Mac Chrome版本56.0.2924.87(64位)

上。要使用concat,集合必须是数组参数。任何其他论点都不会被驳倒。您可以为此使用
.slice()

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

现代语法会让它变得更好。这将使用“spread”语法创建一个新数组,其中包含两个集合的内容:

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

或者,要使用更容易填充的集合,可以使用
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()
的工作方式是将给定的任何值作为其
值及其参数,并将它们组合成一个数组。但是,当
值或任何参数是实际的
数组
时,它会将其内容展平到结果的一个级别。因为
HTMLCollection
不是数组,所以它被看作是要添加的任何其他值,而不是平坦的

.apply()
允许您设置所调用方法的
this
值,然后将其第二个参数的成员作为单个参数分散到该方法。因此,在上面的示例中,作为第一个参数传递给
.apply()
HTMLCollection
不会被展平到结果中,但是作为第二个参数传递给
.apply()
会被展平,因为它是
.apply()
进行扩展的,而不是
.concat()
。从
.concat()
的角度来看,第二个DOM选择从未存在过;它只看到具有
msg
类的单个DOM元素


第二个例子:

这有点不同。这部分
[].concat.apply(document.getElementsByClassName('fullStacktrace'))
遇到了与第一个示例相同的问题,但是您注意到
HTMLCollection
不会在结果中结束。原因是当您将
.apply.concat(…
链接到末尾时,实际上放弃了该调用的结果

举一个简单的例子。当您这样做时:

[].concat.apply(["bar"], ["baz", "buz"])
…实际上,
[]
是一个浪费的数组。这只是到达
.concat()
方法的捷径。在
.concat()
内部,
[“bar”]
将是
这个
值,
“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
的数组

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

因为调用
document.getElementByClassName
会返回一个
HTMLCollection
而不是一个
Array
,您会看到奇怪的结果。我们可以做的是将HTMLCollection转换为一个数组,然后将其合并,如下所示:

traces= [].slice.call(document.getElementsByClassName('fullStacktrace')).concat([].slice.call(document.getElementsByClassName('msg')))
但是,如果浏览器兼容性是一个问题,请查看其他一些解决方案。

说明:

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);

向上投票支持提供
querySelectorAll
解决方案我也向上投票支持querySelectorAll,但我仍然不明白为什么全括号表达式没有导致[HTMLCollection[],HTMLCollection[],也不明白为什么对于第一个表达式,第二个HTMLCollection被迭代(在第一种情况下,不存在,在第二种情况下,它的所有div都被追加),而第一个HTMLCollection只是按原样注入。@android.weasel:
.concat()
仅在集合是实际数组的情况下将其展平到结果中,因此将
HTMLCollection
视为其他值
[].concat.apply(collection).concat(collection)
// [HTMLCollection, HTMLCollection]
traces= [].slice.call(document.getElementsByClassName('fullStacktrace')).concat([].slice.call(document.getElementsByClassName('msg')))
var fullStacktrace = [].slice.call(document.getElementsByClassName('fullStacktrace'), 0),
  msg = [].slice.call(document.getElementsByClassName('msg'), 0),

  mergedDivs = fullStacktrace.concat(msg);