今回は、HTMLページにおいて複数のドキュメントを扱う方法とかについてざっくり解説していきます。まず、下のコードをご覧ください。
<!DOCTYPE html>
<html>
<head>
<title>てすと</title>
</head>
<body>
<p>
<iframe src="test.html"></iframe>
</p>
</body>
</html>
これは、iframe要素、つまりインラインフレームを使用した文書です。インラインフレームとは、その中に別の文書を表示する仕組みのことです。この文書では、文書の中にtest.htmlという別のページが表示されていることがわかります。
このページ全体が一つの文書ですが、表示されているtest.htmlも一つの文書です。そこで、JavaScriptでそれを処理する方法があります。
なお、今回のようにJavaScriptから他のページをいじる場合は、同じオリジンポリシーの制限を受けることになります。これは簡単に言うと、他のWebサイトの内容を読み取ることができないということです。Webサイトが同じかどうかは、オリジンが同じかどうかで判断されます。例えば、このウェブサイトのオリジンは、https://qiita.comです。オリジンとは、プロトコル(https:)、ホストであるqiita.com、ポート番号などを組み合わせた概念です。オリジンが同じでない場合、例えば、このサイトのページのJavaScriptは、他のオリジンのページからは読めません。
また、近年の厳しいセキュリティ上の制約から、ローカルファイル上で実行されるJavaScriptは、他のファイルを読み込むことができません。そのため、ここで紹介するサンプルは、単にローカルファイルに書き込んで実行しただけでは動作しません。対処法としては、ブラウザの設定でローカルファイルのSOPを無効にするか、仕方ないのでWebサーバ上にファイルを置いてアクセスする方法があります。自分のPCでWebサーバーを起動することもできます。詳しいやり方は調べてみてください。
documentの取得
JavaScriptでの文書の操作はdocumentを用いて行われ、iframeで読み込まれた文書は文書であるため、その文書に対応するdocumentが存在します。それを取得する方法を説明します。
<!DOCTYPE html>
<html>
<head>
<title>てすと</title>
</head>
<body>
<p>
<iframe src="test.html" id="iframe"></iframe>
</p>
<script type="text/javascript">
var iframe = document.getElementById('iframe');
window.addEventListener('load', function(){
var doc = iframe.contentDocument;
console.log(doc);
});
</script>
</body>
</html>
今回、loadイベントを待っているのは、iframe要素にtest.htmlが読み込まれるのを待つためです。
このサンプルでは、loadイベントが発生したときに、iframe要素のHTMLElementのプロパティcontentDocumentをdocに代入しています。このcontentDocumentは、iframe要素が読み込んだdocumentのdocumentです。
もっと具体的にやっていきます。そのために、読み込むtest.htmlは、
<!DOCTYPE html>
<html>
<head>
<title>てすと</title>
</head>
<body>
<p>てすと</p>
<p>これは、テストだよ</p>
<p>いぇい</p>
</body>
</html>
ということにしておきます。では、次のサンプルを実行してみましょう。
<!DOCTYPE html>
<html>
<head>
<title>てすと</title>
</head>
<body>
<p>
<iframe src="test.html" id="iframe"></iframe>
</p>
<script type="text/javascript">
window.addEventListener('load', function(){
var iframe = document.getElementById('iframe');
var doc = iframe.contentDocument;
console.log(doc.getElementsByTagName('p')[0]);
});
</script>
</body>
</html>
doc.getElementsByTagNameを呼び出すことで、読み込まれたものの中からpが探されます。0番目、つまり最初のものなので、
てすと
と表示されることになります。documentとノードの関係
さて、基本的にノードはdocumentに属します。たとえば、ある文書のすべてのノードは、その文書のdocumentに属します。また、上記のiframeの例では、iframeが配置されているページのノードはドキュメントに属し、iframeがロードされているページのノードはiframe.contentDocumentに属します。
ノード側からは、ノードがどのdocumentに属しているかを知ることができます。そのためには、ノードのownerDocumentプロパティを用います。
<!DOCTYPE html>
<html>
<head>
<title>てすと</title>
</head>
<body>
<p>てすと</p>
<script type="text/javascript">
console.log(document.body.ownerDocument == document); // true
</script>
</body>
</html>
しかし、あるノードがどのdocumentに属しているのかが気になる場面は少ないかもしれません。次のような例を考えてみましょう。
<!DOCTYPE html>
<html>
<head>
<title>てすと</title>
</head>
<body>
<p>
<iframe src="test.html" id="iframe"></iframe>
</p>
<script type="text/javascript">
window.addEventListener('load', function(){
var iframe = document.getElementById('iframe');
var doc = iframe.contentDocument;
var p = doc.getElementsByTagName('p')[0];
console.log(p.ownerDocument == document);
document.body.appendChild(p);
console.log(p.ownerDocument == document);
});
</script>
</body>
</html>
このサンプルでは、iframeで読み込まれたドキュメントから
てすと
を取り出し、ドキュメントに追加しています。その結果、
てすと
はiframeの中から消え、外に現れるはずですが、これはノードが一カ所にしか存在できないからです。ツリー構造内に既に存在するノードが新しい場所に appendChild された場合、このように古い場所から移動します。興味深いのは、2つのconsole.logの結果で、1つ目はfalse、2つ目はtrueであるべきです。これは、このp要素が属するDocumentが、appendChildによってdocumentの下のツリー構造に追加され、変更されたことを意味します。
コメント@ndu3x
0
getElementsByTagName
で該当要素を一旦すべて取得した上で0番目の要素のみを選出するのであれば、のほうが分かりやすいかと思います。