iframe内にコンテンツを表示する際にハマりやすい・・というか自分がハマった・・問題について解決策をメモっておきます。
静的コンテンツをiframe内で表示する
要件は単純です。
PHPでログイン・パスワードでアクセス制限をかけたページ内に、iframeを使って静的HTMLのコンテンツを表示したいだけです。
表示をするだけなら簡単そのもの。
たとえば、静的コンテンツを表示するためのURLはこんな感じを想定します。
PHPプログラム自体の処理は大枠でこんな構造。
- URLパラメータから表示HTML名を得る(上記ならcontents001.html)
- ログイン中であることをチェック。違えば、ログインするように即すエラー画面。
- OKなら、contents001.html の本当の置き場所のURLに変換する。
- そのURLをiframeにわたして表示する。
ソースでエッセンスだけ抜き出して書くとこんな感じですかね。
//ここまででログインチェックがOKになった前提
if(isset($_GET['ct'])) {
$fname = $_GET['ct'];
}else{
$fname = 'error.html';
}
define("URL_PATH","http://hogedoc/doc/");
$disp_url = URL_PATH.$fname;
print("<iframe src=\"$disp_url\" width=\"100%\" height=\"800px\"></iframe>");
一応、これで表示はできます。
でも、2点問題がありました。
- iframeのサイズが固定でスクロールバーがでてしまう。
- 静的URLでセキュリティをパスして参照できてしまう
です。
iframeのサイズが固定でスクロールバーがでてしまう問題の対策
デフォルトのheightよりも長いコンテンツの時は、コンテンツにあわせてiframeの長さがのびてほしい。
普通はそう思います。
でも、iframeではそういう指定ができません。
サイズが固定になって、スクロールバーがでてしまいます。
これはHTMLだけでは対応できないので、jQueryを使って対応します。
jquery-3.2.1.min.js とjquery.cookie.js を適当にダウンロードして、PHPソースを置くフォルダにjsフォルダを作って置いときます。
それで、phpプログラムのお尻の方(PHP命令の外。</body>の直前あたり)に以下のような記述を追加します。
<script src="js/jquery-3.2.1.min.js"></script>
<script src="js/ifexp.js"></script>
ifexp.jsは自分で作ります。
中身はこうです。
ifexp.js
('iframe')
.on('load', function(){
try {
$(this).height(0);
if(this.contentWindow.document.documentElement.scrollHeight > 800){
$(this).height(this.contentWindow.document.documentElement.scrollHeight);
}else{
$(this).height(800);
}
} catch (e) {
}
})
.trigger('load');
こうしておくと、HTMLをiframe内で表示する時に高さを自動調節してくれます。
静的URLでセキュリティをパスして参照できてしまう問題の対策
こちらは、ちょっと補足説明します。
ようするに、コンテンツが見える見えない・・は、親画面にあたるPHP側でセキュリティをかけているわけです。
ところが、iframeで表示をしている限り、ブラウザの「ソースの表示」機能で静的URLを確認されてしまいます。
こんな感じです。
<iframe src="https://hogedoc/doc/contents001.html" width="100%" height="800px"></iframe>
当たり前ですが、静的URLにはセキュリティなんかかかってませんから、フリーパスになってしまいます。
これでは、PHPでアクセス制限をかける意味がありません。
これをふせぐには、URLを直接たたいても表示できない仕掛けが必要です。
方針は簡単です。
<body>の中の表示させたくないコンテンツを<div>タグで囲んで、それを非表示(display:none;)にしておくのです。
今回は元々、<div class=out-f></div>で全体を括ってあったので、ここはCSSの変更だけでOKでした。
そして、表示用のPHPプログラムの中で、ログインOKなら、Cookieにそれを有効期限10秒くらいでセットし、HTML側でそのCookieがあるかどうかをチェックして、OKなら、display:none;を解除してやれば良いわけです。
こうしておけば、PHPプログラムを通してしか表示することができません。
実際のソースの抜粋です。
まずPHP側でログインチェックOKになったら、以下のようにcookieを10秒間だけの有効期限で発行します。
$expire = time() + 10;
setcookie('5Xa9Dfgt03', '5Xa9Dfgt03', $expire);
HTML側に埋め込む表示用のJavaScriptは、こんな感じです。
Cookieの存在チェックがOKなら、display:noneを解除しています。
if($.cookie('5Xa9Dfgt03')){
$(".out-f").css({'cssText': 'display:block !important;'});
}else{
//エラー処理。エラーページへの遷移とか。
}
上記はポイントの部分だけ抜き出してますが、iframeの中で表示させるときは、<script></script>で囲ってあるだけでは実行されません。
上記を、読み込み時に実行するように、例えば以下で囲っておくことはお忘れなく。
(function(){
// 上記の処理
}());
そして、それを「sec.js」とかで保存したす。
上記が「即時関数」の形であることに注意してください。
そして、「sec.js」を置く位置は、bodyの<div class=out-f>の直後です。
<div class=out-f>
<script src="js/sec.js"></script>
こうする理由は、InternetExprorer対策です。
他の位置においたり、即時関数ではなく、よくある「$(function(){ });」の形で囲ったりすると、文末あたりに「2018/05/03追記」で書いた現象に苦しむことになります。
あ、そうそう。
</head>の前あたりで必要なライブラリは読み込んでおいてください。
<script src="js/jquery-3.2.1.min.js"></script>
<script src="js/jquery.cookie.js"></script>
今回は、pythonの使い捨てスクリプトを作って200ファイル一気に埋め込みましたが、HTMLに上記3行を貼り付けるだけなら、HTMLしかさわれなくてもOKですから、今後のメンテナンスの心配もありません。
やれやれです。
2018/05/03追記
>Divタグのdisplayを切り替えて表示した時に、iframeの内容に合わせた高さの自動調節がInternetExprorerで、効かなくなる現象が発生するみたいです。
>それも同じページで発生したりしなかったりです。
>Chromeでは何の問題もないのですけれどね。
>デバッグしてみたら、IEの場合、iFrameがLoadされた時点で「contentWindow.document.documentElement.scrollHeight」が生成されていない場合があって、そのときに例外が発生してるみたいです。
>只今、対応方法調査中です。
2018/05/08追記
>なんとか、対応方法を見つけました。
>それにあわせて、上記記事を一部修正しました。