34
Help us understand the problem. What are the problem?

More than 3 years have passed since last update.

posted at

updated at

別のHTMLを用意せずにiframeを表示する

かなり限られたユースケース(後述)でのみ、iframeを便利に使う方法です

何がしたいのか

iframe要素を使うと、現在のHTML文章の中に、別のHTMLファイルを表示することができますよね。参照先のHTMLファイルはURLで指定するため、インターネット上のファイルでもPC上のファイルでも同じように表示できる楽しい要素です

ところが先日、 制約上iframeを使わなければならないが、できれば別のHTMLを参照したくない という状況に遭遇しました。お前は何を言ってるんだ?と言われるかも知れませんが、つまるところこの記事はフロントですべて完結するiframeの使い方を紹介するものです

結論から言うと、HTMLを単なる文字として用意することさえできれば、URLでiframeに渡すことができます。それだけのことなので、コードは非常にシンプルになります

以後、簡略化のためブラウザやエディタの文字コードはUTF-8がデフォルトになっているものとします

試してみる

Blob URL Schemeを使う方法

Blob URL が使える場合はこちらを使います
と念のため言いましたが、ご覧の通りほぼ全てです http://caniuse.com/#feat=blobURIs

// var iframe = document.getElementById('target');

var html = '<b>ただの文字</b>';

var blob = new Blob([html], { type: 'text/html' });
iframe.src = URL.createObjectURL(blob);

スクリーンショット 2016-10-10 12.28.50.png

これだけでも動作が確認できました
HTMLが<b>しか書かれていませんが、その辺は通常のHTML表示時と同じく<html><head></head><body></body></html>をブラウザが補完してくれます(もちろんから書いても動きます)

{ type: 'text/html' }を付けないとロードに失敗するので注意してください

エスケープしなくても良いというのがポイントでしょうか。さすがBlobくん、優秀です

さて、気になるのはここでjavascriptが動くのかどうかですが…

// var iframe = document.getElementById('target');

var html = `
<b>ただの文字</b>
<script type="text/javascript">
document.body.textContent = 'インジェクションできそう';
</script>
`;

var blob = new Blob([html], { type: 'text/html' });
iframe.src = URL.createObjectURL(blob);

スクリーンショット 2016-10-10 12.41.52.png

かなり簡略化したコードですが、動作は確認できますね!
「ただの文字」は、javascript実行時に上書きされました

ちなみに、下記のコードは 動きません

// var iframe = document.getElementById('target');

var html = `
<script type="text/javascript">
// Won't work, because in <head>
document.body.textContent = 'インジェクションできない…';
</script>
`;

var blob = new Blob([html], { type: 'text/html' });
iframe.src = URL.createObjectURL(blob);

<script>から始まるHTMLが与えられると、<head>の中に入れてしまうためです(標準の仕様なのだろうか)
document.bodyが初期化されていないので、参照エラーになります

それにしても、HTMLの内容を動的に決められて、かつjavascriptも実行可能ということは、つまり…

<iframe id="target"></iframe>
<script type="text/javascript">
var iframe = document.getElementById('target');

var html = document.documentElement.innerHTML;
var blob = new Blob([html], { type: 'text/html' });
iframe.src = URL.createObjectURL(blob);
</script>

こっ、これは……!!

スクリーンショット 2016-10-10 12.58.22.png

とっても……ブラクラですね……

ビデオカメラの映像をテレビに映しながら、そのテレビを撮影する、という遊びを思い出しました

Data URI Schemeを使う方法

Blob URL でできるならということで、Data URI でも試してみます

HTML文字列をData URI に変換するにあたって、こちらの記事を参考にさせていただきました

javascriptでBase64

解説も載っているので、ぜひ参考にしてください。
(コメントでも議論がなされていますが、javascriptの文字列-バイナリ操作は「標準の関数をいかに上手く使うか」「ブラウザ間の違いをいかに吸収するか」が論点となるのが興味深いです)

// var iframe = document.getElementById('target');

var html = '<b>hello こんにちは</b>'
var base64 = btoa(unescape(encodeURIComponent(html)));
iframe.src = 'data:text/html;base64,' + base64;

スクリーンショット 2016-10-10 13.52.57.png

問題ないですね

ちなみにvar base64にはどんな文字列が入っているかというと、

PGI+aGVsbG8g44GT44KT44Gr44Gh44GvPC9iPg==

こんな風になっています

なので、上記のコードは、var htmlが静的なデータであれば
var base64 = "PGI+aGVsbG8g44GT44KT44Gr44Gh44GvPC9iPg==";
と書くのとと等価です

また、encodeURIComponentを使っているので、愚かな私などは「ここでURLにしているのかな?」と勘違いしてしまっていたのですが、そうではなく unescape(encodeURIComponent(html)) がセットになっていて、日本語などを含むUTF-8文字列を、それと同等の文字列に変換しているのです

日本語などが含まれていなければ、下記のコードでも同じことが実現できます

// var iframe = document.getElementById('target');

var html = '<b>hello</b>'
var base64 = btoa(html);
iframe.src = 'data:text/html;base64,' + base64;

以上、2つの方法でiframeを使ってみました

使いどころ


iframeを使いたいが、下記のうちいずれかが障害になっており、かつフロントエンドで完結させたいという場合、この方法が役立ちます

  1. 新しくHTMLファイルを置けない(参照できない)状況
  2. スタンドアロン、かつ、ローカルホスト環境も用意できない状況
  3. ロードする前にHTMLの内容を動的に決めたい状況

ねえよ! と盛大にツッコミをいれられるかも知れませんが…(確かにこのご時世ではiframeを使うことすら…)

でも、Webアプリを作るときではなく、既存のページになにか埋め込みたいときと考えれば、ユースケースはそこそこありそうな気がしませんか?

参考

Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Sign upLogin
34
Help us understand the problem. What are the problem?