我正在制作一个关于产品(颜色等)的选择的脚本,除了 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
并且效果很好,
forEach()
是否有特定用途? Edge ?我认为它是Array.prototype.forEach
并且IE的最近版本(以及Edge的所有版本)都有它??
答案 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;
}
在这种情况下,直接分配很好,因为enumerable
,configurable
和writable
都应该是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;
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;
};
}