jQuery UIを使ってiframeをリサイズできるようにする

jQueryjQuery UIがあれば大抵のことはできるのですが、意外なことにiframeのリサイズができなかったので、できるようにしてみました。


といっても、jQuery UIのResizableにiframe用の処理追加しただけです。
デモ

iframeをdivで囲む

Resizableは、リサイズハンドラーとしてのdiv要素を対象要素の子ノードとして追加するのですが、iframeにはappendChildできません。そこで、iframeをdivで囲みdivをリサイズ対象にして、alsoResizeとしてiframeを指定します(divのリサイズにiframeを連動させる)。

リサイズ中はiframeをdivで覆っておく

通常iframe上でのイベントは、iframe内で起きます。iframe上でマウス動かしても、$(document).mousemoveが起こらないって事です。そこで、iframeに透明なdiv要素をオーバレイさせることで、iframeにイベント横取りされるのを防ぎます。


以下ソース

$(function()
{
  $('#wrapper').resizable({
    handles: 'e, s',
    alsoResize: '#shinshu-u',
    start: function(event, ui)
    {
      // Add frame helpers
      $("iframe").each(function() {
        var offsetWidth = this.offsetWidth, offsetHeight = this.offsetHeight;

        $('<div class="ui-resizable-iframeFix" style="background: #fff;"></div>')
          .css({
            width: offsetWidth+"px", height: offsetHeight+"px",
            position: "absolute", opacity: "0.001", zIndex: 1000
          })
          .css($(this).offset())
          .appendTo("body")
          .data("resizable", { width: offsetWidth, height: offsetHeight });
      });
    },
    resize: function(event, ui)
    {
      var self = $('#wrapper').data("resizable"), o = self.options, os = self.originalSize, op = self.originalPosition;

      var delta = {
        height: (self.size.height - os.height) || 0, width: (self.size.width - os.width) || 0,
        top: (self.position.top - op.top) || 0, left: (self.position.left - op.left) || 0
      },

      _alsoResize = function(exp, c) {
        $(exp).each(function() {
          var el = $(this), start = $(this).data("resizable"), style = {}, css = c && c.length ? c : ['width', 'height', 'top', 'left'];

          $.each(css || ['width', 'height', 'top', 'left'], function(i, prop) {
            // iframeより少し大きめに設定することで、iframe上でmouseupしてしまったときにmouseupイベント捕捉できない問題解消
            var sum = (start[prop]||0) + (delta[prop]||0) + 5;
            if (sum && sum >= 0)
              style[prop] = sum || null;
          });

          //Opera fixing relative position
          if (/relative/.test(el.css('position')) && $.browser.opera) {
            self._revertToRelativePosition = true;
            el.css({ position: 'absolute', top: 'auto', left: 'auto' });
          }

          el.css(style);
        });
      };

      _alsoResize('div.ui-resizable-iframeFix', ['width', 'height']);
    },
    stop: function(event, ui)
    {
      // Remove frame helpers
      $("div.ui-resizable-iframeFix")
        .removeData('resizable')
        .each(function() { this.parentNode.removeChild(this); });
    }
  });
});
<!DOCTYPE HTML PUBLIC '-//W3C//DTD HTML 4.01//EN' 'http://www.w3.org/TR/html4/strict.dtd'>
<html>
  <head>
    <meta http-equiv='Content-type' content='text/html; charset=utf-8'>
    <link href='css/ui.all.css' type='text/css' rel="stylesheet"/>
    <script type='text/javascript' language='javascript' src="http://ajax.googleapis.com/ajax/libs/jquery/1.3.2/jquery.min.js" charset='utf-8'></script>
    <script type='text/javascript' language='javascript' src="http://ajax.googleapis.com/ajax/libs/jqueryui/1.7.2/jquery-ui.min.js" charset='utf-8'></script>
    <script type='text/javascript' language='javascript' src="js/resize-iframe.js" charset='utf-8'></script>
    <style type='text/css' media='screen'>
    /* <![CDATA[ */
      #wrapper, #shinshu-u { width: 300px; height: 300px; }
      .ui-resizable-handle { background-color: #CCC; }
    /* ]]> */
    </style>
    <title>resize</title>
  </head>
  <body>
    <div id="wrapper">
      <iframe id="shinshu-u" src="http://www.cs.shinshu-u.ac.jp/"></iframe>
    </div>
  </body>
</html>