Proportionally resize iframe to fit in a DIV using jQuery
Asked Answered
C

3

4

I have an iframe of a video inside a div, like so:

<div class="media">
    <iframe>
</div>

I set the DIV's size dynamically on window resize.

I want to scale the iframe to fit inside the div, while maintaining it's aspect ratio. Most of the code out there deals with scaling images, which is easier.

This is what I have so far, but it doesn't work:

jQuery.fn.fitToParent = function()
{
    this.each(function()
    {
        var width  = jQuery(this).width();
        var height = jQuery(this).height();
        var parentWidth  = jQuery(this).parent().width();
        var parentHeight = jQuery(this).parent().height();

        if(width < parentWidth)
        {
            newWidth  = parentWidth;
            newHeight = newWidth/width*height;
        }
        else
        {
            newHeight = parentHeight;
            newWidth  = newHeight/height*width;
        }

        jQuery(this).css({
            'height'     :newHeight,
            'width'      :newWidth'
        });
    });
};

Basically, I'm looking to replicate the sizing that "background-size: contain" does for images in CSS, but for an iframe in a DIV.

Thanks for the help!

Concentre answered 16/9, 2013 at 23:23 Comment(0)
C
3

I have really improved on the answer from @TrueBlueAussie over time, and thought I'd post a more sophisticated answer here for future reference.

Made it a plugin on GitHub here: https://github.com/drewbaker/fitToParent

Here is the jQuery plugin:

jQuery.fn.fitToParent = function (options) {

    this.each(function () {

        // Cache the resize element
        var $el = jQuery(this);

        // Get size parent (box to fit element in)
        var $box;
        if( $el.closest('.size-parent').length ) {
            $box = $el.closest('.size-parent');
        } else {
            $box = $el.parent();
        }

        // These are the defaults.
        var settings = jQuery.extend({  
                height_offset: 0,
                width_offset: 0,
                box_height: $box.height(),
                box_width: $box.width(),
        }, options );

        // Setup box and element widths
        var width = $el.width();
        var height = $el.height();
        var parentWidth = settings.box_width - settings.width_offset;
        var parentHeight = settings.box_height - settings.height_offset;

        // Maintin aspect ratio
        var aspect = width / height;
        var parentAspect = parentWidth / parentHeight;

        // Resize to fit box
        if (aspect > parentAspect) {
            newWidth = parentWidth;
            newHeight = (newWidth / aspect);
        } else {
            newHeight = parentHeight;
            newWidth = newHeight * aspect;
        }

        // Set new size of element
        $el.width(newWidth);
        $el.height(newHeight); 

    });
};

So, assuming you have HTML of this:

<div id="wrapper">
    <iframe width="720" height="405" src="//player.vimeo.com/video/19223989"></iframe>
</div>

The most basic way to call the plugin is like this:

jQuery('#wrapper iframe').fitToParent();

But I'll often set #wrapper to be close to window height and width, like this:

// Get window height and width
var winHeight = jQuery(window).height();
var winWidth = jQuery(window).width();

// Set wrapper height/width to that of window
jQuery('#wrapper').height(winHeight).width(winWidth);

// Size Iframe
jQuery('#wrapper iframe').fitToParent({
    height_offset: 100, // Put some space around the video
    width_offset: 100, // Put some space around the video
});

You can also feed in a custom box size to fit the element in, like this:

// Get window height and width
var winHeight = jQuery(window).height();
var winWidth = jQuery(window).width();

// Size element
jQuery('#wrapper iframe').fitToParent({
    height_offset: 100, // Put some space around the video
    width_offset: 100, // Put some space around the video
    box_height: winHeight, // Force use of this box size
    box_width: winWidth // Force use of this box size
});

I've also added the ability to set a CSS class of "size-parent" to a parent element, and it will then use that parent element for the box size. A full example of that:

// HTML like this
<div id="wrapper" class="size-parent">
    <div class="media">
        <iframe width="720" height="405" src="//player.vimeo.com/video/19223989"></iframe>
    </div>
</div>

// jQuery like this
jQuery('.media iframe').fitToParent();    

If you don't set a .size-parent, it will fallback to the element parent. If you set the box_height/box_width parameter, then those override everything obviously.

Now, to show how powerful this can be, try this for a vertically centered, horizontal centered aspect ratio correct iFrame!

// CSS like this
#wrapper {
    text-align: center;
    display: table-cell;
    vertical-align: middle;
}
#wrapper iframe {
    display: inline-block;
}

// HTML like this
<div id="wrapper" class="size-wrapper">
    <iframe width="720" height="405" src="//player.vimeo.com/video/19223989"></iframe>
</div>

// jQuery like this
// Get window height and width
var winHeight = jQuery(window).height();
var winWidth = jQuery(window).width();

// Size wrapper
jQuery('#wrapper').height( winHeight ).width( winWidth );

// Size element
jQuery('#wrapper iframe').fitToParent({
    height_offset: 200, // Put some space around the video
    width_offset: 200, // Put some space around the video
});

// Fit iFrame to wrapper
jQuery('#wrapper iframe').fitToParent();

In real life, I wrap the jQuery in a function, and then call that function on window resize for true responsive iFrames!

Concentre answered 9/8, 2014 at 19:45 Comment(0)
D
7

Three issues noted:

  1. You have an error (trailing quote) in your example:

    :newWidth'

  2. You need to set the iframe actual height and width attributes and not the style. Styling an iframe size has no effect:

    $(this).width(newWidth);
    $(this).height(newHeight);
    
  3. the calculation for aspect ratio was wrong (needs to compare ratios to see which way they overlap). Without this not all overlap cases are catered for.

    var aspect = width/height;
    var parentAspect = parentWidth/parentHeight;
    if (aspect > parentAspect)
    {
        newWidth  = parentWidth;
        newHeight = newWidth / aspect;
    }
    else
    {
        newHeight = parentHeight;
        newWidth  = newHeight * aspect;
    }
    

I also cleaned up the code a little to speed up element access. Each call to jQuery(this) costs cycles.

JSFiddle here: http://jsfiddle.net/TrueBlueAussie/ZJDkF/8/

Updates:

The jsfiddle now has examples of the 4 different scenarios of overlap and each retains the proportions of the iframe. I also added the window resize you mentioned and made the first div resize dynamically with it to demonstrate.

Duologue answered 19/9, 2013 at 11:53 Comment(2)
This is amazing, very well done. I'll award the bounty as soon as it lets me.Concentre
Safari would loose ratio on resize, due to rounding errors adding up. So I saved the aspect ratio on display, and they used that ratio for all future sizing: jsfiddle.net/ZJDkF/9Concentre
C
3

I have really improved on the answer from @TrueBlueAussie over time, and thought I'd post a more sophisticated answer here for future reference.

Made it a plugin on GitHub here: https://github.com/drewbaker/fitToParent

Here is the jQuery plugin:

jQuery.fn.fitToParent = function (options) {

    this.each(function () {

        // Cache the resize element
        var $el = jQuery(this);

        // Get size parent (box to fit element in)
        var $box;
        if( $el.closest('.size-parent').length ) {
            $box = $el.closest('.size-parent');
        } else {
            $box = $el.parent();
        }

        // These are the defaults.
        var settings = jQuery.extend({  
                height_offset: 0,
                width_offset: 0,
                box_height: $box.height(),
                box_width: $box.width(),
        }, options );

        // Setup box and element widths
        var width = $el.width();
        var height = $el.height();
        var parentWidth = settings.box_width - settings.width_offset;
        var parentHeight = settings.box_height - settings.height_offset;

        // Maintin aspect ratio
        var aspect = width / height;
        var parentAspect = parentWidth / parentHeight;

        // Resize to fit box
        if (aspect > parentAspect) {
            newWidth = parentWidth;
            newHeight = (newWidth / aspect);
        } else {
            newHeight = parentHeight;
            newWidth = newHeight * aspect;
        }

        // Set new size of element
        $el.width(newWidth);
        $el.height(newHeight); 

    });
};

So, assuming you have HTML of this:

<div id="wrapper">
    <iframe width="720" height="405" src="//player.vimeo.com/video/19223989"></iframe>
</div>

The most basic way to call the plugin is like this:

jQuery('#wrapper iframe').fitToParent();

But I'll often set #wrapper to be close to window height and width, like this:

// Get window height and width
var winHeight = jQuery(window).height();
var winWidth = jQuery(window).width();

// Set wrapper height/width to that of window
jQuery('#wrapper').height(winHeight).width(winWidth);

// Size Iframe
jQuery('#wrapper iframe').fitToParent({
    height_offset: 100, // Put some space around the video
    width_offset: 100, // Put some space around the video
});

You can also feed in a custom box size to fit the element in, like this:

// Get window height and width
var winHeight = jQuery(window).height();
var winWidth = jQuery(window).width();

// Size element
jQuery('#wrapper iframe').fitToParent({
    height_offset: 100, // Put some space around the video
    width_offset: 100, // Put some space around the video
    box_height: winHeight, // Force use of this box size
    box_width: winWidth // Force use of this box size
});

I've also added the ability to set a CSS class of "size-parent" to a parent element, and it will then use that parent element for the box size. A full example of that:

// HTML like this
<div id="wrapper" class="size-parent">
    <div class="media">
        <iframe width="720" height="405" src="//player.vimeo.com/video/19223989"></iframe>
    </div>
</div>

// jQuery like this
jQuery('.media iframe').fitToParent();    

If you don't set a .size-parent, it will fallback to the element parent. If you set the box_height/box_width parameter, then those override everything obviously.

Now, to show how powerful this can be, try this for a vertically centered, horizontal centered aspect ratio correct iFrame!

// CSS like this
#wrapper {
    text-align: center;
    display: table-cell;
    vertical-align: middle;
}
#wrapper iframe {
    display: inline-block;
}

// HTML like this
<div id="wrapper" class="size-wrapper">
    <iframe width="720" height="405" src="//player.vimeo.com/video/19223989"></iframe>
</div>

// jQuery like this
// Get window height and width
var winHeight = jQuery(window).height();
var winWidth = jQuery(window).width();

// Size wrapper
jQuery('#wrapper').height( winHeight ).width( winWidth );

// Size element
jQuery('#wrapper iframe').fitToParent({
    height_offset: 200, // Put some space around the video
    width_offset: 200, // Put some space around the video
});

// Fit iFrame to wrapper
jQuery('#wrapper iframe').fitToParent();

In real life, I wrap the jQuery in a function, and then call that function on window resize for true responsive iFrames!

Concentre answered 9/8, 2014 at 19:45 Comment(0)
G
0

here:

http://jsfiddle.net/fFTS8/

  <div id="test" style="width:300px;height:200px;background:red;"></div>
  <script src="js/vendor/jquery-1.10.2.min.js"></script>
  <script type="text/javascript">
     jQuery.fn.fitToParent = function()
     {
        var that = this;

        function rezise() {
           that.each(function()
           {
             var a = $(this).width();
             var b = $(this).height();
             var c = $(this).parent().width();
             var d = $(this).parent().height();

             var ab = a/b;
             var cd = c/b;

             var e, f = 0; // e - newWidth, f - newHeight

             if(ab > cd) {
                e = c;
                f = c*b/a;
             } else {
                e = a*d/b;
                f = d;
             }

             $(this).width(e);
             $(this).height(f);
           });
        }

        $(window).resize(function() {
           rezise();
        });
     };
     $('#test').fitToParent();

  </script>
Gannie answered 19/9, 2013 at 11:24 Comment(1)
According to the asker the iframe is meant to resize to the parent div. This one is backwards and resizes the div to the JSFiddle iframe.Duologue

© 2022 - 2024 — McMap. All rights reserved.