スポンサーリンク

JavaScriptの動かないコード (中級編) DOMで子要素を指定する際のエラー


以下のJavaScriptコードが意図した動作をしないのは,なぜですか。(制限時間1分)

<body>

<table border=1>
<tbody id="my_tbody">
	<tr><td>この行がコピーされます。</td></tr>
</tbody>
</table>

<input type="button" value="行をコピー" onClick="f()">

<script language="JavaScript">

function f()
{
	// tbody中の最初の行を取得
	var old_element = my_tbody.childNodes[0];
	
	// コピー
	var new_element = old_element.cloneNode( true );
	
	// 表に追加
	my_tbody.appendChild( new_element );
}

</script>

</body>



答え


IEでは,行がコピーされて2行になる。

しかしFirefoxでは,コピーされない。画面上で何も起こらなかったように見える。



IEとFFでは,要素の子ノードの認識の仕方が異なる。

次のコードを実行してみよう。

<div id="my_div">

<div id="div1">text1 (この上下の行は改行)</div>

<div id="div2">text2 (この下には2つの改行)</div>


<div id="div3">text3</div>
text4
<div id="div5">text5 (この下には一つの全角空白)</div>
 
<div id="div6">text6 (この下には一つの半角空白)</div>
 
<div id="div7">text7</div>
<div id="div8">text8 (この下には1つの改行)</div>

</div>


<script language="JavaScript">

function f()
{
	// 子ノード数
	alert( my_div.childNodes.length );

	// すべてのノードについて内容を表示
	for( var i = 0; i < my_div.childNodes.length; i ++ )
	{
		// ノードタイプで分岐
		switch( my_div.childNodes[ i ].nodeType)
		{
			// 要素ノード
			case 1:
				alert( my_div.childNodes[ i ].innerHTML );
				break;
			// テキストノード
			case 3:
				alert( my_div.childNodes[ i ].nodeValue );
				break;
		}
	}
}

</script>


IEでは,子ノード数は9になる。

まず div タグが7つあって,それらタグと同じ階層に「text4」というテキストノードが1つある。

それに加え,全角空白が1つのテキストノードとみなされる。

半角スペースや改行はノードとみなさず合計9つのノードとなる。



しかしFirefoxでは,タグとタグの間に存在する空白や改行を漏らさずカウントするため,なんと子ノード数は15になる。

div7 と div8 の間にあるような,見やすくしようとして挿入した単なる改行も,一つのテキストノードとして認識されてしまう。


この場合の空白やスペースのカウント規則としては,タグとタグの「間のテキスト」が一つのノードになるため

  • divタグの要素ノードが7つ
  • divタグの前後のテキストノードが合計8つ

合計15の子ノードが計上される。

冒頭のコードでFirefoxが行コピーできなかった理由も,1番目の要素として, tr タグではなくその前にある改行を取得してしまっているからだ。



IEのほうが人間の直感にあった動作をしているように見える。

でも,実はFirefoxの動作のほうがDOMの仕様上正しい。


Mozillaのサイト:DOM Inspector FAQ
https://developer.mozilla.org/ja/DOM_...

Q.

元のドキュメントにない空の #text ノードがいっぱいあります。これらは何でしょう? どうして存在して、どうすれば取り除けるのでしょう?


A.

これらのテキストノードは実際は要素間の改行とスペースなのです。どうしてそれらを表示するのかについての長々とした議論は bug 26179 で行われています。

都合の良いことに、[表示] メニューの [ホワイトスペースノードを表示] からチェックを外せば、Inspector の中でこれらの ホワイトスペースノード を隠せます。ただし、すべての空のテキストノードを隠せるわけではありません。連続するホワイトスペースをユーザエージェントがまとめることを CSS の white-space プロパティの値で制限しているノードが隠されることはありません。


改行がノード?(JAXP)
http://oshiete1.goo.ne.jp/qa1222983.html

改行がノードとして取り出されるのは,DOMの正しい仕様です。これがちゃんと取り出されないと,DOMからXMLに書き戻す時に改行が消去されてしまいます。


この問題の元をただすと,「HTMLソース中の連続空白や改行が,ブラウザの画面上では省略されてしまう」という点に帰着されるようだ。

IEは表示通りの形でDOMツリーを構築しようとする。

ブラウザにおける空白文字に関する考察
http://d.hatena.ne.jp/elm200/20080229...

  • IE でテキストを DOM インターフェイスで取得すると、ソースの空白文字(改行・タブ・半角スペース)は、省略されるか、または半角スペースに変換されている。(表示重視)

ところで,どちらが正しいかという議論ももちろん大事だが,より関心があるのはクロスブラウザで同一の動作をさせるための方法だ。

冒頭のコードでいえば,trタグを取得する部分をノード番号に依存させず,getElementsByTagName() メソッドを使うように修正すればよいだろう。

あるいは,tr要素自体にIDを振るのが手軽。
さらに,そのIDを振った行をhiddenで隠しておき,コピー元として利用するだけのためにりようするのもいい。



これはJavaScriptでのブラウザ間の相違を考慮する際に,まっさきに挙げられるエラー。