###前提・実現したいこと
初めて投稿します。調べたがわからず途方にくれてしまい、勇気を出して書きます。
Chromeで動いているとても簡単なJavaScriptなのですが、IEやEdgeで動かないのです。
DOM操作のところです。
これだけシンプルだと、IEやEdgeのバグか仕様かな?とも思ったのですが、ネットで検索しても同様の報告はなく、私の初歩的な勘違いの可能性もあって、質問してみることにしました。
下記サンプルコードにあるように、DOM操作をしているのですが、ChildNodesは動くのですがChildrenが動きません。
またinnerHTMLも動きませんし、getElementByIdも動かないようです。
対象をDocumentにするとIEやEdgeでも動くのですが、このようにparseFromStringを使って代入したDOMのオブジェクトに対する操作で起きてしまいます。
何がいけないのでしょうか?
ご教授いただければ幸いです。
###発生している問題・エラーメッセージ
下記サンプルで、Alertで<3>を表示しようとしているところでAbortします。
サンプルソースをシンプルにするために省きましたが、innerHTMLや、getElementByIdも動きません。
###該当のソースコード
HTMLとJavaScriptです。
1<html> 2<HEAD> 3<meta charset="UTF-8" /> 4<script> 5function init() { 6var dpObj = new DOMParser(); 7var xmlDoc = dpObj.parseFromString('<?xml version="1.0" encoding="UTF-8"?><root id="11" result="abc"><test id="22">てすと</test>\n<test id="33"><test2></test2></test></root>', "text/xml"); 8alert("<1>"+document.getElementById("table01").children.length+", "+document.getElementById("table01").innerHTML); 9alert("<2>"+xmlDoc.getElementsByTagName("root")[0].childNodes.length+", "+xmlDoc.getElementsByTagName("root")[0].childNodes[2].innerHTML); 10alert("<3>"+xmlDoc.getElementsByTagName("root")[0].children.length); 11} 12</script> 13</HEAD> 14<BODY onLoad="init()"> 15<table id='table01' border=1><tr><td>あいうえお</td></tr></table> 16</BODY> 17</html>
###試したこと
###補足情報(言語/FW/ツール等のバージョンなど)
気になる質問をクリップする
クリップした質問は、後からいつでもMYページで確認できます。
またクリップした質問に回答があった際、通知やメールを受け取ることができます。
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
回答4件
0
確認してみました。
https://developer.mozilla.org/ja/docs/Web/API/ParentNode/children
こちらを見るとわかりますがIEの場合、Elementに対してはchildrenをサポートするものの、Documentに対してはサポートがされていません。
取得するものがElementオブジェクトだったとしても、親に依存するようで、親がDocumentオブジェクトの場合はサポートされていませんでした。(Chrome等はDocumentオブジェクトでもサポートしているのでうまくいく)
なので結論としては仕様ですね・・・。
投稿2017/06/30 04:44
編集2017/06/30 04:46総合スコア2158
0
DOMParser#parseFromString
DOMParser#parseFromString
は「Document インターフェースを持つオブジェクト」を返します。
###Interface ParentNode
下記URLより引用。
interface ParentNode { [SameObject] readonly attribute HTMLCollection children; (中略) }; Document implements ParentNode; DocumentFragment implements ParentNode; Element implements ParentNode;
Interface Document, Element は Interface ParentNode を実装しなければなりません。
つまり、ParentNode#children を実装しなければなりません。
Interface Node
下記URLより引用。
interface Node : EventTarget { (中略) [SameObject] readonly attribute NodeList childNodes;
Interface Node は childNodes
プロパティを持たなければなりません。
Interface NonElementParentNode
下記URLより引用。
interface NonElementParentNode { Element? getElementById(DOMString elementId); }; Document implements NonElementParentNode;
Interface Document は Interface NonElementParentNode を実装しなければなりません。
つまり、NonElementParentNode#getElementById も実装しなければなりません。
Interface Document
下記URLより引用。
interface Document : Node { (中略) HTMLCollection getElementsByTagName(DOMString qualifiedName);
Interface Document は Interface Node を継承します。
Interface Document は getElementsByTagName を実装しなければなりません。
Interface Element
下記URLより引用。
interface Element : Node { HTMLCollection getElementsByTagName(DOMString
Interface Element は Interface Node を継承します。
Interface Element は getElementsByTagName を実装しなければなりません。
Interface Element の拡張
下記URLより引用。
partial interface Element { (中略) attribute DOMString innerHTML;
Interface Element は innerHTML を実装しなければなりません。
更に同URLより引用。
element . innerHTML [ = value ]
(中略)
In the case of an XML document, throws a "InvalidStateError" DOMException if the Element cannot be serialized to XML, or a "SyntaxError" DOMException if the given string is not well-formed.
XMLドキュメントの場合、要素をXMLにシリアル化できない場合は "InvalidStateError" DOMExceptionをスローし、指定された文字列が整形式でない場合は "SyntaxError" DOMExceptionをスローします。
まとめ
まとめると、下記のようになります。
DOMParser#parseFromString
は「Document インターフェースを持つオブジェクト」を返します。- Interface Document は getElementsByTagName, getElementById, childNodes, children を持ちます。
- Interface Element は getElementsByTagName, childNodes, children を持ち、getElementById を持ちません。
- Interface Element は innerHTML を持ちますが、それがXML文書であるならば、XML構文が正しくなければ例外を返します。
この通りに動かないブラウザがあるなら、未実装もしくは実装バグと思われます。
http://caniuse.com やMDNに実装状況は載っているので、そちらを参照して下さい。
innerHTML のIE実装バグ
下記URLより引用。
The innerHTML property is read-only on the col, colGroup, frameSet, html, head, style, table, tBody, tFoot, tHead, title, and tr objects.
innerHTMLプロパティは、col、colGroup、frameSet、html、head、style、table、tBody、tFoot、tHead、titleおよびtrオブジェクトでは読み取り専用です。
従って、IE ではtable要素ノードをinnerHTMLで書き換える事は出来ません。
(このバグはどこかのバージョンで修正されたような記憶があったようななかったような…、失念しました。)
table関係要素ノードのAPIを使うか、appendChild等の単純なDOM APIで操作して下さい。
不具合の詳細について
Chromeで動いているとても簡単なJavaScriptなのですが、IEやEdgeで動かないのです。
IEやEdgeのバージョンはいくつですか。
下記サンプルコードにあるように、DOM操作をしているのですが、ChildNodesは動くのですがChildrenが動きません。
またinnerHTMLも動きませんし、getElementByIdも動かないようです。
childNodes は複数ありますが、全ての場所で動かないのですか。動く場所と動かない場所があるのではありませんか。
他のプロパティ(children, innerHTML, getElementById,, getElementsByTagName)についても同様に確認していますか。
エラーメッセージがコンソールに出力されていませんか。
実装状況の検証 (IE11, Edge38)
SCRIPT5007: Unable to get property 'length' of undefined or null reference
というエラーが出た個所が、
xmlDoc.getElementsByTagName("root")[0].children.length
です。
エラーメッセージを言葉通りに読むと、**Element インターフェースが children を持っていない(undefined)**と読めます。
私の環境で検証したところでは、次のようになりました。
- IE11 は
Element.prototype.innerHTML
,Element.prototype.children
,Document.prototype.children
を実装していない - Edge38 は
Element.prototype.children
,Document.prototype.children
を実装していない
JavaScript
1'use strict'; 2console.log('children' in Element.prototype); // false (IE11, Edge38) 3console.log('children' in HTMLElement.prototype); // true (IE11, Edge38) 4console.log('children' in Document.prototype); // false (IE11, Edge38) 5 6console.log('innerHTML' in Element.prototype); // false (IE11) / true (Edge38) 7console.log('innerHTML' in HTMLElement.prototype); // true (IE11, Edge38) 8 9console.log('getElementsByTagName' in Document.prototype); // true (IE11, Edge38) 10console.log('getElementsByTagName' in Element.prototype); // true (IE11, Edge38) 11 12console.log('getElementById' in Document.prototype); // true (IE11, Edge38) 13 14console.log('evaluate' in Document.prototype); // false (IE11) / true (Edge38)
HisayukiIgeta さんが書かれたコードを私の環境で検証したところ、Edge38 ではエラー無く実行できましたが、
JavaScript
1var xmlDoc = new DOMParser().parseFromString('<?xml version="1.0" encoding="UTF-8"?><root id="11" result="abc"><test id="22">てすと</test>\n<test id="33"><test2></test2></test></root>', "text/xml"); 2console.log("<1>"+document.getElementById("table01").children.length+", "+document.getElementById("table01").innerHTML); // <1>1, <tbody><tr><td>あいうえお</td></tr></tbody> 3console.log("<2>"+xmlDoc.getElementsByTagName("root")[0].childNodes.length+", "+xmlDoc.getElementsByTagName("root")[0].childNodes[2].innerHTML); // <2>3, <test2/> 4console.log("<3>"+xmlDoc.getElementsByTagName("root")[0].children.length); // <3>2
IE11では <2> の innerHTML が undefined
を返し、<3> は例外で処理が停止されました。
JavaScript
1var xmlDoc = new DOMParser().parseFromString('<?xml version="1.0" encoding="UTF-8"?><root id="11" result="abc"><test id="22">てすと</test>\n<test id="33"><test2></test2></test></root>', "text/xml"); 2console.log("<1>"+document.getElementById("table01").children.length+", "+document.getElementById("table01").innerHTML); // <1>1, <tbody><tr><td>あいうえお</td></tr></tbody> 3console.log("<2>"+xmlDoc.getElementsByTagName("root")[0].childNodes.length+", "+xmlDoc.getElementsByTagName("root")[0].childNodes[2].innerHTML); // <2>3, undefined 4console.log("<3>"+xmlDoc.getElementsByTagName("root")[0].children.length); // 未定義または NULL 参照のプロパティ 'length' は取得できません
未知のHTML要素ノード (HTMLUnknownElement)
parseFromStringの2つ目のパラメータで、text/xmlのところをtext/htmlにするとエラーがなくなるところにもヒントがありそうです。
そこまで検証する時間がとれなかったので未確認ですが、HTML文書には未知のタグ名を持つ要素ノード(HTMLUnknownElement)は HTMLElement を継承する仕様があり、IE11がHTMLUnknownElement を実装している可能性があります。
これは Object.prototype.toString
, instanceof
演算子を使うことではっきりします。
https://triple-underscore.github.io/HTML-dom-ja.html#htmlunknownelement
JavaScript
1var root = xmlDoc.getElementsByTagName("root")[0]; 2 3console.log(Object.prototype.toString.call(root)); 4console.log(root instanceof HTMLElement);
修正案
Edge38 では Document#evaluate
が使えます。
IE11 にはありませんが、Function#call
を併用すれば実用だと思います(そこまで時間が取れなかったので、未確認)。
JavaScript
1document.evaluate.call(xmlDoc, ...);
Re: HisayukiIgeta さん
投稿2017/07/01 01:51
編集2017/07/02 15:44総合スコア18189
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
2017/07/01 10:10
2017/07/01 23:03
2017/07/02 03:09
2017/07/02 03:48
2017/07/02 04:25
2017/07/02 04:51
2017/07/02 08:47
2017/07/02 16:03
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
2017/06/30 04:36
0
解決したわけではないのですが、当問題は結局マイクロソフトのバグだと認識し、以下のような回避策を試みようと思っています。
ご丁寧に回答いただいた3人の方々、本当にありがとうございました!
【回避方法】
var xmlDoc = dpObj.parseFromString('<?xml version="1.0" encoding="UTF-8"?><root id="11" result="abc"><test id="22">てすと</test>\n<test id="33"><test2></test2></test></root>', "text/html");
wk_xml = xmlDoc.getElementsByTagName("root")[0].cloneNode(true);
xmlDoc.removeChild(xmlDoc.childNodes[1]);
xmlDoc.appendChild(wk_xml);
【ポイント】
・text/xmlを諦め、text/htmlにしました。
・text/htmlにするとparseFromStringで出来上がるXML構造が変わりますので、既存のプログラムへの影響を最小限にするため、rootエレメント以下を保存した後に、htmlエレメント以下を一旦削除(removeChild)し、そしてルート配下にrootエレメントを付け直しました(appendChild)
・これにより、IE/Edgeでは効かなかった.childrenや、.innerHTMLが効くようになりました。
【残存する配慮点】
・ノード名が大文字で返って来るようになってしまったので、JavaScript上でノード名を比較する時は、比較する相手側に.toUpperCase()関数を付けて比較しなければならない
・xmlDoc.children[0]が相変わらず効かないので、ここは、xmlDoc.childNodes[1]で代替する
スッキリしない自己解決ですが、当面はこれで切り抜けようかと思います。
もし、もっとベターな方法がありましたら、引き続きご教授いただけたら幸いです。
ご回答いただけた方、またそうしようと当質問をお読みいただいた方々には感謝です。
ありがとうございました。
投稿2017/07/05 04:27
総合スコア12
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
あなたの回答
tips
太字
斜体
打ち消し線
見出し
引用テキストの挿入
コードの挿入
リンクの挿入
リストの挿入
番号リストの挿入
表の挿入
水平線の挿入
プレビュー
質問の解決につながる回答をしましょう。 サンプルコードなど、より具体的な説明があると質問者の理解の助けになります。 また、読む側のことを考えた、分かりやすい文章を心がけましょう。
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
2017/06/30 04:59
2017/06/30 05:26
2017/06/30 05:35
2017/06/30 06:10 編集
2017/06/30 06:27
2017/06/30 06:32
2017/06/30 06:36