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

时间:2017-10-25 09:47:13

标签: javascript arrays internet-explorer foreach microsoft-edge

我正在制作一个关于产品(颜色等)的选择的脚本,除了 Internet Explorer (11)&

我将每个参数的选择放在数组中,并使用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;
    };
});

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

  

Object不支持属性或方法'forEach'

在搜索了这个问题之后,我了解到这个功能应该是supported by IE 9 and newer。我试图自己定义功能但没有成功。当我记录该函数时,它被定义为一个函数(里面有“[native code]”。

我用.forEach替换每个for并且效果很好,

  • 但我怎样才能让它发挥作用?
  • Internet Explorer &的forEach()是否有特定用途? Edge

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

3 个答案:

答案 0 :(得分:13)

querySelectorAll的返回值不是数组,而是NodeList。最近才获得forEach(兼容JavaScript' 迭代协议,允许您将它们用作for-of的目标并传播表示法。)

您可以轻松地填充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;
}

在这种情况下,直接分配很好,因为enumerableconfigurablewritable都应该是true,并且它是一个值属性。 (enumerable true让我感到惊讶,但这就是它在Chrome,Firefox,Edge和Safari上如何定义的原因。

NodeList获得forEach时,它也变为 iterable ,这意味着您可以通过NodeList循环遍历for-of的内容,并在需要迭代的其他地方使用NodeList(例如,在数组初始化程序中的扩展表示法中)。

实际上,具有使用可迭代功能的浏览器(如for-of循环)也可能已经提供NodeList的这些功能,但要确保(也许您正在进行转换)并且包括Symbol)的polyfill,我们需要做第二件事:向创建迭代器的Symbol.iterator属性添加一个函数:

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

两者兼顾:

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;
    if (typeof Symbol !== "undefined" && Symbol.iterator && !NodeList.prototype[Symbol.iterator]) {
        Object.defineProperty(NodeList.prototype, Symbol.iterator, {
            value: Array.prototype[Symbol.itereator],
            writable: true,
            configurable: true
        });
    }
}

这里是一个使用两者的实例,尝试使用(例如)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
    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>
&#13;
&#13;
&#13;

HTMLCollection(以及其他各种旧版API)返回的getElementsByTagName并未定义为可迭代,但如果您愿意,也可以为HTMLCollection执行此操作。这是一个循环做粗暴NodeList(如有必要)和HTMLCollection(如有必要):

for (const ctor of [typeof NodeList !== "undefined" && NodeList, typeof HTMLCollection !== "undefined" && HTMLCollection]) {
    if (ctor && ctor.prototype && !ctor.prototype.forEach) {
        // (Yes, there's really no need for `Object.defineProperty` here)
        ctor.prototype.forEach = Array.prototype.forEach;
        if (typeof Symbol !== "undefined" && Symbol.iterator && !ctor.prototype[Symbol.iterator]) {
            Object.defineProperty(ctor.prototype, Symbol.iterator, {
                value: Array.prototype[Symbol.itereator],
                writable: true,
                configurable: true
            });
        }
    }
}

请注意HTMLCollection 实时,因此您对DOM的更改会立即反映在集合中,这会对集合中的内容产生反映,这可能会导致令人惊讶的行为。 (NodeList是一个断开连接的集合,因此行为不会发生。)

答案 1 :(得分:5)

好的,让我们从这里开始,在JavaScript中,我们有一些我们称之为类似数组的情况,即使它看起来像一个数组,它也不是一个真实的阵列...

例如参数在函数中或在您的情况下 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];

答案 2 :(得分:1)

虽然它可能看起来像一个数组,但它实际上是一个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;
    };
}