forEach on querySelectorAll 在最近的 Microsoft 浏览器中不起作用

IT技术 javascript arrays internet-explorer foreach microsoft-edge
2021-01-12 00:56:39

我正在制作一个用于选择产品(颜色等)的脚本,该脚本适用于除Internet Explorer (11) 和Edge之外的所有浏览器

我将每个参数的选择放在一个数组中,并使用该array.forEach()方法对它们应用一个函数

颜色参数示例:

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

我在IEEdge的控制台中得到以下输出

对象不支持属性或方法“forEach”

搜索这个问题后,我了解到IE 9及更新版本应该支持此功能我试图自己定义函数但没有成功。当我记录函数时,它被定义为一个函数([native code]里面有“ ”)。

我用.forEacha替换foreach 并且效果很好,

  • 但我怎样才能让它工作?
  • 有没有的特定用途forEach()互联网浏览器边缘

我认为它是Array.prototype.forEach并且最近版本的 IE(以及所有版本的 Edge)都有它......?

3个回答

大多数 DOM 方法和集合属性实际上并不是数组,它们是集合:

NodeList最近才得到forEach(以及keys其他一些数组方法)。HTMLCollection没有也不会;事实证明,添加它们会破坏网络上的太多代码。

这两个NodeListHTMLCollection迭代,虽然,这意味着你可以遍历他们for-of,他们扩展到一个数组通过传播([...theCollection])等,但如果你在浏览器中运行,其中NodeList没有forEach,它可能太旧,有什么ES2015+ 等功能for-of或迭代。

由于NodeList被指定为 have forEach,您可以安全地对它进行 polyfill,而且容易做到:

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

在这种情况下,直接赋值很好,因为enumerable, configurable, 和writable都应该是true并且它是一个值属性。enumerabletrue让我感到惊讶,但这就是它在 Chrome/Chromium/Edge/Etc.、Firefox、旧的 Legacy Edge 和 Safari 上的本地定义方式)。

在你自己的代码中,HTMLCollection如果你愿意,你也可以这样做,只是要注意,如果你使用一些旧的 DOM 库,比如 MooTools 或 YUI 或其他类似的东西,如果你添加forEachHTMLCollection.


正如我之前所说,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, {
        value: Array.prototype[Symbol.itereator],
        writable: true,
        configurable: true
    });
}

(对于HTMLCollection.也是如此。)

这是一个使用两者的现场示例,在(例如)IE11 上试试这个(虽然它只会演示forEach),它NodeList本身没有这些功能:


¹ 令人困惑,因为HTMLCollection它是可迭代的,但它没有iterable声明标记,奇怪的是,在 JavaScript DOM 绑定中,这并不意味着某些东西是可迭代的,它意味着它具有forEach, entries, keys, values,并且它是可迭代的。但是HTMLCollection,没有用iterable声明标记的仍然是可迭代的。相反,由于前面提到这个 Web IDL 规则,它是可迭代的

@wOxxOm - 是的。这主要集中在forEach,并且在实践中,支持使用可迭代性(如for-of)的功能的环境很可能已经在 上提供了可迭代性NodeList,但我已经在上面添加了它。请注意,直接赋值不会复制本机属性描述符,您需要使用Object.defineProperty.
2021-03-22 00:56:39

好吧,让我们从这里开始,在 JavaScript 中,我们有一些情况我们称之为Array-like,意思是即使它看起来像一个数组,它也不是一个真正的数组......

例如函数中的参数或在您的情况下Nodelist ...

即使所有现代浏览器都知道您想将其更改为数组并运行良好,在 IE 和其他一些浏览器中,它不支持在 Nodelist 上使用数组函数,例如......

因此,如果您支持广泛的浏览器,最好在对它们进行任何活动之前将它们转换为数组...

有几种方法可以将类似数组的转换为真正的数组...

ES5 中广泛使用的一种结构是这样的:

Array.prototype.slice.call(YourNodeList);

所以你可以这样做:

var allDivs = document.querySelectorAll("div");
var allRealDivsArray = Array.prototype.slice.call(allDivs);

但是如果你使用 ES6,还有更简洁的方法来做到这一点,只要确保你使用 babel 将它们转换为 ES5,例如那些不支持类似数组循环的旧浏览器,肯定也不会支持 ES6。 ..

两种非常常见的方法是:

1) 使用 Array.from

const allDivs = document.querySelectorAll("div");
const allRealDivsArray = Array.from(allDivs);

2) 使用 [...Array]

const allDivs = document.querySelectorAll("div");
const allRealDivsArray = [...allDivs];

虽然它可能看起来像一个数组,但它实际上是一个 NodeList,它没有与数组相同的功能。改用 for 循环

color_btns = document.querySelectorAll('#color > p'); 

for (var i = 0; i < color_btns.length; i++) {
    color_btns[i].onclick = function () { 
        for (var j = 0; j < color_btns.length; j++) {
            if(color_btns[j].classList.contains('selected')) { 
                color_btns[j].classList.remove('selected');
            }
        }
    color_btns[i].classList.add('selected'); 
    document.querySelector('#f_color').value = color_btns[i].dataset.id;
    };
}
是的,我错过了那个。对不起!正在让孩子们睡过头
2021-03-12 00:56:39
我已经使用了一个for循环,是的,它仍然是一个很好的经典替代方案。谢谢 !
2021-03-13 00:56:39
我做到了,谢谢@jERCle :) 顺便说一下@TJCrowder 写的你忘了转换第一个forEach呵呵
2021-04-02 00:56:39
痛,我知道哈哈。看看 TJ Crowder 对 polyfill 的回答,无论如何都能让它工作:)
2021-04-05 00:56:39
然而,我在使用 IE11 时遇到了类似的痛苦,尽管它应该从 IE9 开始可用。即使按照微软的文档docs.microsoft.com/en-us/scripting/javascript/reference/...
2021-04-05 00:56:39