2009年9月19日土曜日

JavaScript で a 要素を生成し、name 属性を設定するときは DOCTYPE 宣言と setAttribute メソッドを使用する

1. IE8 で a 要素の name 属性が生成されない

JavaScript で、HTML の「ページの特定の場所を示すアンカー要素」を生成した。

結果を Firefox で表示すると、アンカー要素が正常に生成された。しかし、Internet Explorer 8 では想定していた属性名が生成されなかった。

このとき、特定の場所を示すために a 要素の

  • name 属性

を利用していた。

例えば、次のように JavaScript で a 要素を生成し、name 属性を設定した後、HTML の要素として挿入する。

<html>
	<head></head>
	<body>
	</body>
	<script type="text/javascript">
	var anchor = document.createElement('a');
	anchor.name = "hoge";
	anchor.appendChild(document.createTextNode('hoge'));
	document.body.appendChild(anchor);
	</script>
</html>

Firefox において、Firebug で上記のHTML を確認すると、

<a name="hoge">hoge</a>

IE8 の DebugBar で確認すると

<a submitName="hoge">

name 属性ではなく、 submitName 属性になっている。これにより、アンカーが機能しない。

 

2. a 要素で name 属性を使うことは問題ない

name 属性を使うことは、問題があるのだろうか?

文書の特定の場所へのリンク」 によると、

リンク先には、文書中の特定の段落など具体的な場所(フラグメントといいます)を指定することもできます。フラグメントを示すには、対象となる要素にid属性を使って名前を付けます(古いブラウザとの互換性のためには、アンカー要素の2番目の役割であるname属性による名前付け機能を使うこともできます)

では、なぜ name 属性が勝手に変ってしまったんだろう?

 

3. DOM の createElement メソッドで生成したオブジェクトに対する属性の指定

アンカー要素を生成するために、createElements メソッドを利用した。

DOM の createElements で生成したオブジェクトは、その属性を直接設定できる。

Document Object Model (Core) Level 1 によると、

createElement

Creates an element of the type specified. Note that the instance returned implements the Element interface, so attributes can be specified directly on the returned object.

例えば、img タグに対応したオブジェクトを生成し、その src 属性を設定してみる。

var img = document.createElement('img');
img.src = "http://3.bp.blogspot.com/_2IbZQWoiKQ0/R5_QFbCgN_I/AAAAAAAAALU/9AHeXGbX4IU/S220/profile.png";
document.body.appendChild(img);

上記のコードは、 Firefox, IE の両方で問題なく動作する。

先ほどの引用中に `the instance returned implements the Element interface’ とあった。よって、Interface ElementsetAttribute メソッドを使い、属性を設定することもできる。

var img = document.createElement('img');
img.setAttribute("src", "http://3.bp.blogspot.com/_2IbZQWoiKQ0/R5_QFbCgN_I/AAAAAAAAALU/9AHeXGbX4IU/S220/profile.png");
document.body.appendChild(img);

setAttribute メソッドを使い、アンカー要素の  name 属性を設定してみた。

anchor.setAttribute("name", "hoge");

しかし、これだけでは、結果は変わらなかった。 (+_+)

 

4. DOCTYPE スイッチで Standards モードでレタリングする

文書の先頭で DOCTYPE を、キチンと設定しないとダメだろうか?

IE8 では、DOCTYPE を指定しないと、互換モードでレタリングされる。

IE8のレンダリングモードと互換表示 - page2 - builder by ZDNet Japan によると、

レンダリングモードはIE6の時代から採用されてきたDOCTYPE宣言による指定か、IE8で採用されたMETAタグまたはHTTPレスポンスヘッダによる指定で切り替えることができる。…

IE8ではDOCTYPE宣言によってIE8 StandardsモードとQuirksモードが切り替わる。

問題のソースコードを IE8 で開き、DebugBar を起動。メニューの右隅を見ると `Quirks’ と表示された。

090919-008

DOCTYPE 宣言による「解釈モード」の切り替え によると、

過去の慣習的な解釈を再現するモード (Quirks mode)

仕様準拠の厳格解釈モード (Standards mode)

では、Standards モードでは、アンカー要素を表示させることはできるだろうか?

モードを変更するには、IE8のレンダリングモードと互換表示 - page2 - builder by ZDNet Japan によると、

IE8 StandardsモードになるDOCTYPE宣言の記述
  • HTML4.01 Transitional/FramesetのDOCTYPE宣言でシステム識別子(DTDのURL)を記述している場合
    • (例)
      <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
  • HTML4.01 StrictのDOCTYPE宣言を記述している場合
    • (例)
      <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
  • XHTML1.0のDOCTYPE宣言を記述している場合
    • (例)
      <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

問題のコードの先頭に DOCTYPE 宣言を書き Standard モードでレタリングするようにした。

属性を直接設定する方法でアンカー要素を生成したら、a 要素が以下のようになった。 (@_@;)

<a propdescname="hoge">

これに対して、属性を setAttribute メソッドを使った場合、問題なくアンカー要素が生成された。

上記をまとめると、

  1. DOCTYPE 宣言により Standard モードでレタリングし、
  2. setAttribute メソッドを使えば良い。
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
	<head></head>
	<body>
	</body>
	<script type="text/javascript">
	var anchor = document.createElement('a');
	anchor.setAttribute("name", "hoge");
	anchor.appendChild(document.createTextNode('hoge'));
	document.body.appendChild(anchor);
	</script>
</html>

上記の DOCTYPE は、IE6 でも標準準拠モードになる。

また、システム識別子がついていないだけで、動作ががらっと変ってしまう。

「システム識別子」とは、DOCTYPE 宣言による「解釈モード」の切り替え によると、

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN"
   "http://www.w3.org/TR/html4/strict.dtd">

"-//W3C//DTD HTML 4.01//EN" の部分は「 公開識別子 」と呼ばれます。…

"http://www.w3.org/TR/html4/strict.dtd" は、この W3C HTML 4.01 の文法を定義しているモノ (DTD) のありかです。「 システム識別子 」と呼ばれます。

 

5. クロスブラウザ対策

ところで、Setting the “name” attribute in Internet Explorer » Semicolon には、次のように MSDN における説明が引用されている。

I found an explanation in the MSDN DHTML reference, on the page describing the NAME Attribute.

“The NAME attribute cannot be set at run time on elements dynamically created with the createElement method. To create an element with a name attribute, include the attribute and value when using the createElement method.”

以下の記述により、Quirks モードとなり、 DOCTYPE を書かなくても良いようだ。

var anchor = document.createElement("<a name='hoge'>");
anchor.appendChild(document.createTextNode('hoge'));
document.body.appendChild(anchor);

しかし、残念ながら  Firefox では動かない。 (+_+)

には、クロスブラウザ対策がされているコードが書かれている。

 

6. jQuery で DOM エレメントを生成する

jQuery を使うなら、DOCTYPE を宣言しなくてもちゃんと表示された。

利用したメソッドは次の二つ。

<html>
	<head>
		<script type="text/javascript" src="jquery-1.3.2.js"></script>
		<script type="text/javascript">
		$(document).ready(function(){
			$("<a name='hoge'>hoge</a>").appendTo("body");
		});
		</script>
	</head>
	<body>
	</body>
</html>

ただし、IE8 では、先ほどの DOCTYPE を記述したり、

$("<a>").append("hoge").attr("name", "hoge").appendTo("body");

または、以下のように記述すると、

$("<a name='hoge'>").append("hoge").appendTo("body");

正常に表示されない。Firefox では、問題ないのだけれど。

 

7. まとめ

  • DOCTYPE 宣言して、setAttribute メソッド使う。
  • jQuery なら $ メソッドにおいて、属性と中身を含んだ HTML をごっそり引数として渡す。

てゆうか、name 属性は使わない方がいいのかな?