【JavaScript】XPathを使用する#3 ノードセットとスナップショットの違い

document.evaluateメソッド戻り値の種類について、よく使いそうなノードセットスナップショットを試しました。
どちらを使った方がよいかといわれると、私はスナップショットを使います。
理由は、ノードセットではappendChildするとエラーが出てしまったからです。

ノードセットでappendChild

戻り値の種類をノードセット(ORDERED_NODE_ITERATOR_TYPE)にしてappendChildした(20行目)コードを実行したところエラーとなりました。
※使用するXMLは#1、#2と同じものを使用

<!DOCTYPE html>

<html>
    <head>
        <script>
            // onload時に実行
            function my_onLoad(){

                const objXml = my_loadXML('./static/player.xml');

                // XPathを作成
                let xPath = "//player/name[../position='外野手']";

                // XPathを適用(ノードセット)
                let result = objXml.evaluate(xPath, objXml, null, XPathResult.ORDERED_NODE_ITERATOR_TYPE, null);

                // XPathを適用した結果をbodyに追加する
                let r;
                while (r = result.iterateNext()) {
                    document.body.appendChild(r);
                }
            }

            // XMLを読み込む
            // path : 読み込むXMLのパス
            function my_loadXML(path){
                let objXml = new XMLHttpRequest();
                objXml.open('GET', path, false);
                objXml.send('');
                return objXml.responseXML;
            }
        </script>
    </head>
    <body onload='my_onLoad()'>
    </body>
</html>
エラーメッセージ

Uncaught DOMException: Failed to execute ‘iterateNext’ on ‘XPathResult’: The document has mutated since the result was returned.

結果が返されてから、ドキュメントが変更されました(The document has mutated since the result was returned.)」とあります。appendChildしたので元のXMLが変更されてしまったのが原因です。

W3Cのドキュメントにも「ドキュメントを変更すると、イテレーションが無効になります(Document modification invalidates the iteration.)」と記載されています。

ORDERED_NODE_ITERATOR_TYPE
 The result is a node set as defined by [XPath 1.0] that will be accessed iteratively, which will produce document-ordered nodes. Document modification invalidates the iteration.

スナップショットでappendChild

戻り値の種類をスナップショット(ORDERED_NODE_SNAPSHOT_TYPE)にしてappendChildしたコードです。こちらは特にエラーはでないと思います。
※使用するXMLは#1、#2と同じものを使用

<!DOCTYPE html>

<html>
    <head>
        <script>
            // onload時に実行
            function my_onLoad(){

                const objXml = my_loadXML('./static/player.xml');

                // XPathを作成
                let xPath = "//player/name[../position='外野手']";

                // XPathを適用(スナップショット)
                let result = objXml.evaluate(xPath, objXml, null, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null);

                // XPathを適用した結果をbodyに追加する
                for(let i=0; i<result.snapshotLength; i++){
                    document.body.appendChild(result.snapshotItem(i));    
                }
            }

            // XMLを読み込む
            // path : 読み込むXMLのパス
            function my_loadXML(path){
                let objXml = new XMLHttpRequest();
                objXml.open('GET', path, false);
                objXml.send('');
                return objXml.responseXML;
            }
        </script>
    </head>
    <body onload='my_onLoad()'>
    </body>
</html>

コードの補足

今回のコード例は、ノードセットとスナップショットの違いが見れればよいという観点で作ったため、XPathで適用した結果をbodyにappendChildするという、実際にはあまり行わないようなことをしました。
実際には、XPathを適用した結果をJavsScriptで生成したXMLにappendChildし、そのXMLにXSL変換を行うといったようなことをやったことがあります。

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です

CAPTCHA