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

新手上路,请多包涵

我正在制作一个用于选择产品(颜色等)的脚本,它适用于除 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”

查了一下issue,得知这个功能应该是 IE 9及以上版本才支持的。我试图自己定义函数,但没有成功。当我记录函数时,它被定义为函数(内部带有“ [native code] ”)。

我用一个 .forEach for 并且它工作得很好,

  • 但我怎样才能让它发挥作用呢?
  • forEach() 是否有用于 Internet ExplorerEdge 的特定用法?

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

原文由 AymDev 发布,翻译遵循 CC BY-SA 4.0 许可协议

阅读 619
2 个回答

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

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

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] ), 等。但是如果你在浏览器上运行 NodeList 没有 forEach ,它可能太旧了,没有任何 ES2015+ 功能,比如 for-of 或迭代。

由于 NodeList 被指定为具有 forEach ,您可以安全地填充它,而且它 真的很 容易做到:

 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 true 让我感到惊讶,但这就是它在 Chrome/Chromium/Edge/Etc.、Firefox、旧版 Legacy Edge 和 Safari 上的本地定义方式)。

在您自己的代码中,您也可以使用 HTMLCollection 来做到这一点,如果您愿意,请注意,如果您使用的是一些旧的 DOM 库,例如 MooTools 或 YUI 等,如果您添加它们,它们可能会混淆 forEachHTMLCollection


正如我之前所说, NodeListHTMLCollection 都被指定为可迭代的(因为 这个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.iterator],
        writable: true,
        configurable: true
    });
}

(对于 HTMLCollection 。)

这是一个同时使用两者的实例,在(例如)IE11 上试试这个(尽管它只会演示 forEach ),而 NodeList 本身没有这些功能:

 // 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.iterator],
            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>

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

原文由 T.J. Crowder 发布,翻译遵循 CC BY-SA 4.0 许可协议

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

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

即使所有现代浏览器都知道您想将其更改为 Array 并且运行良好,在 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) 使用 […数组]

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

原文由 Alireza 发布,翻译遵循 CC BY-SA 3.0 许可协议

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题