forEach on querySelectorAll 在最近的 Microsoft 浏览器中不起作用 [英] forEach on querySelectorAll not working in recent Microsoft browsers
问题描述
我正在制作一个用于选择产品(颜色等)的脚本,它适用于所有浏览器,除了 Internet Explorer (11) &边缘.
我将每个参数的选择放在一个数组中,并使用 array.forEach()
方法对它们应用一个函数.
颜色参数示例:
var color_btns = document.querySelectorAll('#color > p');color_btns.forEach(函数(颜色){color.onclick = 函数 () {color_btns.forEach(函数(元素){if (element.classList.contains('selected')) {element.classList.remove('selected');}});color.classList.add('selected');document.querySelector('#f_color').value = color.dataset.id;};});
我在 IE & 的控制台中得到以下输出边缘:
<块引用>对象不支持forEach"属性或方法
搜索问题后,我了解到这个函数应该是IE 9 及更新版本支持.我试图自己定义函数但没有成功.当我记录函数时,它被定义为一个函数(里面有[native code]
").
我用一个 for
替换了每个 .forEach
并且它工作得很好,
- 但我怎样才能让它发挥作用?
- 是否有
forEach()
用于 Internet Explorer 和的特定用法?边缘 ?
我以为是 Array.prototype.forEach
并且最近版本的 IE(以及所有版本的 Edge)都有它......?
大多数 DOM 方法和集合属性实际上并不是数组,而是集合:
querySelectorAll
返回一个静态的NodeList
(调用时匹配元素的快照).getElementsByTagName
、getElementsByTagNameNS
,getElementsByClassName
和children
在ParentNode
(元素是父节点)返回 liveHTMLCollection
实例(如果您更改 DOM,该更改会实时反映在集合中).getElementsByName
返回一个 liveNodeList
(不是快照).
NodeList
最近才得到 forEach
(以及 keys
和其他几个数组方法).HTMLCollection
没有也不会;事实证明,添加它们会破坏网络上的太多代码.
NodeList
和 HTMLCollection
都是可迭代的,不过,这意味着您可以使用 for-of
遍历它们>,通过扩展 ([...theCollection]
) 等将它们扩展为数组.但是如果您在 NodeList
没有 NodeList
的浏览器上运行code>forEach,它可能太老了,没有任何 ES2015+ 特性,如 for-of
或迭代.
由于 NodeList
被指定为具有 forEach
,你可以安全地对它进行 polyfill,而且它真的很容易做到:
if (typeof NodeList !== "undefined" && NodeList.prototype && !NodeList.prototype.forEach) {//是的,这里真的不需要 `Object.defineProperty`NodeList.prototype.forEach = Array.prototype.forEach;}
在这种情况下直接赋值没问题,因为enumerable
、configurable
和writable
都应该是true
它是一个值属性.(enumerable
成为 true
令我感到惊讶,但这就是它在 Chrome/Chromium/Edge/Etc.、Firefox、旧版 Legacy Edge 和 Safari 上的原生定义方式.>
在你自己的代码中,如果你愿意,你也可以使用 HTMLCollection
来做到这一点,只是要注意,如果你使用的是一些旧的 DOM 库,比如 MooTools 或 YUI 等,它们可能是如果将 forEach
添加到 HTMLCollection
会感到困惑.
正如我之前所说,NodeList
和 HTMLCollection
都被指定为可迭代的(因为 此 Web IDL 规则¹).如果您遇到具有 ES2015+ 功能的浏览器,但由于某种原因不使集合可迭代,您也可以对其进行 polyfill:
if (typeof Symbol !== "undefined" && Symbol.iterator && typeof NodeList !== "undefined" && NodeList.prototype && !NodeList.prototype[Symbol.iterator]) {Object.defineProperty(NodeList.prototype, Symbol.iterator, {值:Array.prototype[Symbol.itereator],可写:真实,可配置:真});}
(对于 HTMLCollection
也是如此.)
这是一个使用两者的现场示例,在(例如)IE11 上试试这个(虽然它只会演示 forEach
),而 NodeList
没有这些功能原生:
//仅使用 ES5 功能,因此可在 IE11 上运行函数日志(){if (typeof console !== "undefined" && console.log) {console.log.apply(控制台,参数);}}if (typeof NodeList !== "undefined" && NodeList.prototype) {//forEach如果(!NodeList.prototype.forEach){//是的,这里真的不需要 `Object.defineProperty`console.log("为Each 添加");NodeList.prototype.forEach = Array.prototype.forEach;}//可迭代性 - 不会在 IE11 上发生,因为它没有 Symbolif (typeof Symbol !== "undefined" && Symbol.iterator && !NodeList.prototype[Symbol.iterator]) {console.log("添加Symbol.iterator");Object.defineProperty(NodeList.prototype, Symbol.iterator, {值:Array.prototype[Symbol.itereator],可写:真实,可配置:真});}}日志(测试每个");document.querySelectorAll(".container div").forEach(function(div) {var html = div.innerHTML;div.innerHTML = html[0].toUpperCase() + html.substring(1).toLowerCase();});//可迭代if (typeof Symbol !== "undefined" && Symbol.iterator) {//这里使用 eval 避免在 IE11 上导致语法错误log("测试可迭代性");评估('for (const div of document.querySelectorAll(".container div")) { ' +' div.style.color = "蓝色";' +'}');}
<div>一个</div><div>二</div><div>三</div><div>四</div>
¹ 令人困惑,因为 HTMLCollection
是可迭代的,但它没有标有 iterable
声明,奇怪的是在 JavaScript DOM 绑定中不意味着某些东西是可迭代的,它意味着它有forEach
、entries
、keys
、values
、和它是可迭代的.但是 HTMLCollection
没有用 iterable
声明标记,它仍然是可迭代的.相反,它是可迭代的,因为 这个 Web IDL 规则 如前所述.
I am making a script for choices about a product (colors etc), which works in every browser except for Internet Explorer (11) & Edge.
I put the choices of each parameter in a array and apply a function to them with the array.forEach()
method.
Example for the color parameter:
var color_btns = document.querySelectorAll('#color > p');
color_btns.forEach(function(color) {
color.onclick = function () {
color_btns.forEach(function(element) {
if (element.classList.contains('selected')) {
element.classList.remove('selected');
}
});
color.classList.add('selected');
document.querySelector('#f_color').value = color.dataset.id;
};
});
I get the following output in the console of both IE & Edge:
Object doesn't support property or method 'forEach'
After searching about the issue, I learnt that this function should be supported by IE 9 and newer. I tried to define the function by myself without success. When I log the function it is defined as a function (with "[native code]
" inside).
I replaced every .forEach
by a for
and it's working pretty well,
- but how can I make it work ?
- Is there a specific usage of the
forEach()
for Internet Explorer & Edge ?
I thought it was Array.prototype.forEach
and that recent versions of IE (and all versions of Edge) had it...?
Most DOM methods and collection properties aren't actually arrays, they're collections:
querySelectorAll
returns a staticNodeList
(a snapshot of matching elements as of when you call it).getElementsByTagName
,getElementsByTagNameNS
,getElementsByClassName
, and thechildren
property on aParentNode
(Elements are parent nodes) return liveHTMLCollection
instances (if you change the DOM, that change is reflected live in the collection).getElementsByName
returns a liveNodeList
(not a snapshot).
NodeList
only recently got forEach
(and keys
and a couple of other array methods). HTMLCollection
didn't and won't; it turned out adding them would break too much code on the web.
Both NodeList
and HTMLCollection
are iterable, though, meaning that you can loop through them with for-of
, expand them into an array via spread ([...theCollection]
), etc. But if you're running on a browser where NodeList
doesn't have forEach
, it's probably too old to have any ES2015+ features like for-of
or iteration.
Since NodeList
is specified to have forEach
, you can safely polyfill it, and it's really easy to do:
if (typeof NodeList !== "undefined" && NodeList.prototype && !NodeList.prototype.forEach) {
// Yes, there's really no need for `Object.defineProperty` here
NodeList.prototype.forEach = Array.prototype.forEach;
}
Direct assignment is fine in this case, because enumerable
, configurable
, and writable
should all be true
and it's a value property. (enumerable
being true
surprised me, but that's how it's defined natively on Chrome/Chromium/Edge/Etc., Firefox, the old Legacy Edge, and Safari).
In your own code, you can do that with HTMLCollection
as well if you want, just beware that if you're using some old DOM libs like MooTools or YUI or some such, they may be confused if you add forEach
to HTMLCollection
.
As I said before, NodeList
and HTMLCollection
are both specified to be iterable (because of this Web IDL rule¹). If you run into a browser that has ES2015+ features but doesn't make the collections iterable for some reason, you can polyfill that, too:
if (typeof Symbol !== "undefined" && Symbol.iterator && typeof NodeList !== "undefined" && NodeList.prototype && !NodeList.prototype[Symbol.iterator]) {
Object.defineProperty(NodeList.prototype, Symbol.iterator, {
value: Array.prototype[Symbol.itereator],
writable: true,
configurable: true
});
}
(And the same for HTMLCollection
.)
Here's a live example using both, try this on (for instance) IE11 (although it will only demonstrate forEach
), on which NodeList
doesn't have these features natively:
// Using only ES5 features so this runs on IE11
function log() {
if (typeof console !== "undefined" && console.log) {
console.log.apply(console, arguments);
}
}
if (typeof NodeList !== "undefined" && NodeList.prototype) {
// forEach
if (!NodeList.prototype.forEach) {
// Yes, there's really no need for `Object.defineProperty` here
console.log("Added forEach");
NodeList.prototype.forEach = Array.prototype.forEach;
}
// Iterability - won't happen on IE11 because it doesn't have Symbol
if (typeof Symbol !== "undefined" && Symbol.iterator && !NodeList.prototype[Symbol.iterator]) {
console.log("Added Symbol.iterator");
Object.defineProperty(NodeList.prototype, Symbol.iterator, {
value: Array.prototype[Symbol.itereator],
writable: true,
configurable: true
});
}
}
log("Testing forEach");
document.querySelectorAll(".container div").forEach(function(div) {
var html = div.innerHTML;
div.innerHTML = html[0].toUpperCase() + html.substring(1).toLowerCase();
});
// Iterable
if (typeof Symbol !== "undefined" && Symbol.iterator) {
// Using eval here to avoid causing syntax errors on IE11
log("Testing iterability");
eval(
'for (const div of document.querySelectorAll(".container div")) { ' +
' div.style.color = "blue"; ' +
'}'
);
}
<div class="container">
<div>one</div>
<div>two</div>
<div>three</div>
<div>four</div>
</div>
¹ It's confusing, because HTMLCollection
is iterable but it isn't marked with the iterable
declaration, which bizarrely in the JavaScript DOM bindings doesn't mean that something is iterable, it means that it has forEach
, entries
, keys
, values
, and it's iterable. But HTMLCollection
, which isn't marked with the iterable
declaration, is still iterable. Instead, it's iterable because of this Web IDL rule as mentioned earlier.
这篇关于forEach on querySelectorAll 在最近的 Microsoft 浏览器中不起作用的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!