JS node.childNodesの仕様について

このQ&Aのポイント
  • JSのnode.childNodesの仕様について調査しました。
  • dummyTag.childNodesの動作がfirefoxとIEで異なることがわかりました。
  • firefoxでは、childNodesの要素は1つでAAAのみが要素となり、BBBはAAAの子要素になります。IEでは、childNodesの要素は3つで、AAA、BBB、/AAAが要素とされます。
回答を見る
  • ベストアンサー

JS node.childNodesの仕様について

var dummyTag = document.createElement("div") dummyTag.innerHTML = "<AAA>BBB</AAA>"; としたときに、dummyTag.childNodes の動作がfirefoxとIEで異なります。 firefoxでは、childNodes の要素は1つでAAAのみ。BBBはAAAの子要素になります。 IEでは、childNodesの要素は3つで、AAA, BBB, /AAA がそれぞれ要素とされてしまうようです。 AAAをタグとして解釈しているfirefoxの動作の方が自分にとって好ましいのですが、 IEでもこのように認識させる方法はないものでしょうか。 なお、WinXP SP3, firefox10, IE8 で確認しています。

質問者が選んだベストアンサー

  • ベストアンサー
  • Chaire
  • ベストアンサー率60% (79/130)
回答No.2

先に createElement('AAA') しておく、というのならあります。HTML(5) の新要素を IE8 以下に認識させるときに用いられています。ただし、フレームをまたぐなどした別文書には効果がありません。 そもそも「HTML として」なら、IE(ver. 8 以下)の振る舞いも仕方ない面があるでしょう。「HTML としては」不正なデータなのですから、エラー処理が走ります。 HTML 2.0-4.01 規定では「未知のタグは無視する」とだけあり、具体的な処理方法および DOM 木にどう反映すべきかまでは書かれていません。未知のタグセットであっても要素ノードとして解釈するのは、XML および HTML(5) です。 どうしても AAA 要素なるものを DOM 断片として扱いたければ、XML として処理して下さい。当然ながら、XML 要素ノードには innerHTML などありませんので、IE/MSXML なら xml プロパティ、DOM Level 3 なら markupContent プロパティ(サポートしている実装を見たことがありませんが)があります。でなければ、いちいち createElement() して下さい。 XML 処理したくなければ(あるいは整形式でないのなら)、既存の HTML 要素を用いて下さい。 なお、childNodes にはちゃんと定義がありますので、そこから外れた振る舞いは「バグ」と見なせます。「ブラウザごとに違う」で終わるのでなく、それが規定の動作であるのか、それとも規定から外れた振る舞いであるのか、を把握して下さい。後者は将来的に修正される可能性が高く、例外処理に回すべきものだからです。これは CSS を使うときも同じです。 とはいえ今回の件は childNodes の定義よりもっと下層、マークアップデータの解析レベルの問題です。

palayo
質問者

お礼

innerHTMLとして読み込ませたいテキストは 状況によって内容がことなり、しかも、 その子要素を再帰的に処理する必要があるため、 createElement()することはできないという事情があります。 HTML5として解釈するというのは、古いブラウザだと対応してないですよね。 XMLとしてテキストを読み込んで要素ノードとして解釈し、 処理を実施した後に結果をinnerHTMLに入れるようにすれば、 可能なのでしょうか。

その他の回答 (7)

  • Chaire
  • ベストアンサー率60% (79/130)
回答No.8

すでに別質問に移っておられますが、とりあえず。 No.7 お礼より: > 実行時に他のjsonファイルを参照しながら非HTMLタグをHTMLタグに変換 サーバ側と何回往復するかによりますが、もしクライアント側だけで完結を目指すのなら、データ保持層(モデルとしての XML)と、ブラウザ表示層(ビューとしての HTML)を分離する必要があります。ぶっちゃけ、XSLT でやった方が楽です。 基本的に、HTML と XML(すなわち未知要素)の混在という手段は「ない」ものと考えて下さい(最近、この問題についての W3C Report も出ました)。どうしてもそれが必要なら XHTML でやることになりますが、何度も繰り返しております通り、IE の HTML パーサを通しては駄目です。HTML パーサを通すのは、それを表示するときだけです。 私の結論としてましては、全てを X(HT)ML でやるか(ブラウザに表示させるときのみ HTML 変換して innerHTML を使う)、全てを HTML でできるよう未知要素を使わないようにするか、どちらかしかない、としておきます。 ※一応、IE 5.0-8.0 には XML Data Island というのがあり、<script type="text/html">...</script> または独自拡張の xml 要素を置くことで、HTML 文書内に XML 文書を埋め込めます(IE9 で廃止されました)。しかし、ご要望とはまた少し違うでしょう。

palayo
質問者

お礼

<http://ejohn.org/blog/pure-javascript-html-parser/> にあるhtmlparser.jsを参考に、パーサを実装しました。 ご回答ありがとうございました。

  • Chaire
  • ベストアンサー率60% (79/130)
回答No.7

今気付いたのですが、ひょっとしてご質問の意図は <A>BBB</A> から DOM 文書木を構築できれば良いだけで、HTML 語彙との混在は別に考えていなかったりしますか? であれば、No.6 に書いた通り、IE なら DOMDocument、それ以外なら DOMParser を使えば良いだけです。 問題になるのは、木と木を結合するときです。Firefox は(大雑把に)XML ノードと HTML ノードが一元管理されていますので、割と自由に切り貼りできます。しかし、IE は MSXML と MSHTML が別機構ですので、自由にノードを切り貼りすることができません。 本来、このために DOM Level 2 の document.importNode() があるのですが、これはこれで微妙な問題があります(略)。だから、MSXML 側のノードをいったん文字列に直し、MSHTML 側の innerHTML に解析させるという二度手間を行うことになります。 このとき、innerHTML による HTML 解析が走る以上、IE7 以下にとって未知の要素は使えません。なので、No.6 では MSXML ノードの文字列化がてら、HTML の span 要素に変換したわけです。 innerHTML は万能のプロパティではないです。かなり限定された用途にしか使えません。

palayo
質問者

お礼

丁寧なご回答ありがとうございます。 やりたいことは、HTMLタグと非HTMLタグが混在したHTMLライクなドキュメントがあり、実行時に他のjsonファイルを参照しながら非HTMLタグをHTMLタグに変換して、変換結果を表示する、というものです。非HTMLタグは何度か非HTMLタグに変換されたあとに最終的にHTMLタグに変換されることもあり、再帰的な処理が必要になります。 HTMLライクなドキュメントの階層構造を再帰的に辿りながら変換する際に、childNodeで辿るようにFireFoxで確認しながら実装したのですが、IEで確認したところ思うように動かず、childNodeの挙動が違うことに気づいた次第です。 domなり、MSXMLなり、調べていますが、この辺りの経験があまりなく、推奨される方法がありましたら教えてください。

  • Chaire
  • ベストアンサー率60% (79/130)
回答No.6

No.2 お礼より: > XMLとしてテキストを読み込んで要素ノードとして解釈し、処理を実施した後に結果をinnerHTMLに入れる ですから、これは DOM の問題ではなく、ソース解析レベルの問題だということを念頭に置いて下さい。IE6/7 が HTML 文書内の未知のタグ(開始タグであれ終了タグであれ)をひとつの空要素と見なす以上、未知のタグのまま扱うことはできません。基本的に方法はひとつ、既知のタグに置き換えてやることです。 下記では、A 要素を HTML の span[@class="A"] 要素に変換します。 var xmlDoc; var xmlString =  '<dummyRoot>' + '<AAA>BBB</AAA><AAA>CCC</AAA>' + '</dummyRoot>'; var xslDoc; var xslString =  '<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">' +   '<xsl:output method="html"/>' +   '<xsl:template match="/">' +    '<xsl:apply-templates select="dummyRoot/*"/>' +   '</xsl:template>' +   '<xsl:template match="*">' +    '<span class="{local-name()}">' +     '<xsl:apply-templates/>' +    '</span>' +   '</xsl:template>' +  '</xsl:stylesheet>'; var htmlDoc = document; var htmlDiv = htmlDoc.createElement('div'); try {  xmlDoc = new ActiveXObject('Msxml2.DOMDocument.3.0');  xmlDoc.loadXML(xmlString);  xslDoc = new ActiveXObject('Msxml2.FreeThreadedDOMDocument.3.0');  xslDoc.loadXML(xslString);  htmlDiv.innerHTML = xmlDoc.transformNode(xslDoc); } catch (err) {  var proc = new XSLTProcessor;  var parser = new DOMParser;  xmlDoc = parser.parseFromString(xmlString, 'application/xml');  xslDoc = parser.parseFromString(xslString, 'application/xml');  proc.importStylesheet(xslDoc);  htmlDiv.appendChild(proc.transformToFragment(xmlDoc, htmlDoc)); } alert(htmlDiv.innerHTML); もちろん、こんな手間をかけるくらいなら最初から span[@class="A"] で出力した方が早いです。@class の他、@itemprop、@rel、@content、@data-* なども候補になるでしょう。 なお、No.2 で触れた MSXML の xml プロパティですが、読み取り専用であることを失念しておりました。失礼しました。 ちなみに、実装的には HTMLUnknownElement がなくても Element で返せば十分です。HTML(5) で HTMLUnknownElement をわざわざ設けたのは、未知の要素でも innerHTML など HTML-DOM API を使えるようにするためです。とはいえ、基本的に文字列処理を避けるべきなのは言うまでもありません。 ここから分かるように、HTML(5) では未知の要素の解析・処理方法も定められています。3.2.2 節、12.2.4.10 節、15.3.4 節などを参照して下さい。でないと将来、要素が追加されたときに、またも今の IE のような扱いになりますので。

  • think49
  • ベストアンサー率59% (285/482)
回答No.5

IE8 はhtmlファイルに "<AAA>BBB</AAA>" を直書きしても不正な要素を(制作者の期待通りに)認識しません。 http://fiddle.jshell.net/hDNkE/2/ 最近のブラウザは HTML 5 規定に従い、HTMLUnknownElement インターフェースを持つ HTMLElement を返します。(Google Chrome 16, Firefox 10.0, Opera 11.61 で確認) http://www.whatwg.org/specs/web-apps/current-work/multipage/elements.html#htmlunknownelement IE8 は不正な要素に対して [object HTMLUnknownElement] を返す実装をしていますが、質問者さんが期待する動作はしていないようです。 HTML (HTML5) がなかった時代のブラウザですから仕方ないですが…。 http://www.asahi-net.or.jp/~sd5a-ucd/rec-html401j/appendix/notes.html#h-B.1 最も、対象ブラウザ全てが HTML (HTML5) に従うようになったとしても "<AAA>BBB</AAA>" は Invalid な文字列です。 "<AAA>BBB</AAA>" というXML文字列をDOMに挿入せずに処理をした上で HTML に変換し、DOMに挿入させるのが一般的な対応だと思います。 でなければ、XHTML文書にしてXMLを挿入しても良いですが、IE8- は XHTML を認識できないため、XML文書として扱わせるようにトリックを使う必要があります。 具体的には IE8- 向けに「Content-Type: application/xml」を出力して XML 文書として扱わせます。 http://www.w3.org/MarkUp/2004/xhtml-faq#ie

回答No.4

IEはタグとして認識してます。 ただそれが、空要素(<img>などと同じ)か、空要素ではない<span>などと同じかの違いだけです。 HTMLの文法では未定義タグの扱いをどうするかは定義されていません。 完全にブラウザ依存です。 (なお、タグ属性の扱いに付いてもブラウザ依存です。) HTMLで定義された要素を使うのをお勧めしますが、どうしても未定義タグを使用するなら、createElementで可能です。 var tmp=document.createElement('AAA'); var div=document.createElement('div'); div.innerHTML='<aaa>あいうえお</aaa>'; より厳格にするには、詳しくは、いわゆる"html5.js"を参考にしてみてください。 HTML5タグをサポートしないIEで、<section>や<article>などを、空要素ではなくいわゆるインライン要素として認識させるためのスクリプトです。 (ブロック要素にみせかけるには、display:blockをスタイルシートで指定する) googlecodeなどに登録されている物では、DOCTYPEやXML/HTMLモードなどで区別しているのか、そうとうややこしい処理を行っている物もあります。 ファイル名として"html5.js"が使われることが多いので、そのファイル名で検索したらたくさん出てきます。 -------- 逆に、Fxで空要素として認識させる方法はたぶんありません。 <p><mytag>あいうえお</p> 上記は、Fxは<p><mytag>あいうえお</mytag></p>として認識します。

palayo
質問者

お礼

詳しい回答ありがとうございます。 どうもIEの動きが掴めないのですが・・・。 IEが空要素として認識しているのであれば、 以下を実行した場合、0=AAAから始まると思います。 また、var tmp=document.createElement('AAA'); があってもなくても動作は変わらないようです・・・。 <html> <head> <title>テスト</title> </head> <body> <div>本文</div> <div id="xx">あああ</div> <script type="text/javascript"> var ret = new Array(); //var tmpA=document.createElement('AAA'); //var tmpB=document.createElement('BBB'); var dummyTag = document.createElement("div") dummyTag.innerHTML = "<AAA>a</AAA><BBB>b</BBB><CCC>c</CCC>"; for ( var i = 0; i < dummyTag.childNodes.length; i++){ ret[ret.length] = i + "=" + dummyTag.childNodes[i].nodeName; } var res = ret.join(''); document.getElementById("xx").innerHTML = res; </script> </body> </html> childNodeエンティティを上書きする(できるのか分かりませんが)か、 innerHTMLに相当するテキストをタグかどうか解釈して createElement するしかないものでしょうかね。

  • yambejp
  • ベストアンサー率51% (3827/7415)
回答No.3

つまりこういう処理 var dummyTag = document.createElement("div") var BBB=document.createTextNode("BBB"); var AAA=document.createElement("AAA"); AAA.appendChild(BBB); dummyTag.appendChild(AAA);

palayo
質問者

補足

innerHTMLとして読み込ませたいテキストは 状況によって内容がことなるため、 createElement()で対応できないという事情があります。 ご丁寧に作成いただいたのに申し訳ないです。

  • nda23
  • ベストアンサー率54% (777/1415)
回答No.1

>IEでもこのように認識させる スクリプトでブラウザの動作を変更できたら どうなると思います? 誰もそんな恐ろしいブラウザは使いません。 しょうがないから自分でブラウザを識別して 同じ結果になるようにプログラムしている のが現状です。ブラウザごとに微妙に違う ところはChildNodesだけではありませんしね。

関連するQ&A

  • 【javascript 文法】値渡し?参照渡し?

    以下実行すると、AAAをクリックしてもBBBをクリックしても「BBB」とアラートされます。 <html> <head> <script type="text/javascript" src="prototype.js"></script> </head> <body> <script type="text/javascript"><!-- var gDown= false; var div = document.createElement('div'); div.id = 'AAA'; div.innerHTML = 'AAA'; document.body.appendChild(div); Event.observe(div, 'mousedown',function(){ gDown = div; }); var div = document.createElement('div'); div.id = 'BBB'; div.innerHTML = 'BBB'; document.body.appendChild(div); Event.observe(div, 'mousedown',function(){ gDown = div; }); Event.observe(document, 'mouseup',function(){ alert(gDown.id); gDown=false; }); //--></script> </body> </html> javascriptって値渡しだと思っていたので、期待した動作と異なります。参照渡しだと考えると納得できるのですが、javascriptって 参照渡し?それとも値渡し? と混乱しています。 この辺のところを、教えてください。お願いいたします。

  • appendChildがieだとできない??

    こんにちは。DOMについては全く無知です。 このようなのを作ったところ、google chromeでは動いたものの、ieでは動きませんでした。 var iBody = $("hoge").contentWindow.document.getElementsByTagName("body")[0]; var newText = document.createElement("div"); newText.innerHTML = "aaa"; iBody.appendChild(newText); ※$("hoge")は、iframeタグを指しています いろいろ試したところ、ieだとフレーム内にアクセスすることができないみたいです。 どうすればいいのか、ご存知でしたらご教示願います。

  • firefox、opera等での番号の数え方

    現在Javascriptを勉強中で 教材中での疑問なのですが 下の記述でどうしても腑に落ちないことがあります。 IEの方の[]の中の番号の数え方は分かるのですが firefox用ではどういう数え方をすれば node[0].childeNodes[5].childeNodes[1].innerHTML......... という数え方になるのでしょうか? 開始タグと終了タグを分けて数えているのでしょうか? 分かる方回答お願いします。 // IE用 function change(){ var node = document.getElementsByTagName("body"); node[0].childNodes[2].childNodes[0].innerHTML = "変更しました"; } // FireFoxやOpera用 function change(){ var node = document.getElementsByTagName("body"); node[0].childNodes[5].childNodes[1].innerHTML = "変更しました"; } --------HTML---------- <body> <h1>サンプル3</h1> <p>ここが変わります。</p> <div> <p>深いノード1</p> <p>深いノード2</p> </div> <form> <input type="button" value="変更" onclick="change();"> </form> </body>

  • [JS] あるクラス名をページからすべて削除したい

    ページ内のすべてのタグから、例えば"aaa"というクラス名を削除したいです。 単純に以下のようにしてみました。 var elms = document.getElementsByClassName("aaa"); for ( var i = 0; i < elms.length; i++ ) { elms[i].removeClassName("aaa"); } この場合、firefoxではremoveClassNameする度に、elmsから動的に要素が削除されてしまいます。 そこで、以下のようにしましたが、IEではelmsから要素が減らないため、無限ループになってしまいます。 var elms = document.getElementsByClassName("aaa"); while ( elms.length > 0 ) { elms[0].removeClassName("aaa"); } どうやって削除するのがよいのでしょうか??

  • PSPのWebブラウザで、動的にタグ入の文字を挿入するにはどうしたら良

    PSPのWebブラウザで、動的にタグ入の文字を挿入するにはどうしたら良いですか? <div id="aaa">ここを変える</div> function change(){ document.getElementById('aaa').innerHTML="<b>foo</b>"; } としても、<div>の中身が太文字のfooとならずに、<b>foo</b>となってしまいます。 <div id="aaa">ここを変える</div> function change(){ var aaa=document.getElementById("aaa"); aaa.firstChild.nodeValue='<b>foo</b>'; document.body.appendChild(aaa); } としても結果は同じでした。 これを太字で表示させる方法はありますか?

  • appendChildとinnerHTMLを短く

    テーブルのthタグと内容を追加したい ・下記で期待通り動作するのですが、冗長かなと思い、スッキリさせたいです ・どうすれば良いでしょうか? var tr = document.createElement("tr"); tbody.appendChild(tr); var th = document.createElement("th"); th.innerHTML = '見出1'; tr.appendChild(th); var th = document.createElement("th"); th.innerHTML = '見出2'; tr.appendChild(th); var th = document.createElement("th"); th.innerHTML = '見出3'; tr.appendChild(th); var th = document.createElement("th"); ……

  • Apend前のElementの中のElement

    var div = document.createElement('div'); div.innerHTML="hogehoge<div id='hoge'>aaa</div>"; とし、 このdivの中のelementをID指定で取得したいです。 document.body.appendChild(div); とした後なら、 document.getElementById('hoge') で取得できるのですが、 appendChildしたくないです。 ご名案ありましたら、ご教授ください。宜しくお願いいたします。

  • JS onmouseoverについて

    お世話になっております。 お忙しいところ申し訳ありません onmouseoverでテキストリンクから 画像を表示させているのですが onmouseで出た画像の場所に <select>で表示したセレクターがあり 画像が隠れてしまいます どちら様か画像を一番上に持ってくる 方法を教えていただけないでしょうか? 仕様 WIN XP IE SP2 NNで見ると表示されます。 文章が下手で申し訳ありません かいつまんだソースをのせます <!-- function msg() { document.all.aaa.innerHTML ='<table width=200 cellspacing=0 cellpadding=0 border=1><tr><td>bbb</td></tr></table>'; } //--> </script> <div id="aaa" STYLE="position:absolute;"></div> <a onmouseover="msg()" HREF="###">1111</a> <select name="ggg"> <option VALUE="" SELECTED>xxx</option> <option VALUE="">yyy</option> </select> です。 bbbを見えるようにしたいのです よろしくおねがいします

  • 要素を追加する関数の作成

    <div id="here"></div> 上の様な要素内に要素と内容を追加する関数を作りたいのですが、思った結果が得られずに悩んでいます。 function ins( id ) { var output = document.createElement( 'span' ); output.className = 'ins'; output.innerHTML = <p>content</p>; var element = document.getElementById( id ); element.appendChild( output ); } ins( 'here' ); この様な場合にはどうしたら良いのか、何かアドバイスを頂けると嬉しいです。

  • 【javascript】document.getElementByIdは一つしか使えないの?

    以下をhtmlをブラウザで見ると、文字列"aaa"は表示されますが、"bbb"は表示されません。 <html> <head> <SCRIPT LANGUAGE='JavaScript1.2'><!-- function init(){ document.getElementById("a").innerHTML="aaa"; document.getElementById("b").innerHTML="bbb"; } //--></script> </head> <body onload="init()"> <div id="a"></dev> <div id="b"></dev> </body> </html> ブラウザはIE,FFと試しましたが共に同じ結果でした。 FFのエラーコンソールには document.getElementById("b").innerHTML="bbb" is null のエラーメッセージが出てましたが、文法的に何が問題なのでしょうか?