and*_*sel 2 javascript element concatenation
我正在尝试编写一个可在两个不同页面上使用的书签。一旦掌握了元素,我就可以成功地迭代和处理它们,但为了适应两个不同的页面,我想通过两个不同的类名编译 DIV 列表。我是这样开始的:
\n\ntraces=\n [].concat.apply(document.getElementsByClassName(\'fullStacktrace\'),\n document.getElementsByClassName(\'msg\'))\nRun Code Online (Sandbox Code Playgroud)\n\n但在第一页上结果是这样的:
\n\n> traces=[].concat.apply(document.getElementsByClassName(\'fullStacktrace\'),\n document.getElementsByClassName(\'msg\'))\n[HTMLCollection[2]]\n> traces[0]\n[div.fullStacktrace, div.fullStacktrace]\n> traces[1]\nundefined\n> traces[0][0]\n<div class=\xe2\x80\x8b"fullStacktrace" style=\xe2\x80\x8b"height:\xe2\x80\x8b auto;\xe2\x80\x8b">\xe2\x80\x8b\xe2\x80\xa6\xe2\x80\x8b</div>\xe2\x80\x8b\nRun Code Online (Sandbox Code Playgroud)\n\n而在另一页上
\n\n> traces=[].concat.apply(document.getElementsByClassName(\'fullStacktrace\'),\n document.getElementsByClassName(\'msg\'))\n[HTMLCollection[0], div#msg_2.msg.l1, ... div#msg_6460.msg.l1]\n> traces[0]\n[]\n> traces[1]\n<div class=\xe2\x80\x8b"msg l1" id=\xe2\x80\x8b"msg_2">\xe2\x80\x8b\xe2\x80\xa6\xe2\x80\x8b</div>\xe2\x80\x8b\nRun Code Online (Sandbox Code Playgroud)\n\n因此 getElementsByClassName 适用于两个页面,但看起来 concat.apply 不会迭代其第一个参数,但会迭代其第二个参数?!
\n\n所以我尝试使用 concat 两次并全力以赴使用括号:
\n\ntraces=(\n (\n [].concat.apply(document.getElementsByClassName(\'fullStacktrace\'))\n )\n .concat.apply(document.getElementsByClassName(\'msg\'))\n)\nRun Code Online (Sandbox Code Playgroud)\n\n但它变得更奇怪:第一页说:
\n\nArray[1]\n> 0: HTMLCollection[0]\n length: 0\n > __proto__: HTMLCollection\nlength: 1\n> __proto__: Array[0]\nRun Code Online (Sandbox Code Playgroud)\n\n和另一个:
\n\nArray[1]\n> 0: HTMLCollection[283] // Lots of div#msg_3.msg.l1 sort of things in here\n length: 1\n>__proto__: Array[0]\nRun Code Online (Sandbox Code Playgroud)\n\n获取其完整的 div 补集。
\n\n由于两组元素仅在一个或另一个页面上,因此我可以在我的特定情况下使用条件,但上面的行为对我来说非常令人惊讶,所以我想理解它。有任何想法吗?
\n\n作为参考,这是 Mac Chrome 版本 56.0.2924.87(64 位)
\n小智 5
要使用 concat,集合需要是数组参数。任何其他争论都不会平息。您可以.slice()为此使用:
traces= [].concat([].slice.call(document.getElementsByClassName('fullStacktrace')),
[].slice.call(document.getElementsByClassName('msg')))
Run Code Online (Sandbox Code Playgroud)
现代语法会让它变得更好一些。这使用“spread”语法创建一个新数组,并将两个集合的内容分布到其中:
traces = [...document.getElementsByClassName('fullStacktrace'),
...document.getElementsByClassName('msg')]
Run Code Online (Sandbox Code Playgroud)
或者要使用更容易填充的东西,您可以使用Array.from()来转换集合:
traces = Array.from(document.getElementsByClassName('fullStacktrace'))
.concat(Array.from(document.getElementsByClassName('msg'))))
Run Code Online (Sandbox Code Playgroud)
getElementsByClassName总的来说,我一开始就不会使用。我会用.querySelectorAll. 您将获得更好的浏览器支持和更强大的选择功能:
traces = document.querySelectorAll('.fullStacktrace, .msg')
Run Code Online (Sandbox Code Playgroud)
它使用与 CSS 相同的选择器,因此上面的选择器实际上传递了一组两个选择器,每个选择器选择具有各自类的元素。
详细解释
第一个例子:
我对上述问题的解释太简洁了。这是您的第一次尝试:
traces = [].concat.apply(document.getElementsByClassName('fullStacktrace'),
document.getElementsByClassName('msg'))
Run Code Online (Sandbox Code Playgroud)
有效的方法.concat()是将给出的任何值作为其this值和参数,并将它们组合成一个数组。但是,当this值或任何参数是实际的时Array,它会将其内容平铺到结果中。因为 anHTMLCollection不是数组,所以它被视为只是要添加的任何其他值,而不是被展平。
允许.apply()您设置this被调用方法的值,然后将其第二个参数的成员作为单独的参数传播给该方法。因此,在上面的示例中,HTMLCollection作为第一个参数传递的.apply()不会被展平为结果,但作为第二个参数传递的会展平为.apply()结果,因为它.apply()正在进行传播,而不是.concat()。从 的角度来看.concat(),第二个 DOM 选择从来不存在;它只看到具有该类的单个 DOM 元素msg。
第二个例子:
traces=(
(
[].concat.apply(document.getElementsByClassName('fullStacktrace'))
)
.concat.apply(document.getElementsByClassName('msg'))
)
Run Code Online (Sandbox Code Playgroud)
这有点不同。这部分[].concat.apply(document.getElementsByClassName('fullStacktrace'))遇到与第一个示例相同的问题,但您注意到HTMLCollection结果并没有出现。原因是当你链接.apply.concat(...到最后时,你实际上放弃了该调用的结果。
举一个更简单的例子。当你这样做时:
[].concat.apply(["bar"], ["baz", "buz"])
Run Code Online (Sandbox Code Playgroud)
...这[]实际上是一个浪费的数组。这只是到达该.concat()方法的一个捷径。.concat()will内部["bar"]是this值,并且"baz"和"buz"将作为单独的参数传递,因为再次.apply()将其第二个参数作为方法的单独参数展开。
如果将简单的示例更改为以下内容,您可以更清楚地看到这一点:
["foo"].concat.apply(["bar"], ["baz", "buz"])
Run Code Online (Sandbox Code Playgroud)
...请注意,这"foo"不在结果中。那是因为.concat()对它一无所知;它只知道是否["bar"]、"baz"和"buz"。
所以当你这样做时:
[].concat.apply(collection).concat.apply(collection)
Run Code Online (Sandbox Code Playgroud)
你也做了同样的事情。第二个.concat.apply基本上放弃第一个并仅继续提供给 的数据.apply(),因此第一个集合不会出现。如果您没有使用.apply第二次调用,那么您最终会得到一个包含两个未展平的数组HTMLCollection。
[].concat.apply(collection).concat(collection)
// [HTMLCollection, HTMLCollection]
Run Code Online (Sandbox Code Playgroud)