Nodeオブジェクト

継承関係

  • Node (ドキュメント ツリーのノード)

ノードの追加

Node appendChild( Node newChild );
Node.appendChild - DOM | MDN

追加するnewChildがすでにドキュメントに存在している場合には、それが新しい位置へ移動させられます。このnewChildnullを指定することは認められません。

またこのappendChild()は追加するノードnewChildを返すため、そのノードにさらにノードを追加する、

node0.appendChild( node1 );
node1.appendChild( node2 );

のような処理は、次のように続けて記述できます。

node0.appendChild( node1 ).appendChild( node2 );

これはリストに連続してノードを追加するような処理を、簡潔に記述できます。

var list = document.createElement( 'ul' );
list.appendChild( document.createElement( 'li' ) ).appendChild( node1 );
list.appendChild( document.createElement( 'li' ) ).appendChild( node2 );

ところでappendChild()は、ドキュメント ツリーの最後にノードを追加します。一方でドキュメント ツリーの最初にノードを追加するには、

node.insertBefore( newChild, node.firstChild );

のように、ノードの最初の子の直前に追加するようにします。

指定要素の直前へ追加

ドキュメント ツリーの指定要素の直前にノードを追加します。

Node insertBefore( Node newChild, Node refChild );
Node.insertBefore - DOM | MDN

このノードのchildNodes[]配列のrefChildの直前にnewChildを追加し、newChildを返します。

  • newChildがすでにドキュメントに存在している場合 … appendChild()と同様に、それが新しい位置へ移動させられる
  • refChildnullの場合 … appendChild()と同様に、ツリーの最後に追加される
  • refChildがこのノードの子ではない場合 … 例外が発生する

ノード自身の直前へ追加

node.parentNode.insertBefore( newChild, node );

親要素のメソッドを利用し、自身の直前にノードを追加するようにします。

指定要素の直後へ追加

指定要素の直後にノードを追加するメソッドは用意されていませんが、指定要素の直後のノードをnextSiblingで取得し、その直前にinsertBefore()をすることで所望の動作を実現できます。このとき指定要素が最後の場合にはnextSiblingはnullを返しますが、insertBefore()の第2引数にnullを渡した場合にはドキュメント ツリーの最後にノードが追加されるため、このようなときも正しく処理されます。

node.insertBefore( newChild, refChild.nextSibling );

ノード自身の直後へ追加

基本的に、前述のノード自身の直前へ追加する方法と同じです。違いは追加する場所を、ノード自身の直後のノードの直前とすることです。

node.parentNode.insertBefore( newChild, node.nextSibling );

親要素に追加

要素を子として追加するならばappendChild()で簡単に実現できますが、親として追加するには工夫が必要です。nodeに親となる要素parentを追加する場合を考えると、

parent.appendChild( node.parentNode.replaceChild( parent, node ) );

となります。これは、

// nodeの親ノードからnodeを削除し、parentを追加する。
// 結果として削除されたnodeが返される。
node = node.parentNode.replaceChild( parent, node );

// parentにnodeを追加する。
parent.appendChild( node );

のように処理しています。

スクリプトを実行している場所に追加

スクリプトを実行している場所にHTML要素を追加で解説しています。

ノードの削除

指定のノードを削除します。

Node removeChild( Node oldChild );
Node.removeChild - Web API リファレンス | MDN

削除するoldChildはこのノードの子でなければならず、さもなくばNotFoundError例外が発生します。よってノードが存在するか不明瞭ならば、先にcontains()でその存在を確認します。

ノードが削除されたときにはそのノード、つまりoldChildが返されます。

すべての子ノードの削除

すべての子ノードを削除するには、子ノードが存在しなくなるまでノードの削除をくり返します。

while( node.hasChildNodes() )
{
    node.removeChild( node.firstChild );
}

hasChildNodes()メソッドで子ノードを持つか確認できます。子ノードがあるならばremoveChild()メソッドで、最初の子ノード (firstChild) を削除します。この処理を子ノードがなくなるまで、whileループでくり返します。

またはノードが子を持たない場合はnullを返すのを利用して、

while( node.firstChild )
{
    node.removeChild( node.firstChild );
}

のようにも記述できます。

innerHTML

Element.innerHTMLは、要素内に含まれるHTMLテキストを表します。よってこれに空文字を設定することでも、子ノードをすべて削除できます。

node.innerHTML = '';

しかしこの方法は、Internet Explorerでは予期せぬ挙動を示すために注意が必要です。次のコードを用いて実験してみます。

var a = document.createElement( 'div' );
var b = document.createElement( 'div' );
var c = document.createElement( 'div' );

a.appendChild( b );  // aの子ノードにbを追加
b.appendChild( c );  // bの子ノードにcを追加

alert( b.firstChild );  // *1
a.innerHTML = '';
alert( b.firstChild );  // *2

2か所のalert()では、下表のように表示されます。

alert()の出力結果
コードの場所 Firefox / Chrome IE6 / IE7 IE8
*1 object HTMLDivElement object object HTMLDivElement
*2 object HTMLDivElement null null

このことからIEでは、innerHTML = ''で削除した子ノードの子ノードまで削除されてしまうことがわかります。

ノード自身の削除

ノード自身の削除は、親ノードの子ノードから削除することで実現できます。

node.parentNode.removeChild( node );

たとえば、

<div>BBB<span id="a">AAA</span></div>

のようなHTMLのコードがあるとき、次のようにノードを削除すると、

var node = document.getElementById( 'a' );
node.parentNode.removeChild( node );

削除後には、

<div>BBB</div>

となります。

子要素を残して、指定要素だけを削除

前項の方法では、ノードを削除するときその子要素も削除されます。たとえば、

<div>BBB<span id="a">AA<b>X</b></span></div>

のようなHTMLで、id="a"の要素を削除すると、

<div>BBB</div>

となります。これが期待する動作ならば問題ありませんが、子要素を残したいならば少し工夫が必要です。

var node = document.getElementById( 'a' );

var parent = node.parentNode;
while( node.firstChild )
{
    // 指定要素の子要素を、親要素の子要素となるように移動
    parent.insertBefore( node.firstChild, node );
}

// 指定要素を削除
parent.removeChild( node );

この方法では指定要素のすべての子要素を、親要素の子要素、つまり指定要素の兄弟要素となる位置に移動しています。そして最後に指定要素だけを削除することで、子要素が削除されないようにしています。この結果は、次のようになります。

<div>BBBAA<b>X</b></div>

ちなみにこの方法では、子要素に登録されたイベントハンドラも削除されません。

ノードの複製

Node cloneNode( boolean deep );
Node.cloneNode | MDN

ノードの複製を取得できます。そのとき引数にtrueを指定すると、その派生ノードも含みます。

Elementノードを対象とした場合には、そのすべての属性も複製されます。ただしイベントハンドラは複製されません

cloneNode()ではid属性も複製されるため、複製したノードをドキュメントに追加すると、ドキュメント内でidが重複する恐れがあります。

サンプルコード

var link = document.createElement( 'a' );
link.style.color = 'red';
link.appendChild( document.createTextNode( 'Click ' ) );
link.onclick = function() { alert( 'OK' ); }; // イベントハンドラ

document.body.appendChild( link );
document.body.appendChild( link.cloneNode( true ) );
document.body.appendChild( link.cloneNode( false ) );

複製元のノードと、それを複製したノード2つの合計3つのノードを、以下に並べて表示します。2つ目のノードはクリックに反応せず、3つ目のノードは派生ノードを持たないため表示されないはずです。

ノードの置換

Node replaceChild( Node newChild, Node oldChild );
Node.replaceChild - Web API リファレンス | MDN

指定のノードを削除し、新しいノードに置換します。削除するoldChildは、このノードの子でなければなりません。戻り値では削除されたノード、つまりoldChildが返されます。

置換するのがTextなどのCharacterDataの実装ならば、そのdataプロパティを書き換えるだけで内容を置換できます。

サンプルコード

たとえば処理の進捗を表示するのに、次のようにreplaceChild()を使用できます。このとき必ず置き換えられるノード (前述のoldChild) を指定しなくてはならないため、あらかじめ「&nbsp;」などの要素を設定しておきます。

<p><span id="node">&nbsp;</span></p>

<script type="text/javascript">
for( var i = 0; i <= 100; i++ )
{
  var node = document.getElementById( 'node' );
  var newNode = document.createTextNode( i );

  node.replaceChild( newNode, node.firstChild );

  // innerHTMLで記述するならば、
  // node.innerHTML = i;
}
</script>

タグの変更

ノードがElementオブジェクトのとき、そのタグ名 (タグの種類) を変更する場合を考えます。たとえば、

<div><a id="a">ABC</a></div>

のようなHTMLのコードがあったとき、これを

<div><b id="a">ABC</b></div>

に変更する場合です。そのためには、

var oldNode = document.getElementById( 'a' );

var newNode = document.createElement( 'b' );
newNode.id = oldNode.id;
newNode.appendChild( oldNode.firstChild.cloneNode( true ) );

oldNode.parentNode.replaceChild( newNode, oldNode );

とします。変更するタグ (この場合は<a id="a">) に複数の子要素があることを考慮するならば、

var oldNode = document.getElementById( 'a' );

var newNode = document.createElement( 'b' );
newNode.id = oldNode.id;

for( var i = 0; i < oldNode.childNodes.length; i++ )
{
    newNode.appendChild( oldNode.childNodes[ i ].cloneNode( true ) );
}

oldNode.parentNode.replaceChild( newNode, oldNode );

とします。一方でinnerHTMLを使用するならば、子要素の数と無関係に

var oldNode = document.getElementById( 'a' );

var newNode = document.createElement( 'b' );
newNode.id = oldNode.id;
newNode.innerHTML = oldNode.innerHTML;

oldNode.parentNode.replaceChild( newNode, oldNode );

と記述できます。

ノードの移動

appendChild()とinsertBefore()では、追加するノードがすでにドキュメントに存在している場合には、それが新しい位置へ移動させられます。これを利用することでノードを移動できます。

var node1 = document.createElement( 'div' );
var node2 = document.createElement( 'div' );
var node3 = document.createElement( 'div' );

node1.id = '1';
node2.id = '2';
node3.id = '3';

document.body.appendChild( node1 );
document.body.appendChild( node2 );
document.body.appendChild( node3 );

document.body.insertBefore( node3, node1 );

// <div id="3"></div>
// <div id="1"></div>
// <div id="2"></div>

ノードの順番の並べ替え

insertBefore()を共通の親を持つノードに適用することで、ノードの順番を並べ替えることができます。それはたとえば、

<div id="list"><b>0</b><b>1</b><b>2</b><b>3</b><b>4</b></div>

のようなノードがあり、これが

01234

のように表示されるとき、

var list = document.getElementById( 'list' );
list.insertBefore( list.childNodes[ 2 ], list.childNodes[ 1 ]  );

このようにinsertBefore()で[1]の要素の直前に[2]の要素を追加することで、

02134

のように、ノードの順番を並べ替えられることを意味します。もしこのとき、

list.insertBefore( list.childNodes[ 3 ], list.childNodes[ 1 ] );

のように[1]の直前に[3]を追加するならば、

03124

の順番となります。

ノードの探索

あるノードを基準として、それと親子関係にあるノードを取得する方法について解説します。それがElementオブジェクトならば、id属性やname属性から取得する方法もあります。またInternet Explorer 8以降などの新しいブラウザを対象とするならば、CSSの構文を用いられるセレクタAPIでもノードを取得できます。

関係 プロパティ 説明
parentNode 親ノード
兄弟 previousSibling 直前のノード
nextSibling 直後のノード
childNodes すべての子ノード
firstChild 最初の子ノード
lastChild 最後の子ノード

ノードがHTMLCollectionオブジェクトを返すプロパティを持つならば、これとは異なる方法でノードを参照できます。

下記の構造のドキュメントを例に、id="B2"を基準としたときの各プロパティが参照するノードを示します。ただし次項で示すように、Internet Explorer以外ではこのような結果となりません。

<div id="A">                 // parentNode
    <div id="B1"></div>      // previousSibling
    <div id="B2">            // 基準
        <div id="C1"></div>  // childNodes[ 0 ] および firstChild
        <div id="C2"></div>  // childNodes[ 1 ]
        <div id="C3"></div>  // childNodes[ 2 ] および lastChild
    </div>
    <div id="B3"></div>      // nextSibling
</div>

ブラウザによる解釈の相違

前述のドキュメントに対して次のコードを実行し、ブラウザごとの相違を調査します。

<pre>
<script type="text/javascript">
  var element = document.getElementById( 'B2' );

  document.writeln( element.parentNode.id );

  document.writeln( element.previousSibling.id );
  document.writeln( element.nextSibling.id );

  document.writeln( element.firstChild.id );
  document.writeln( element.lastChild.id );
</script>
</pre>
ブラウザ コードの実行結果 childNodesの値
IE6
A
B1
B3
C1
C3
 
IE7
IE8
Firefox 3
A
undefined
undefined
undefined
undefined

(Firebugによる取得結果)
Chrome 4
A
undefined
undefined
undefined
undefined

(Developer Toolsによる取得結果)

このような違いは、空白や改行をノードとして認識するかどうかによって生じます。よって、

<div id="A"><div id="B1"></div><div id="B2"><div id="C1"></div><div id="C2"></div><div id="C3"></div></div><div id="B3"></div></div>

のように空白などを含まない表記とすれば、ブラウザによる相違は発生しません。

一方で、新しくElementに実装されたプロパティ (Element Traversal Specification) を用いれば、空白などを除外してノードを取得できます。

Nodeと新しく実装されたElementのプロパティとの対比
関係 プロパティ
Node Element
parentNode -
兄弟 previousSibling previousElementSibling
nextSibling nextElementSibling
childNodes children
firstChild firstElementChild
lastChild lastElementChild
Element Traversal Specification - W3C

ノードの存在確認

contains()で確認できます。

boolean node.contains( otherNode )
Node.contains - Web API インターフェイス | MDN

ノードの子孫にotherNodeがあるならば、trueが返されます。

ノードの種類の確認

nodeTypeプロパティで確認できます。Node.nodeType - Web APIs | MDN

Constant Value Description
Node.ELEMENT_NODE 1 An Element node such as <p> or <div>.
Node.TEXT_NODE 3 The actual Text of Element or Attr.
Node.PROCESSING_INSTRUCTION_NODE 7 A ProcessingInstruction of an XML document such as <?xml-stylesheet ... ?> declaration.
Node.COMMENT_NODE 8 A Comment node.
Node.DOCUMENT_NODE 9 A Document node.
Node.DOCUMENT_TYPE_NODE 10 A DocumentType node e.g. <!DOCTYPE html> for HTML5 documents.
Node.DOCUMENT_FRAGMENT_NODE 11 A DocumentFragment node.
非推奨
Constant Value Description
Node.ATTRIBUTE_NODE 2 An Attribute of an Element. The element attributes are no longer implementing the Node interface in DOM4 specification.
Node.CDATA_SECTION_NODE 4 A CDATASection. Removed in DOM4 specification.
Node.ENTITY_REFERENCE_NODE 5 An XML Entity Reference node. Removed in DOM4 specification.
Node.ENTITY_NODE 6 An XML <!ENTITY ...> node. Removed in DOM4 specification.
Node.NOTATION_NODE 12 An XML <!NOTATION ...> node. Removed in DOM4 specification.

ノードのテキストの取得と設定

textContentにより、そのノードおよびその子孫のテキストへアクセスできます。このプロパティはInternet Explorer 9以降でサポートされます。Node.textContent - Web API インターフェイス | MDN