document.querySelectorAllが実際の配列ではなくStaticNodeListを返すのはなぜですか?


103

document.querySelectorAll(...).map(...)はFirefox 3.6でさえも実行できないだけでなく、答えを見つけることができないというバグを抱えています。そのため、このブログからの質問をSOにクロスポストすると思いました。

http://blowery.org/2008/08/29/yay-for-queryselectorall-boo-for-staticnodelist/

アレイを取得できない技術的な理由を知っている人はいますか?または、なぜStaticNodeListは、あなたが使用できるようにアレイから継承していないmapconcatなど、?

(ところで、必要な機能が1つだけの場合は、次のようなことを行うことができNodeList.prototype.map = Array.prototype.map;ますが、この機能が(意図的に?)そもそもなぜブロックされているのですか?)


3
実際には、getElementsByTagNameは配列ではなくコレクションも返します。配列(concatなどのメソッドを使用)のように使用したい場合は、ループを実行してそのようなコレクションを配列に変換し、コレクションを配列に。これについて文句を言う人はいません。
Marco Demaio、2010

回答:


81

それはW3Cの哲学的決定であると私は信じています。W3C DOM [仕様]の設計は、DOMがプラットフォームや言語に中立であること意図しているため、JavaScriptの設計と完全に直交しています。

「reordered a getElementsByFoo()ordered NodeList」または「querySelectorAll()returns 」などの決定StaticNodeListは非常に意図的であるため、実装は言語依存の実装に基づいて返されたデータ構造の整列を心配する必要はありません(.mapJavaScriptやRubyの配列で利用可能など)。C#のリストにはありません)。

W3C照準低:彼らは言うよNodeList含める必要が読み取り専用.lengthunsigned long型のプロパティを、彼らは少なくともサポートですべての実装缶を信じているからということが、彼らがいることを明示的に言うことはありません[]インデックスオペレータは、位置の要素を取得してサポートするために、オーバーロードされなければなりません、彼らは、実装したいgetElementsByFoo()がオペレーターのオーバーロードをサポートできない、一緒にやって来る貧しい小さな言語を邪魔したくないからです。これは、仕様の多くに存在する一般的な哲学です。

John Resigはあなたと同様のオプション表明しましたが、それに追加しました

私の主張は、それほど NodeIteratorDOMに似ているわけではなく、JavaScriptにあまり似ていないということです。JavaScript言語に存在する機能を利用せず、その能力を最大限に活用します...

やや共感します。DOMがJavaScript機能を念頭に置いて具体的に記述されていれば、使いやすく、直感的に使用できます。同時に、W3Cの設計上の決定についても理解しています。


ありがとう、それは私が状況を理解するのに役立ちます。
Kev

@Kev:そのブログ記事のページでStaticNodeList、配列に変換する方法を尋ねるコメントを見ました。@ mck89の回答をNodeList/ StaticNodeListをネイティブ配列に変換する方法として推奨しますが、これらのオブジェクトはhosted / "special"であるため、IE(8 obv)ではJScriptエラーで失敗します。
クレセントフレッシュ

確かに、それが私が彼に賛成した理由です。他の誰かが私の+1をキャンセルしました。hosted / specialとはどういう意味ですか?
Kev

1
@Kev:ホスト変数は、「ホスト」環境(例:Webブラウザー)によって提供される変数です。例えばdocumentwindow等は、多くの場合、これらの「特別」時々のような小さく、微妙な方法で通常の使用に適合しないこと(COMオブジェクトとして)実装IE Array.prototype.slice.call爆撃所与StaticNodeList);
クレセントフレッシュ

199

ES2015(ES6)スプレッド演算子を使用できます。

[...document.querySelectorAll('div')]

StaticNodeListを項目の配列に変換します。

使用例を以下に示します。

[...document.querySelectorAll('div')].map(x => console.log(x.innerHTML))
<div>Text 1</div>
<div>Text 2</div>


24
もう一つの方法は、使用することですArray.fromを() Array.from(document.querySelectorAll('div')).map(x => console.log(x.innerHTML))
マイケルBerdyshev

42

配列ではなくノードリストを返す理由がわかりません。おそらく、getElementsByTagNameのように、DOMを更新すると結果が更新されるためです。とにかく、結果を単純な配列に変換する非常に単純な方法は次のとおりです。

Array.prototype.slice.call(document.querySelectorAll(...));

そして、あなたは行うことができます:

Array.prototype.slice.call(document.querySelectorAll(...)).map(...);

3
実際、DOMを更新しても結果は更新されません。つまり、「静的」です。結果を更新するには、手動でqSAを再度呼び出す必要があります。sliceただし、回線の+1 。
Kev 2010

1
ええ、ケブが言ったように:qSA結果セットは静的で、getElementsByTagName()結果セットは動的です。
joonas.fi 2015

IE8は、標準モードでquerySelectorAll()のみをサポートします
mbokil

13

クレッセントの発言に加えて、

必要な関数が1つだけの場合は、NodeList.prototype.map = Array.prototype.mapのようにすることができます。

これを行わないでください!動作が保証されているわけではありません。

JavaScriptまたはDOM / BOM標準では、NodeListコンストラクター関数がglobal / windowプロパティとして存在すること、NodeList戻り値がquerySelectorAllそれから継承すること、またはプロトタイプが書き込み可能であること、または関数Array.prototype.mapが実際にNodeListで機能することを指定していません。

NodeListは「ホストオブジェクト」になることができます(IEや一部の古いブラウザでは、これが1つです)。Array方法は、数値および公開する任意のJavaScriptを「ネイティブオブジェクトの操作が許されるものとして定義されているlengthプロパティが、それらはホストオブジェクト上で作業する必要はありません(とIEで、そうではない)しています。

DOMリストのすべての配列メソッド(StaticNodeListだけでなく、すべて)を取得できないのは不愉快ですが、信頼できる方法はありません。取得したすべてのDOMリストを手動で配列に変換する必要があります。

Array.fromList= function(list) {
    var array= new Array(list.length);
    for (var i= 0, n= list.length; i<n; i++)
        array[i]= list[i];
    return array;
};

Array.fromList(element.childNodes).forEach(function() {
    ...
});

1
シュート、私はそれを考えていなかった。ありがとう!
Kev 2010

+1に同意します。単なるコメントですが、「var array = new Array(list.length)」の代わりに「var array = []」を実行すると、コードがさらに短くなると思います。しかし、これを行う際に問題が発生する可能性があることをご存知でしたら、私は興味を持っています。
Marco Demaio、2010

@MarcoDemaio:いいえ、問題ありません。new Array(n)JS terpに、配列が終了するまでの時間のヒントを与えるだけです。これにより、事前にその量のスペースを割り当てることができます。これにより、配列が大きくなるにつれて一部のメモリの再割り当てを回避できるため、速度が向上する可能性があります。それが最近のブラウザーで実際に役立つかどうかはわかりませんが...私は測定できないと思います。
ボビンス


2

あなたは単に以下を行うことができると思います

Array.prototype.map.call(document.querySelectorAll(...), function(...){...});

それは私にぴったりです


0

これは私がここで他の人が提案した他の可能性の範囲に追加したかったオプションです。知的な楽しみのためだけのものであり、推奨されません


それを楽しむために、querySelectorAllひざまずいてお辞儀をする「強制」する方法を次に示します。

Element.prototype.querySelectorAll = (function(QSA){
    return function(){
        return [...QSA.call(this, arguments[0])]
    }
})(Element.prototype.querySelectorAll);

さて、その機能全体を踏んで、誰が上司であるかを示すのは良い感じです。今私は何が良いのか分かりません、まったく新しい名前付き関数ラッパーを作成してから、すべてのコードでその奇妙な名前を使用するか(かなりjQueryスタイル)、上記のように関数を一度オーバーライドして、残りのコードを引き続き実行できるようにします元のDOMメソッド名を使用するquerySelectorAll

あなたが正直に[あなたが何を知っているか]を与えない限り、私はこれを決してお勧めしません。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.