16

Situation: I'm working on a responsive design that involves the typical HTML/CSS combo. Everything is working nicely except in one case where there is an iframe inside of a div. The iframe should adjust automatically to the size of the parent div. A purely css solution has not presented itself so I'm going with a JQuery approach. It works nicely except in one scenario, when resizing from a smaller width to a larger width screen.

HTML:

<div class="container">
    <iframe class="iframe-class" src="http://www.cnn.com/"></iframe>
</div>

CSS:

.container {
    width: 100%;
    height: 250px;
}
.iframe-class {
    width: 100%;
    height: 100%;
    border: 1px solid red;
    overflow: auto;
}

Javascript:

$(function () {
    setIFrameSize();
    $(window).resize(function () {
        setIFrameSize();
    });
});

function setIFrameSize() {
    var ogWidth = 700;
    var ogHeight = 600;
    var ogRatio = ogWidth / ogHeight;

    var windowWidth = $(window).width();
    if (windowWidth < 480) {
        var parentDivWidth = $(".iframe-class").parent().width();
        var newHeight = (parentDivWidth / ogRatio);
        $(".iframe-class").addClass("iframe-class-resize");
        $(".iframe-class-resize").css("width", parentDivWidth);
        $(".iframe-class-resize").css("height", newHeight);
    } else {
        // $(".iframe-class-resize").removeAttr("width");
        // $(".iframe-class-resize").removeAttr("height");
        $(".iframe-class").removeClass("iframe-class-resize");
    }
}

http://jsfiddle.net/TBJ83/

Problem: As the window is resized smaller, it continually checks the window width and once it hits < 480 px, the code adds a class called iframe-class-resize and sets the width and height to that class. As the window is resized larger, it removes the class once the size hits 480 px. The problem is that setting the width and height attributes adds them directly to the element and not the class itself. Therefore, removing the class does not remove the new width and heights. I tried to force removing the attributes using removeAttr() (commented out above) but that didn't work.

Anyone see where the code above went wrong? Or any suggestions on how to accomplish having a responsive iframe more effectively? The main things that are required are that the iframe has to be inside the <div></div> and the div may not necessarily have a width or height defined. Ideally, the parent div should have the width and height explicitly defined but the way this site is currently setup, that won't always be possible.

Additional: In case the above wasn't clear enough, try the following to reproduce the issue:

  • Open up a browser on a desktop machine. I'm using Chrome on a Windows machine. Don't maximize the browser.
  • Open up the jsfiddle above (http://jsfiddle.net/TBJ83/). You'll notice that the iframe content spans the entire width of the Preview panel.
  • Manually resize the width down until the entire window is < 480px. At this point, the iframe content will be pretty tiny.
  • Manually resize the width back up until the entire window is >> 480px. The goal is to have that iframe content to regain the entire width of the Preview panel. Instead, the content is retaining the resized width and height since the .css() function applies css changes directly to elements rather than to the classes.

Thanks in advance!

jiminy
  • 1,612
  • 3
  • 18
  • 21

4 Answers4

9

You can do this in about 30 characters. Change:

$(".iframe-class").removeClass("iframe-class-resize")

to:

$(".iframe-class").removeClass("iframe-class-resize").css({ width : '', height : '' })

This will reset the width/height you applied to the element. When you use .css() you add whatever you pass-in to the style attribute of the element. When you pass a blank value, jQuery removes that property from the style attribute of the element.

Here is an updated fiddle: http://jsfiddle.net/TBJ83/3/

EDIT

OK, here's something tweaked for performance (and just some other ways to do things):

$(function () {

    //setup these vars only once since they are static
    var $myIFRAME   = $(".iframe-class"),//unless this collection of elements changes over time, you only need to select them once
        ogWidth     = 700,
        ogHeight    = 600,
        ogRatio     = ogWidth / ogHeight,
        windowWidth = 0,//store windowWidth here, this is just a different way to store this data
        resizeTimer = null;

    function setIFrameSize() {
        if (windowWidth < 480) {

            var parentDivWidth = $myIFRAME.parent().width(),//be aware this will still only get the height of the first element in this set of elements, you'll have to loop over them if you want to support more than one iframe on a page
                newHeight      = (parentDivWidth / ogRatio);

            $myIFRAME.addClass("iframe-class-resize").css({ height : newHeight, width : parentDivWidth });
        } else {
            $myIFRAME.removeClass("iframe-class-resize").css({ width : '', height : '' });
        }
    }

    $(window).resize(function () {

        //only run this once per resize event, if a user drags the window to a different size, this will wait until they finish, then run the resize function
        //this way you don't blow up someone's browser with your resize function running hundreds of times a second
        clearTimeout(resizeTimer);
        resizeTimer = setTimeout(function () {
            //make sure to update windowWidth before calling resize function
            windowWidth = $(window).width();

            setIFrameSize();

        }, 75);

    }).trigger("click");//run this once initially, just a different way to initialize
});
Jasper
  • 75,717
  • 14
  • 151
  • 146
  • 1
    @jiminy Glad to help. Honestly you did pretty well with your code. You can chain some calls and combine `.css()` calls by passing-in an object. You could also calculate the ratio once rather than each resize as well as throttle your resize event handler. Other than that it looks pretty good. You could simplify but at the cost of changing your whole method, and the savings would be minimal. – Jasper Feb 19 '14 at 01:27
  • 1
    I don't know, look how many jQuery selectors are in that code, like 5. That's 5 times parsing through the DOM to find those items. I updated mine, it only does 1 selector, then reuses the object. – gfrobenius Feb 19 '14 at 01:28
  • 1
    @gfrobenius I kind of assumed the class selectors are that way for a reason but if not then you're right. The code does assume a single element on the page, `$(".iframe-class").parent().width()`, will only return a single width, for the first element it finds that matches the selector functions. But all said, I've done a lot of performance tests in the last few years and a lot of usability tests and these selectors shouldn't slow the script down noticeably. – Jasper Feb 19 '14 at 01:31
  • 1
    @Jasper Thanks for the feedback. Glad to know the code is in the right direction. =) I'll check out your suggestions. – jiminy Feb 19 '14 at 01:33
  • I'll play around with the code and see if speed becomes a significant issue. Thank you to both of you for pointing that out. – jiminy Feb 19 '14 at 01:33
  • @jiminy Take a look at my update. It's got some examples of how to be better with performance as well as some different ways to do things. Notice how most of what I did was simplify the code inside the resize-event-handler. That's the code that will take most of the time to run, so it's the best place to work on performance. – Jasper Feb 19 '14 at 01:41
  • @Jasper Awesome! I'll look into integrating the suggestions. P.S. The comments in your code are much appreciated. – jiminy Feb 19 '14 at 01:50
5

This can be done using pure CSS as below:

iframe {
  height: 300px;
  width: 300px;
  resize: both;
  overflow: auto;
}

Set the height and width to the minimum size you want, it only seems to grow, not shrink.

Abhi Andhariya
  • 550
  • 5
  • 15
  • 2
    Awesome - THIS is what I was looking for, and it's so dang simple! – ashleedawg Jun 22 '19 at 00:12
  • I can't understand why the jQuery **thing** above exists. I tested the fiddle with an other `src`, and it didn't work. Then, I removed all jQuery and just added `resize: both` in the CSS and it worked very well. – BarryCap Jun 01 '21 at 17:30
2

This is how I would do it, code is much shorter: http://jsfiddle.net/TBJ83/2/

<div class="container">
    <iframe id="myframe" src="http://www.cnn.com/"></iframe>
</div>

<script>
$(function () {
    setIFrameSize();
    $(window).resize(function () {
        setIFrameSize();
    });
});

function setIFrameSize() {
    var parentDivWidth = $("#myframe").parent().width();
    var parentDivHeight = $("#myframe").parent().height();
    $("#myframe")[0].setAttribute("width", parentDivWidth);
    $("#myframe")[0].setAttribute("height", parentDivHeight);
}
</script>

I did it that way for read-ability, but you could make it even shorter and faster...

function setIFrameSize() {
    f = $("#myframe");
    f[0].setAttribute("width", f.parent().width());
    f[0].setAttribute("height", f.parent().height());
}

One selector, so you only look through the DOM once instead of multiple times.

gfrobenius
  • 3,987
  • 8
  • 34
  • 66
  • 1
    That fiddle doesn't seem to respect the hard-coded aspect ratio when under 480px wide. – Jasper Feb 19 '14 at 01:25
  • 1
    This didn't end up working in my case. However, I like the idea of it and I'll still keep it in mind for future reference. Thanks! – jiminy Feb 19 '14 at 01:26
  • This works perfectly for a resizable container I need an embedded iframe to fill completely and remain responsive within -- many thanks. – jimiayler Jun 03 '21 at 16:46
1

For those using Prestashop, this is how I used the code.

In the cms.tpl file I added the below code:

{if $cms->id==2}
<script type='text/javascript' src='http://ajax.googleapis.com/ajax/libs/jquery/1.3.2/jquery.min.js?ver=1.3.2'></script>
<script type="text/javascript" src='../themes/myheme/js/formj.js'></script>
<div style="width: 100%; height: 800px;">
<iframe style=" width: 100%; height: 100%; border: overflow: auto;" src="https://cnn.com"></iframe>
</div>
{/if}

Then created a new js file: formj.js and added the below code:

$(function () {

    //setup these vars only once since they are static
    var $myIFRAME   = $(".iframe-class"),//unless this collection of elements changes over time, you only need to select them once
        ogWidth     = 970,
        ogHeight    = 800,
        ogRatio     = ogWidth / ogHeight,
        windowWidth = 0,//store windowWidth here, this is just a different way to store this data
        resizeTimer = null;

    function setIFrameSize() {
        if (windowWidth < 480) {

            var parentDivWidth = $myIFRAME.parent().width(),//be aware this will still only get the height of the first element in this set of elements, you'll have to loop over them if you want to support more than one iframe on a page
                newHeight      = (parentDivWidth / ogRatio);

            $myIFRAME.addClass("iframe-class-resize").css({ height : newHeight, width : parentDivWidth });
        } else {
            $myIFRAME.removeClass("iframe-class-resize").css({ width : '', height : '' });
        }
    }

    $(window).resize(function () {

        //only run this once per resize event, if a user drags the window to a different size, this will wait until they finish, then run the resize function
        //this way you don't blow up someone's browser with your resize function running hundreds of times a second
        clearTimeout(resizeTimer);
        resizeTimer = setTimeout(function () {
            //make sure to update windowWidth before calling resize function
            windowWidth = $(window).width();

            setIFrameSize();

        }, 75);

    }).trigger("click");//run this once initially, just a different way to initialize
});
Steve O
  • 5,224
  • 1
  • 24
  • 26
keylawnzo
  • 81
  • 1
  • 1
  • 4