Strategies for the iFrame on the iPad Problem

What’s the deal?

IFrames originally behaved on iOS mobile safari sort of how you would expect, the iframe had the size that you set, and the contents inside could be scrolled – with two fingers. Apple changed all that with an update in 2011. The current behaviour is that an iframe width is as you specify it in your code, but the iframe expands vertically to display the full document which it contains. Whoah.
I can imagine why they thought that was a good solution, now the iframe document is fully viewable within the host document, and users are not confused by a scrolling region within a scrolling page. But an expanding iframe is not so great for a number of situations, for example if you are building a web application that behaves like a typical desktop application. A desktop application has scrolling regions – but the main window itself usually does not scroll.

This is all related to another iOS mobile safari problem, wanting to put a large amount of content in a div on the page which has a fixed size. Previously, the “overflow:scroll;” css property did not work, and it still does not have “momentum scrolling” as expected and so several plugins have been created to address this such as iScroll. But iScroll does not work for an iFrame because the touch events fire in the iFrame document, but the parent document must do the scrolling.

Apple/webkit recently alleviated some of the problem with a css property called -webkit-overflow-scrolling: touch;
With this property a div with a lot of content will behave how you would like it to, and it even has the nice mobile safari “momentum scroll” when you flick it with your finger. Cool yeah? I bumped into tons of enthusiastic blog posts on this when the feature was announced. Problem is, its buggy. Under certain situations the contents of a container with this property will not be properly displayed, portions of it will be blank.

Unfortunately I have not discovered a solution that will make an iframe behave the way you want it to in all cases. I did find a solution that works in our specific case – and along the way a number of strategies for addressing it which I’ll list here. Consider if you really need an iframe, if you don’t then an overflow div may be enough, you can use the standard “overflow:scroll;”, or “-webkit-overflow-scrolling:touch;”, or use a library like iScroll. You could even load another document in with AJAX. In our case we require an iframe so that we have a separate context of CSS and Javascript. You’ll want to view the test cases on your iPad or android tablet – Ive found Site To Phone to be a handy tool if you dont have link sharer yet.

Strategies

iFrame in an Overflow Div #1: -webkit-overflow-scrolling: touch

Test Case:
http://dev.magnolia-cms.com/~czimmermann/blog/ipad-iframe/iframe1.html
http://dev.magnolia-cms.com/~czimmermann/blog/ipad-iframe/iframe1-tall.html

iFrame wants to expand vertically? Fine just let it. Put it in a div with a fixed height. But how to scroll it then, applying “overflow:scroll;” on the enclosing div does not work. But the new “-webkit-overflow-scrolling: touch;” on the enclosing div does work! But there can be rendering problems.
Some people have solved these problems by ensuring that the div contents have only static positioning. One person reported they were able to solve it by forcing hardware acceleration. (SO: images dissapear) (SO: webkit-overflow-overflow error)
In our case we have no control over the iframe contents, so these were not an option.
In my own experimentation with Iframes, the render problem consistently appeared when the contents of the iframe had a greater width or height, then the containing page. (Maybe iOS allocates graphics memory based on parent document size?)
A strategy for this is simply to increase the size of the parent body to match that of the iframe contents (as suggested here:SO iframe content not rendering).
This is the solution that works for us. The rest of our app elements are absolutely positioned and our iframe has 100% width, so a user does not notice that we have created a tall page when we load the iframe contents. The additional problem that this caused is that once the user has scrolled to the bottom of the iframe, then on the *next* down scroll gesture, the whole app scrolls up. Whoops. Until Apple fixes their bug, our solution is to capture scroll events and animate the documents scrollTop back to zero when we detect a document scroll.

/*
Prevent Scrolling down.
Uses jQuery.
*/
$(document).on("scroll",function(){
    checkForScroll();
});

var checkForScroll = function(e)
{
    var iScroll = $(document).scrollTop();
    if (iScroll > 1){
        // Disable event binding during animation
        $(document).off("scroll");

        // Animate page back to top
        $("body,html").animate({"scrollTop":"0"},500,function(){
            $(document).on("scroll",checkForScroll);
        });
    }
}

iFrame in an Overflow Div #2:  Javascript Scrolling

Test Case:
http://dev.magnolia-cms.com/~czimmermann/blog/ipad-iframe/iframe-js.html

Another concept, explored here (SO: iframe js scrolling). is to put the iframe in a div as above, and to add touch event handlers in the iframe document to capture the drag events. This code then calls to the parent document to move the iframe within the div. I experimented with this, but in my short tests the iframe content jerked around instead of smooth scrolling – understandable given the strange situation. Perhaps this could be made smooth – but I have not seen an example of that yet. This solution could only work if you control the contents of the iframe as you must have your touch detection javascript in there.
If someone gets this approach to work nicely, please post a comment.

Fixed Controls & Scrolling iFrame Page

Test Case:
http://dev.magnolia-cms.com/~czimmermann/blog/ipad-iframe/iframe-fixed.html

Another strategy is to just let the iframe expand and fill the page, but surround it with position:fixed elements such as a header, side bars, and even a footer. Fixed position elements work great in iOS 5, and you could fall back to javascript to reposition the fixed elements after a scroll in iOS 4 and below. This almost worked for us.

Popup Window

For completeness sake, if all else fails you could simply launch that iframe content in a popup browser window. I know, i know.

Object instead of IFrame

Test Case: Covered in first Strategy.

I saw a few references to using an object tag instead of an iframe to solve the problem.
An object tag does indeed retain its size as it should – but the content was still not scrollable in my tests. I tried expanding the size of the object element and putting it in an overflow div just like the iframe – but it had exactly the same rendering problems as the iframe, so I don’t see any advantage to the object tag.

Example of Object tag to embed another web page:

<object type="text/html" data="content-to-scroll.html"></object>

They found that object works (but does not for me): (SO: ios cropping issue)

Notes

It’s interesting to see that a div with -webkit-overflow-scrolling: touch renders differently, it appears a bit blurry. I guess this is the hardware acceleration – as it reminds me of a how a hardware accelerated css transitioning div looks, you can see it pop back to crispness when the transition is over.

Conclusion

What a mess, huh? There are some strategies but all of them have some pitfalls. I welcome your feedback and suggestions. How have you coped with the situation? Anyone from Apple care to comment?

20 thoughts on “Strategies for the iFrame on the iPad Problem

  1. Addendum: The context of this problem is that we are working on a page editor tool for our product Magnolia CMS. We need to have the context of the CMS editor with its tools and navigation – and also to load in the website which is being edited. An IFrame is a great match because it keeps the CSS/Javascript context separated, and ideally allows us to keep our application as a full screen application – yet scroll through the website page being edited.

  2. Pingback: Strategies for the iFrame on the iPad Problem | CMS Radar

  3. Thanks for the very informative article. I spent some time struggling with this problem, none of the solutions you listed above worked quite perfectly for me. I eventually took a completely different approach, which so far seems to be very robust – attaching a load handler to the iframe which wraps the iframe’s document contents in a scrollable div. I call this function from the parent:


    $(function(){
    if (/iPhone|iPod|iPad/.test(navigator.userAgent)) {

    $("iframe").bind("load",function() {
    var b = this.contentWindow.document.body;

    var div = $(document.createElement("div"));
    var divCSS = {
    'height' : $(this.parentNode).height(),
    'width' : $(this.parentNode).width(),
    'overflow' : 'scroll'
    }
    div.css(divCSS);

    // Move the body's children into this wrapper
    while (b.firstChild)
    {
    div.append(b.firstChild);
    }

    // Append the wrapper to the body
    $(b).append(div);
    });
    }
    })

  4. One (http://dev.magnolia-cms.com/~czimmermann/blog/ipad-iframe/iframe-js.html) of your approaches works for me but I am facing a problem. The website I am working on requires to display multiple (dynamic number of) PDFs on one page. While PDFs can be scrolled with two fingers with the help of your code, they are not centered and are cutting off on the right side.

    Do you have the same problem? Do you have an idea how to make them centered within the iFrame?

    Thanks a lot for this post and this is the only solution I found that works with a little tweak.

    • Hi Deb,
      I’m glad the post has helped you a bit.
      I have not worked with PDF’s in an iframe, I assume that there is not much control on how they are displayed. Could you perhaps solve the problem by the positioning of the iframe in the page (rather then the positioning of the PDF in the iframe)? For example – could you apply a negative left margin (margin-left: -200px;) to the iframe?
      I have not tried that – just an idea.
      Good luck.

      Also, Im curious: What was the tweak that you needed to use to get one of my approaches to work?

  5. User csdco (https://github.com/fancyapps/fancyBox/issues/2#issuecomment-5997068) has provided a simple answer that solved the issue for me.

    ———————————–
    It’s much easier to control overflowed divs than it is iframes, and the scrolling + blank content issues are working all the way back to iOS 4, where previously I wasn’t even able to get the 2 finger scrolling to work (in iframes).

    It goes something like this:

    a-file.html:

  6. Pingback: Steve Hannah: This week » Scrolling iFrame on iOS

  7. Have u ever use “include ” instead of iframe ? “Server side include” ~ It is similar to PHP’s include ~ Need a sever to test the result , but I don’t have a server…XD

  8. If you have control over the contents in your iframe, add the -webkit-transform: translate3d(0,0,0) CSS style to force hardware rendering to get rid of the blank space when the iFrame is scrolled (iOS5 bug). Try something like this:

    $(‘iframe’).load(function(){
    $(‘iframe’).contents().find(‘body’).css(‘-webkit-transform’, ‘translate3d(0, 0, 0)’);
    });

  9. Pingback: Responsive and Adaptive Design Research – iframes on iOS | the open notebook

  10. Pingback: Problems displaying PDF in iFrame on Mobile Safari | BlogoSfera

  11. Great explanation, which helped me a lot!

    I finally used the following approach (since I required a “sticky-top-right” container):

    HTML:

    CSS:
    #container {
    width: 210px;
    height: 100%;
    display: block;
    z-index:100000001;
    position:fixed;
    top: 0px;
    right: 0px;
    -webkit-overflow-scrolling: touch;
    }

    #frame {
    height: 100%;
    width:195px;
    z-index:100000002;
    }

    And some jQuery:
    if (/iPhone|iPod|iPad/.test(navigator.userAgent)) {
    $(“#container”).css({ overflow: ‘scroll’ });
    }

    Maybe this helps others, too.

  12. Pingback: Building Responsive Widgets for Mobile

  13. Even though the object example didn’t work for you, I am infinitely grateful that you included it — it does the job perfectly for my use case. Thanks!

    Why on earth can’t they just FIX this?

Leave a Reply

Your email address will not be published. Required fields are marked *

*


7 + = eleven

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>