Please note, this is a STATIC archive of website developer.mozilla.org from 03 Nov 2016, cach3.com does not collect or store any user information, there is no "phishing" involved.

Revision 1066290 of NodeList

  • リビジョンの URL スラグ: Web/API/NodeList
  • リビジョンのタイトル: NodeList
  • リビジョンの ID: 1066290
  • 作成日:
  • 作成者: unarist
  • 現行リビジョン? いいえ
  • コメント 英語版 rev.1057098 を途中まで翻訳

このリビジョンの内容

{{APIRef("DOM")}}

NodeList オブジェクトは {{domxref("Node.childNodes")}} や {{domxref("document.querySelectorAll")}} などのメソッドから返されるノードのコレクションを表します。

プロパティ

length
NodeList に含まれるノードの数

メソッド

item ( idx )
インデックスで指定した要素、もしくはインデックスが範囲外の場合は null を返します。範囲外の場合に  undefined を返す nodeList[idx] の代わりにこのメソッドを使うことができます。

説明

生きたコレクションとしての側面

NodeList は DOM の変化を反映する、生きたコレクションになっていることがあります。 {{domxref("Node.childNodes")}} もその一つです。

var parent = document.getElementById('parent');
var child_nodes = parent.childNodes;
console.log(child_nodes.length); // "2" だったものが
parent.appendChild(document.createElement('div'));
console.log(child_nodes.length); // "3" になる

一方で、DOM の変化を一切反映しない静的なコレクションになっていることもあります。例えば {{domxref("document.querySelectorAll")}} は静的な NodeList を返します。

NodeList の要素を列挙したり、要素の数をキャッシュするときには、この違いに注意した方がよいでしょう。

なぜ NodeList は Array ではないのか

NodeList は配列と似た使い方をすることが多く、 Array.prototype のメソッドを使いたくなりますが、 NodeList はおなじみの Array メソッドを一切備えていません。

JavaScript は Array などのビルトインオブジェクトと NodeLIst などのホストオブジェクトのどちらにもプロトタイプベースの継承メカニズムを使います。 Array のインスタンスが forEach や map などのメソッドを備えているのは、以下のようなプロトタイプチェーンになっているからです。

myArray --> Array.prototype --> Object.prototype --> null (オブジェクトのプロトタイプチェーンは Object.getPrototypeOf を繰り返し呼ぶことで調べることができます)

forEach や map は Array.prototype オブジェクト自身が持つプロパティです。

一方 NodeList のプロトタイプチェーンは次のようになっています。

myNodeList --> NodeList.prototype --> Object.prototype --> null

NodeList.prototype には item メソッドがありますが、 Array.prototype のメソッドは含まれていないため、これらを NodeLists で使うことはできないのです。

回避策

まず、 Array.prototype のメソッドを NodeList.prototype に追加する方法があります。ただし DOM の拡張は危険なことであり、特に古い Internet Explorer (6, 7, 8) の挙動には注意が必要です

var arrayMethods = Object.getOwnPropertyNames( Array.prototype );

arrayMethods.forEach( attachArrayMethodsToNodeList );

function attachArrayMethodsToNodeList(methodName)
{
  if(methodName !== "length") {
    NodeList.prototype[methodName] = Array.prototype[methodName];
  }
};
 
var divs = document.getElementsByTagName( 'div' );
var firstDiv = divs[ 0 ];

firstDiv.childNodes.forEach(function( divChild ){
  divChild.parentNode.style.color = '#0F0';
});

もう一つの方法では DOM を拡張しません。

var forEach = Array.prototype.forEach;

var divs = document.getElementsByTagName( 'div' );
var firstDiv = divs[ 0 ];

forEach.call(firstDiv.childNodes, function( divChild ){
  divChild.parentNode.style.color = '#0F0';
});

この方法ではホストオブジェクト (NodeList など) を this としてネイティブメソッド (forEach など) に渡していますが、全てのブラウザで動くことが保証されているわけではなく、動かないケースもあることが知られています。

It's possible to loop over the items in a NodeList using:

for (var i = 0; i < myNodeList.length; ++i) {
  var item = myNodeList[i];  // Calling myNodeList.item(i) isn't necessary in JavaScript
}

Don't be tempted to use for...in or for each...in to enumerate the items in the list, since that will also enumerate the length and item properties of the NodeList and cause errors if your script assumes it only has to deal with {{domxref("element")}} objects. Also, for..in is not guaranteed to visit the properties in any particular order.

for...of loops will loop over NodeList objects correctly, in browsers that support for...of (like Firefox 13 and later):

var list = document.querySelectorAll( 'input[type=checkbox]' );
for (var item of list) {
  item.checked = true;
}

 NodeList を Array に変換する

NodeList の中身を慣れ親しんだ Array のメソッドで扱いたいこともあるでしょう。こうすると NodeList オブジェクトを Array に変換できます。

var div_list = document.querySelectorAll('div'); // returns NodeList
var div_array = Array.prototype.slice.call(div_list); // converts NodeList to Array

Spread operator をサポートするブラウザではこれを使うこともできます。

var div_list = document.querySelectorAll('div'); // returns NodeList
var div_array = [...div_list]; // converts NodeList to Array

もしくは Array.from() メソッドを使ってもよいでしょう。

var div_list = document.querySelectorAll('div'); // returns NodeList
var div_array = Array.from(div_list); // converts NodeList to Array

Another possibility is to use {{jsxref("Object.keys()")}} and {{jsxref("Array.prototype.forEach()")}} (for which polyfills are available):

var div_list = document.querySelectorAll('div'); // returns NodeList
Object.keys(div_list).forEach(function(key) {
  console.log(div_list[key]);
})

仕様

仕様書 策定状況 コメント
{{SpecName('DOM WHATWG', '#interface-nodelist', 'NodeList')}} {{ Spec2('DOM WHATWG') }}  
{{SpecName('DOM4', '#interface-nodelist', 'NodeList')}} {{ Spec2('DOM4') }}  
{{SpecName('DOM3 Core', 'core.html#ID-536297177', 'NodeList')}} {{ Spec2('DOM3 Core') }}  
{{SpecName('DOM2 Core', 'core.html#ID-536297177', 'NodeList')}} {{ Spec2('DOM2 Core') }}  
{{SpecName('DOM1', 'level-one-core.html#ID-536297177', 'NodeList')}} {{ Spec2('DOM1') }} Initial definition.

このリビジョンのソースコード

<div>{{APIRef("DOM")}}</div>

<p><code>NodeList</code>&nbsp;オブジェクトは {{domxref("Node.childNodes")}} や {{domxref("document.querySelectorAll")}} などのメソッドから返されるノードのコレクションを表します。</p>

<h2 id="Properties">プロパティ</h2>

<dl>
 <dt><code>length</code></dt>
 <dd>NodeList に含まれるノードの数</dd>
</dl>

<h2 id="Methods">メソッド</h2>

<dl>
 <dt><code><a href="/en-US/docs/DOM/NodeList.item" title="DOM/NodeList.item">item</a> ( idx )</code></dt>
 <dd>インデックスで指定した要素、もしくはインデックスが範囲外の場合は <code>null</code> を返します。範囲外の場合に &nbsp;<code>undefined</code>&nbsp;を返す&nbsp;<code>nodeList[idx]</code>&nbsp;の代わりにこのメソッドを使うことができます。</dd>
</dl>

<h2 id="Description">説明</h2>

<h3 id="A_sometimes-live_collection">生きたコレクションとしての側面</h3>

<p><code>NodeList</code>&nbsp;は DOM の変化を反映する、<em>生きたコレクション</em>になっていることがあります。&nbsp;{{domxref("Node.childNodes")}} もその一つです。</p>

<pre class="brush: js">
var parent = document.getElementById('parent');
var child_nodes = parent.childNodes;
console.log(child_nodes.length); // "2" だったものが
parent.appendChild(document.createElement('div'));
console.log(child_nodes.length); // "3" になる
</pre>

<p>一方で、DOM の変化を一切反映しない<em>静的なコレクション</em>になっていることもあります。例えば&nbsp;{{domxref("document.querySelectorAll")}} は静的な NodeList を返します。</p>

<p>NodeList の要素を列挙したり、要素の数をキャッシュするときには、この違いに注意した方がよいでしょう。</p>

<h3 id="Why_is_NodeList_not_an_Array"><code>なぜ NodeList</code> は Array&nbsp;ではないのか</h3>

<p><code>NodeList</code>&nbsp;は配列と似た使い方をすることが多く、&nbsp;<code>Array.prototype</code>&nbsp;のメソッドを使いたくなりますが、&nbsp;<code>NodeList</code> はおなじみの Array メソッドを一切備えていません。</p>

<p>JavaScript は Array&nbsp;などのビルトインオブジェクトと NodeLIst などのホストオブジェクトのどちらにもプロトタイプベースの継承メカニズムを使います。&nbsp;Array のインスタンスが&nbsp;<code>forEach</code>&nbsp;や&nbsp;<code>map</code>&nbsp;などのメソッドを備えているのは、以下のようなプロトタイプチェーンになっているからです。</p>

<p><code>myArray --&gt; Array.prototype --&gt; Object.prototype --&gt; null</code> (オブジェクトのプロトタイプチェーンは&nbsp;<code>Object.getPrototypeOf</code>&nbsp;を繰り返し呼ぶことで調べることができます)</p>

<p><code>forEach や</code>&nbsp;<code>map</code>&nbsp;は&nbsp;<code>Array.prototype</code>&nbsp;オブジェクト自身が持つプロパティです。</p>

<p>一方&nbsp;<code>NodeList</code>&nbsp;のプロトタイプチェーンは次のようになっています。</p>

<p><code>myNodeList --&gt; NodeList.prototype --&gt; Object.prototype --&gt; null</code></p>

<p><code>NodeList.prototype</code>&nbsp;には&nbsp;<code>item</code>&nbsp;メソッドがありますが、&nbsp;<code>Array.prototype</code> のメソッドは含まれていないため、これらを NodeLists で使うことはできないのです。</p>

<h4 id="Workarounds">回避策</h4>

<p><code>まず、 Array.prototype</code>&nbsp;のメソッドを&nbsp;<code>NodeList.prototype</code> に追加する方法があります。ただし&nbsp;<a href="https://perfectionkills.com/whats-wrong-with-extending-the-dom/">DOM の拡張は危険なことであり、特に古い Internet Explorer (6, 7, 8) の挙動には注意が必要です</a>。</p>

<pre class="brush: js">
var arrayMethods = Object.getOwnPropertyNames( Array.prototype );

arrayMethods.forEach( attachArrayMethodsToNodeList );

function attachArrayMethodsToNodeList(methodName)
{
  if(methodName !== "length") {
    NodeList.prototype[methodName] = Array.prototype[methodName];
  }
};
&nbsp;
var divs = document.getElementsByTagName( 'div' );
var firstDiv = divs[ 0 ];

firstDiv.childNodes.forEach(function( divChild ){
&nbsp; divChild.parentNode.style.color = '#0F0';
});</pre>

<p>もう一つの方法では DOM を拡張しません。</p>

<pre class="brush: js">
var forEach = Array.prototype.forEach;

var divs = document.getElementsByTagName( 'div' );
var firstDiv = divs[ 0 ];

forEach.call(firstDiv.childNodes, function( divChild ){
&nbsp; divChild.parentNode.style.color = '#0F0';
});
</pre>

<div class="note">
<p>この方法ではホストオブジェクト (NodeList など) を&nbsp;<code>this</code>&nbsp;としてネイティブメソッド&nbsp;(<code>forEach など</code>) に渡していますが、全てのブラウザで動くことが保証されているわけではなく、動かないケースもあることが知られています。</p>
</div>

<h2 id="Example">例</h2>

<p>It's possible to loop over the items in a <code>NodeList</code> using:</p>

<pre class="brush: js">
for (var i = 0; i &lt; myNodeList.length; ++i) {
  var item = myNodeList[i];  // Calling myNodeList.item(i) isn't necessary in JavaScript
}
</pre>

<p>Don't be tempted to use <code><a href="/en-US/docs/JavaScript/Reference/Statements/for...in" title="JavaScript/ Reference/Statements/for...in">for...in</a></code> or <code><a href="/en-US/docs/JavaScript/Reference/Statements/for_each...in" title="JavaScript/ Reference/Statements/for each...in">for each...in</a></code> to enumerate the items in the list, since that will also enumerate the length and item properties of the <code>NodeList</code> and cause errors if your script assumes it only has to deal with {{domxref("element")}} objects. Also, <code>for..in</code> is not guaranteed to visit the properties in any particular order.</p>

<p><code><a href="/en-US/docs/JavaScript/Reference/Statements/for...of" title="/en-US/docs/JavaScript/Reference/Statements/for...of">for...of</a></code> loops will loop over NodeList objects correctly, in browsers that support <code>for...of </code>(like Firefox 13 and later):</p>

<pre class="brush: js">
var list = document.querySelectorAll( 'input[type=checkbox]' );
for (var item of list) {
  item.checked = true;
}</pre>

<h2 id="Converting_a_NodeList_to_an_Array">&nbsp;NodeList を&nbsp;Array に変換する</h2>

<p>NodeList の中身を慣れ親しんだ Array のメソッドで扱いたいこともあるでしょう。こうすると&nbsp;NodeList オブジェクトを Array に変換できます。</p>

<pre class="brush: js">
var div_list = document.querySelectorAll('div'); // returns NodeList
var div_array = Array.prototype.slice.call(div_list); // converts NodeList to Array</pre>

<p><a href="/Web/JavaScript/Reference/Operators/Spread_operator" title="JavaScript/ Reference/Operators/Spread_operator">Spread operator</a>&nbsp;をサポートするブラウザではこれを使うこともできます。</p>

<pre class="brush: js">
var div_list = document.querySelectorAll('div'); // returns NodeList
var div_array = [...div_list]; // converts NodeList to Array</pre>

<p>もしくは <a href="/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/from">Array.from()</a> メソッドを使ってもよいでしょう。</p>

<pre class="brush: js">
var div_list = document.querySelectorAll('div'); // returns NodeList
var div_array = Array.from(div_list); // converts NodeList to Array</pre>

<p>Another possibility is to use {{jsxref("Object.keys()")}} and {{jsxref("Array.prototype.forEach()")}} (for which polyfills are available):</p>

<pre class="brush: js">
var div_list = document.querySelectorAll('div'); // returns NodeList
Object.keys(div_list).forEach(function(key) {
  console.log(div_list[key]);
})
</pre>

<h2 id="Specification" name="Specifications">仕様</h2>

<table class="standard-table">
 <thead>
  <tr>
   <th scope="col">仕様書</th>
   <th scope="col">策定状況</th>
   <th scope="col">コメント</th>
  </tr>
 </thead>
 <tbody>
  <tr>
   <td>{{SpecName('DOM WHATWG', '#interface-nodelist', 'NodeList')}}</td>
   <td>{{ Spec2('DOM WHATWG') }}</td>
   <td>&nbsp;</td>
  </tr>
  <tr>
   <td>{{SpecName('DOM4', '#interface-nodelist', 'NodeList')}}</td>
   <td>{{ Spec2('DOM4') }}</td>
   <td>&nbsp;</td>
  </tr>
  <tr>
   <td>{{SpecName('DOM3 Core', 'core.html#ID-536297177', 'NodeList')}}</td>
   <td>{{ Spec2('DOM3 Core') }}</td>
   <td>&nbsp;</td>
  </tr>
  <tr>
   <td>{{SpecName('DOM2 Core', 'core.html#ID-536297177', 'NodeList')}}</td>
   <td>{{ Spec2('DOM2 Core') }}</td>
   <td>&nbsp;</td>
  </tr>
  <tr>
   <td>{{SpecName('DOM1', 'level-one-core.html#ID-536297177', 'NodeList')}}</td>
   <td>{{ Spec2('DOM1') }}</td>
   <td>Initial definition.</td>
  </tr>
 </tbody>
</table>
このリビジョンへ戻す