querySelectorAll 上的 forEach 在最近的 Microsoft 浏览器中不起作用
我正在为关于产品(颜色等)的选择制作一个脚本,该脚本适用于除 Internet Explorer 之外的所有浏览器 (11) &边缘.
我将每个参数的选择放在一个数组中,并使用 array.forEach()
方法对它们应用一个函数.
颜色参数示例:
var color_btns = document.querySelectorAll('#color > p');color_btns.forEach(功能(颜色){color.onclick = function () {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
并且效果很好,
- 但我怎样才能让它发挥作用?
- Internet Explorer 的
forEach()
是否有特定用途?边缘 ?
我以为是 Array.prototype.forEach
并且最新版本的 IE(以及所有版本的 Edge)都有它...?
大多数 DOM 方法和集合属性实际上并不是数组,它们是集合:
querySelectorAll
返回一个静态NodeList
(您调用时匹配元素的快照).getElementsByTagName
,getElementsByTagNameNS
,getElementsByClassName
和children
属性在ParentNode
(元素是父节点)返回 liveHTMLCollection
实例(如果您更改 DOM,该更改会实时反映在集合中).getElementsByName
返回一个 liveNodeList
(不是快照).
NodeList
最近才获得 forEach
(以及 keys
和其他几个数组方法).HTMLCollection
没有也不会;事实证明,添加它们会破坏网络上的太多代码.
NodeList
和 HTMLCollection
都是 iterable,这意味着您可以使用 for-of
,通过扩展([...theCollection]
)等将它们扩展为数组.但是如果您在 NodeList
没有 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 上的原生定义方式.p>
在您自己的代码中,您也可以根据需要使用 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 上运行功能日志(){如果(控制台类型!==未定义"&& console.log){console.log.apply(控制台,参数);}}if (typeof NodeList !== "undefined" && NodeList.prototype) {//forEachif (!NodeList.prototype.forEach) {//是的,这里真的不需要 `Object.defineProperty`console.log("已添加 forEach");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],可写:真,可配置:真});}}log("测试每个");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 class="container"><div>一个</div><div>两个</div><div>三个</div><div>四个</div></div>
¹这很令人困惑,因为 HTMLCollection
是可迭代的,但它不是标有 iterable
声明,奇怪的是在 JavaScript DOM 绑定中并不意味着某些东西是可迭代的,它意味着它有 forEach
, entries
, keys
, values
, and 它是可迭代的.但是没有用 iterable
声明标记的 HTMLCollection
仍然是可迭代的.相反,它是可迭代的,因为如前所述的 这个 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.
相关文章