HTML
JavaScript
0
どのような問題がありますか?

投稿日

複数ドキュメントの取り扱い

今回は、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の下のツリー構造に追加され、変更されたことを意味します。

おわり

ユーザー登録して、Qiitaをもっと便利に使ってみませんか。
  1. あなたにマッチした記事をお届けします
    ユーザーやタグをフォローすることで、あなたが興味を持つ技術分野の情報をまとめてキャッチアップできます
  2. 便利な情報をあとで効率的に読み返せます
    気に入った記事を「ストック」することで、あとからすぐに検索できます
noty2008
13歳(中2): Webベースの開発者(?) / JavaScript最高 / 男子卓球部 / プログラミング歴2年 / フロントエンドやデザインなど / 最近はp5.jsとかも / 同名でScratchもやってるらしい。

コメント

doc.getElementsByTagName('p')[0];

getElementsByTagNameで該当要素を一旦すべて取得した上で0番目の要素のみを選出するのであれば、

doc.querySelector('.p');

のほうが分かりやすいかと思います。

0
どのような問題がありますか?
あなたもコメントしてみませんか :)
ユーザー登録
すでにアカウントを持っている方はログイン
記事投稿イベント開催中
データに関する記事を書こう!
~
新人プログラマ応援 - みんなで新人を育てよう!
~
0
どのような問題がありますか?
ユーザー登録して、Qiitaをもっと便利に使ってみませんか

この機能を利用するにはログインする必要があります。ログインするとさらに下記の機能が使えます。

  1. ユーザーやタグのフォロー機能であなたにマッチした記事をお届け
  2. ストック機能で便利な情報を後から効率的に読み返せる
ユーザー登録ログイン
ストックするカテゴリー