web-dev-qa-db-ja.com

最近のMicrosoftブラウザーでquerySelectorAllのforEachが機能しない

Internet Explorer(11)&Edgeを除くすべてのブラウザーで機能する、製品に関する選択(色など)のスクリプトを作成しています。

各パラメーターの選択肢を配列に入れ、array.forEach()メソッドを使用してそれらに関数を適用します。

Colorパラメータの例:

_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 [〜#〜]Edgeの両方のコンソールに次の出力が表示されます。

オブジェクトはプロパティまたはメソッド「forEach」をサポートしていません

問題について検索したところ、この関数は IE 9以降 でサポートされているはずです)であることがわかりました。成功しました。関数をログに記録すると、関数として定義されます(「_[native code]_」を使用)。

私はすべての_.forEach_をforに置き換えました。

  • しかし、どうすればそれを機能させることができますか?
  • Internet ExplorerEdgeforEach()の特定の使用法はありますか?

私はそれが_Array.prototype.forEach_であり、IE(およびEdgeのすべてのバージョン))の最近のバージョンにそれがあると思いました...?

10
AymDev

querySelectorAll の戻り値は配列ではなく、 NodeList です。つい最近、forEach(およびJavaScriptのiterationプロトコルとの互換性があり、それらを_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;
}
_

この場合、enumerableconfigurable、およびwritableはすべてtrueである必要があり、値のプロパティであるため、直接割り当ては問題ありません。 (enumerabletrueであることには驚きましたが、それがChrome、Firefox、Edge、およびSafariでネイティブに定義されている方法です)。


NodeListforEachを取得すると、それもiterableになり、_for-of_ループを介してNodeListのコンテンツをループし、反復可能な他の場所でNodeListを使用できます期待されます(たとえば、配列初期化子のスプレッド表記)。

実際には、反復可能性を使用する機能(_for-of_ループなど)を備えたブラウザーも、NodeListのこれらの機能をすでに提供している可能性がありますが、(Symbolのポリフィルを変換して含めることを確実にするため)、 2番目のことを行う必要があります。イテレータを作成する_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>_

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

HTMLCollectionliveなので、コレクションの内容に影響を与えるDOMへの変更はすぐにコレクションに反映され、驚くべき結果になる可能性があることに注意してください動作。 (NodeListは切断されたコレクションであるため、動作は発生しません。)

18
T.J. Crowder

さて、ここから始めましょう。JavaScriptでは、それを呼び出す場合がありますArray-like、つまり、配列のように見えても、そうではありません実際の配列...

たとえば、arguments関数またはあなたの場合Nodelist...

IEおよびその他の一部のブラウザでは、たとえば、Nodelistの配列関数を使用してサポートされていない...

したがって、幅広いブラウザーをサポートしている場合は、ブラウザーでアクティビティを実行する前に、それらを配列に変換することをお勧めします...

Array-like値を実際のArrayに変換する方法はいくつかあります。 ..

ES5で広く使用されているのはこの構造です。

Array.prototype.slice.call(YourNodeList);

だからあなたはできる:

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

ただし、ES6を使用している場合は、より洗練された方法があります。たとえば、配列のようなループをサポートしていない古いブラウザーはES6を確実にサポートしないため、それらをbabelを使用してES5に変換してください。 ..

それらを行うための非常に一般的な2つの方法は次のとおりです。

1)Array.fromを使用する

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

2)[...配列]を使用

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

配列のように見えるかもしれませんが、実際には配列と同じ機能を持たない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;
    };
}
1
jERCle