画像一枚だけの別ドメインページをiframeで読み込みレスポンシブに対応:メモ

悩み
悩み

バッドノウハウかもシリーズ。なんとか形になったので、整理のためにもメモ。

いくつか特殊な状況が絡んでいるので汎用的なものではありません。もう少し頑張れば行けるかもしれませんが、汎用的なところまで行く目処が立ちませんでした…。

特定の方には何のためのものかわかるかもしれませんが、そうでない方には何のためのものなのかわかりにくいと思います。
しかし、この記事ではその点をご説明しませんのであしからずご了承ください。

実現したいこと

まずは前提です。

  • iframeで画像が1枚だけあるページを読み込む
  • 画像のサイズは固定で1サイズでiframeにwidth属性が指定されている
  • 読み込むのは別ドメインのページ
  • IE8以下は切り捨て

上記の状態で、下記の実現を目指しました。

  • iframe内の画像をレスポンシブ対応させる

ここで問題となるのは、iframeで別ドメインのページを読み込むと、読み込んだ先ではcssでもjavascriptでも要素を操作できない点です。javacsriptはともかく、クロスドメインでもcssは効いて欲しい…。

もっとも、効けば効いたで問題が起こるのも分かるので、何かしらの制限を設けつつそこをクリアする方策などがあると助かります。が、ないものはないので色々調べて実験していました。

対応方法

今回の肝は、cssのtransformでした。詳細は下記をどうぞ。非常に助かりました。

外部コンテンツをiframeサイズで拡大縮小させたり、固定幅コンテンツをウィンドウサイズでピッタリ表示させる方法

つまりはtransformを使えばiframeを縮小拡大できるので、それを利用しました。
今回はiframe内のページには画像だけがあり、その画像の幅でiframeにwidthが設定されていたため、ともかくiframeの幅さえリサイズできればOKでしたので。

あとは、jQueryを用いてtransformに使う値を取得し、設定できれば解決です。

コード[2015.7.20追記]

もう少し汎用的な形にできましたので、更新です。

構造は以前と同じですが、念のためこちらでも記載しておきます。


<!-- HTML部分 -->
<div class="continer">
  <div class="wrap-iframe">
    <iframe src=" width="400" height="400">
      <!--画像のみのページを読み込み-->
    </iframe>
  </div>
  <div class="wrap-text">
    <!--テキストのみ-->
  </div>
</div>

/* css部分 */
.continer{ overflow:hidden; }
.wrap-text{ float:right; width:50%;}
.wrap-iframe{
  float:left; width:50%;
 -webkit-transform-origin: 0 0;
 -moz-transform-origin: 0 0;
 -ms-transform-origin: 0 0;
 transform-origin: 0 0;
}

改良したコードは以下のとおり。


<script type="text/javascript">
jQuery(function($){
  //基本サイズの設定
  var iframeBaseWidth = 400;

  //小数点第4位を切り捨て
  function divide(num){
    var  num1 = num * 10000;
    num1 =  Math.round(num1) / 10000;
    return num1;
  }

  $(window).on('load resize orientationchange', function(){
    //.wrap-iframeはiframeを囲む枠
    $('.wrap-iframe').each(function(){
      //iframeのwidth属性を取得
      var iframeWidth = $(this).find('iframe').attr('width');
      //現在のiframeを囲う枠のwidthを測定
      var thisIframeWidth = $(this).width();
      //枠のwidthと基本サイズとの差を割合で算出し小数点4位で切り捨て
      var baseWidth = iframeBaseWidth / iframeWidth;
      var baseWidth1 = divide(baseWidth);
      //枠のwidthと現在のサイズとの差を割合で算出し小数点4位で切り捨て
      var thisWidth = thisIframeWidth / iframeWidth;
      var thisWidth1 = divide(thisWidth);
      //iframeのheightを取得
      var iframeHeight = $(this).find('iframe').attr('height');
      //iframeのheightにwidthと同じ倍率をかけて高さを決定
      var setIframeHeight = iframeHeight * thisWidth;
      //レスポンシブでサイズを揃える
      $('iframe[width="' + iframeWidth + '"]').css({'transform':'scale('+ thisWidth1 +')','-webkit-transform':'scale('+ thisWidth1 +')','-moz-transform':'scale('+ thisWidth1 +')','-ms-transform':'scale('+ thisWidth1 +')'});
      $(this).css('height',setIframeHeight +'px');
    });
  });
});
</script>

前回はiframeが正方形の時や特定の幅の時にしか対応ができていませんでしたが、基本となる幅の変更や縦長横長の場合でも対応できるように変更しました。
ただ、やはりIE8には対応できませんでした。

コード内にコメントは入れていますが、概ね以下のような方法です。

  • iframeの横幅最大値を任意で決めて「基本サイズ」に設定
  • リサイズの度に「基本サイズ」「iframeのwidht属性」「iframeを囲む要素のwidth」を用いて必要な倍率を算出し、transfomeの値にいれてサイズを変更
  • 「iframeのheight属性」に算出した倍率をかけて枠のheightを設定

transfomeに設定する値をリサイズごとに動的に取得して設定することで、レスポンシブ対応の表示に見せている、ということです。
このままだと枠の高さが縮小前の状態のままなので、先に算出しておいた倍率を枠の高さにかけて、その値をheightに設定し変化させました。

コード(旧版)

先に前提となる構造を示します。

  • 横幅800pxの1カラム
  • 内部はそれぞれwidthを50%にしfloatで横並び
  • iframe(中の画像も)のwidthとheightは400

クラスなどは適当ですが、ベースの構造は以下の通りです。


<!-- HTML部分 -->
<div class="continer">
  <div class="wrap-iframe">
    <iframe src=" width="400" height="400">
      <!--画像のみのページを読み込み-->
    </iframe>
  </div>
  <div class="wrap-text">
    <!--テキストのみ-->
  </div>
</div>

/* css部分 */
.continer{ overflow:hidden; }
.wrap-iframe{ float:left; width:50%;}
.wrap-text{ float:right; width:50%;}

jQueryで全部設定しても問題ありませんが、まずは上記のcssに以下を追加。
前出の参考URLで書かれていますが、縮小時の起点を左上に設定するための記述です。


.wrap-iframe{
 -webkit-transform-origin: 0 0;
 -moz-transform-origin: 0 0;
 -ms-transform-origin: 0 0;
 transform-origin: 0 0;
}

続いて、下記のコードを記述。


<script type="text/javascript">
jQuery(function($){
  //iframeのwidthが400の場合
  $('.wrap-iframe iframe[width="400"]').addClass('w400');
  //表示時とリサイズ時に実行
  $(window).on('load resize', function(){
    //枠の幅を取得
    var s = $('.wrap-iframe').width()
    //iframeのサイズの元サイズ400pxに対して現在のサイズが何割りの大きさかを算出し、変数に入れる
    var d = s / 400;
    //変数を用いて数値を設定する
    $('iframe.w400').css({'transform':'scale(' + d + ')','-webkit-transform':'scale(' + d + ')','-moz-transform':'scale(' + d + ')','-ms-transform':'scale(' + d + ')'});
    //iframe内の画像が正方形なので、横幅と同じ値を枠に設定
    $('.wrap-iframe').css('height',s + 'px');
  });
});
</script>

これでウィンドウサイズが縮小すればiframeも縮小して表示されるはずです。

なお、iframeの横幅を取得してiframeにクラス「w400」を付けていますが、iframeが一つしかないなら不要です。ただ、youtubeや他の読み込みでiframeが存在する可能性もありますし、もう少し詳細に限定した方が安全かと思います。

IE8未対応

以下のようにすればIE8でも縮小自体は可能ですが、この値を動的に変更する方法がわかりませんでした。


/* 80%に縮小する場合 */
.iframe.w400{
 filter:progid:DXImageTransform.Microsoft.Matrix(M11=0.8,M22=0.8,sizingMethod="auto expand")
}

「zoom:0.8」でもいけるはずなのですが、縮小した状態が想定した形にはならず。zoomならjQueryの「.css」でtransfomeと同様の方法でセットできるはずです。

とはいえ、メディアクエリに対応していないIE8はcssのbackground-sizeなども効きませんし、レスポンシブでの変化から切り離したほうが良いのかもしれません。

結び

無理矢理感もありますが、iframeで読み込んだ別ドメインの表示サイズをレスポンシブに対応する形で拡大縮小させることができました。
動的な部分はいままで作った経験が活かせましたし、いろいろやってみるものですね。

とはいえ無駄も制限もありますし、もっとスマートで良い方法があればとは思うのですが、別ドメインとなるとなかなか…。後日思いつきましたらコードのブラッシュアップを含めて更新できればと思います。

1人がこの記事を評価

役に立ったよという方は上の「記事を評価する」ボタンをクリックしてもらえると嬉しいです。

連投防止のためにCookie使用。SNSへの投稿など他サービスとの連動は一切ありません。

コメント欄