二章第七回 木構造の操作:さまざまな機能2
このページの最終更新日:
兄弟ノード
DOMには、兄弟ノードを取得する方法もあります。
ここで、兄弟ノードとは、同じ親を持つノードのことです。例えば、
- BBB
- CCC
- DDD
という木構造があったとき、BBB,CCC,DDDは全て、同じAAAという親を持つ兄弟ノードです。
あるノードの兄弟ノードを取得するには、プロパティpreviousSiblingとnextSiblingを使います。
previousSiblingはそのノードの1つ前の兄弟、nextSiblingは1つ後の兄弟です。
実際のサンプルで見てみましょう。
<!doctype html>
<html>
<head>
<title>test</title>
</head>
<body>
<p><strong>a</strong><em id="aaa">b</em><ins>c</ins></p>
<script type="text/javascript">
var aaa = document.getElementById('aaa');
var prev = aaa.previousSibling;
var next = aaa.nextSibling;
console.log(prev.tagName);
console.log(next.tagName);
</script>
</body>
</html>
このサンプルの木構造はこうです。
- strong
- #text
- em
- #text
- ins
- #text
ここで、変数aaaには、"aaa"というidを持つ要素、つまりem要素のノードを代入しています。
prevとnextには、それぞれpreviousSiblingとnextSiblingが代入されています。
つまり、
- strong ← previousSibling
- #text
- em
- #text
- ins ← nextSibling
- #text
このようになっているから、prevにはstrong要素のノード、nextにはins要素のノードが代入されているはずです。
それを確かめているのが次の2行で、tagNameを表示しています。予想通り、「STRONG」と「INS」が表示されるはずです。
ちなみに、兄弟ノードを全てまとめて(NodeListの形で)取得することは、今までの知識でもできます。aaa.parentNode.childNodes
ですね。
このpreviousSiblingとnextSiblingですが、少し不便な場合もあります。それは、間にテキストノードがある場合です。次のサンプルを見てください。
<!doctype html>
<html>
<head>
<title>test</title>
</head>
<body>
<p>段落1です。</p>
<p id="abc">段落2です。</p>
<p>段落3です。</p>
<script type="text/javascript">
var p = document.getElementById('abc');
var prev = p.previousSibling;
var next = p.nextSibling;
console.log(prev);
console.log(next);
</script>
</body>
</html>
変数pに、3つあるうちの真ん中のp要素を代入しました。このとき、前後のp要素を取得したくても、previousSiblingやnextSiblingに入っているのはテキストノードです。なぜなら、上のHTMLではp要素の間に改行があり、それがテキストノードとして現れているからです。このテキストノードは厄介な場合があります。まず、空白とか改行によって発生するテキストノードは作成者が意識していない場合が多く、プログラムのバグにつながります。また、近年、HTMLを最小化(余計な空白や改行を全部取り除く)してから配信することも行われています。この場合、最小化の前後でテキストノードの有無が変わってしまいます。
このような問題に対処できる便利なプロパティが、previousElementSiblingとnextElementSiblingです。これは、それぞれ「前の要素ノード」「次の要素ノード」を取得できます。つまり、間にあるテキストノード等を無視してくれます。これを用いれば、前のサンプルでも前後のp要素を取得できます。
<!doctype html>
<html>
<head>
<title>test</title>
</head>
<body>
<p>段落1です。</p>
<p id="abc">段落2です。</p>
<p>段落3です。</p>
<script type="text/javascript">
var p = document.getElementById('abc');
var prev = p.previousElementSibling;
var next = p.nextElementSibling;
console.log(prev);
console.log(next);
</script>
</body>
</html>
replaceChild
次に紹介するreplaceChildは、ノードが持つプロパティで、そのノードが持つある子ノードを、別のノードに置き換えます。より正確には、もともとあった子ノードを除去し、その位置に新しいノードを挿入します。
実際のサンプルを見てみましょう。
<!doctype html>
<html>
<head>
<title>test</title>
</head>
<body>
<p id="aaaaa">t<strong>es</strong>t</p>
<script type="text/javascript">
var p = document.getElementById('aaaaa');
var newnode = document.createElement('em');
newnode.appendChild(document.createTextNode('置き換えるノード'));
p.replaceChild(newnode, p.firstChild);
</script>
</body>
</html>
このサンプルでは、P要素の最初の子ノードと、新しくつくったem要素を置き換えています。
まず、getElementByIdで
- #text
- strong
- #text
- #text
を取得します。
その後、新しいem要素をcreateElementで作り、newnodeに代入しています。その後、そのem要素にdocument.createTextNodeで作ったテキストノードを子として追加しています。
つまり、
- #text
"置き換えるノード"
を作って変数newnodeに代入したということですね。
その後、いよいよp.replaceChildを呼び出しています。1つめの引数が新しく作ったノードで、2つめの引数は、p.firstChild、つまり
- #text ←これ
- strong
- #text
- #text
ですね。
replaceChildは、1つめの引数が置き換え後のノードで、2つめの引数が置き換え前のノードです。
つまり、この呼び出しでは、p.firstChildをnewnodeに置き換えるという処理をしているわけです。つまり、次のようになります。
- #text
"t"
- strong
- #text
"es"
- #text
- #text
"t"
↓↓↓
- em
- #text
"置き換えるノード"
- #text
- strong
- #text
"es"
- #text
- #text
"t"
画面の実行結果をみても、この通りになっていることが分かります。
cloneNode
ノードは、cloneNodeというメソッドを持っています。これは、ノードをコピーするためのものです。
というのも、ノードもオブジェクトの一種だから、var b = a;
のようにしてしも、bとaは同じノードを指してしまい、うまくいきません。
また、あるノードは木構造中の1箇所にしか入れることができます。同じノードを複数箇所に配置するということはできないのです。そのようなことをしたい場合、既にあるノードと全く同じ形のノードを新しく作る必要があります。これを行ってくれるのがcloneNodeです。
サンプルを見てみましょう。
<!doctype html>
<html>
<head>
<title>test</title>
</head>
<body>
<p id="aaaaa">t<strong>es</strong>t</p>
<script type="text/javascript">
var p = document.getElementById('aaaaa');
var newnode = p.cloneNode(true);
document.body.appendChild(newnode);
</script>
</body>
</html>
やっていることは簡単で、変数pにp要素を代入し、pのcloneNodeで同じノードをもう1つつくってnewnodeに代入し、それをbody要素にappendChildで追加しています。
その結果、同じ(だがノードとしては別々の)p要素が新しくページの最後に追加されました。
では、cloneNodeの引数であるtrueは何なのでしょう。実は、cloneNodeには引数としてtrueまたはfalse(すなはち、真偽値)を与えることができます。この引数にtrueを指定すると、子ノードも全てコピーします。対して、falseを指定すると、そのノードしかコピーしません。つまり、子ノードは全くない状態で新しいノードが作られます。HTMLタグでいうと、<p></p>
の部分だけコピーするようなものです。用途に応じて使い分けましょう。