55

I'm trying to display an iframe in my mobile web application, but I'm having trouble restricting the size of the iframe to the dimensions of the iPhone screen. The height and width attributes on the iframe element seem to have no effect, strangely. Surrounding it with a div manages to constrain it, but then I'm unable to scroll within the iframe.

Has anyone tackled iframes in mobile safari before? Any ideas where to start?

4
  • 1
    Without knowing what he's up to, it's rather pointless to start arguing if it's the right thing to do or not. There are use cases where iframes are required.
    – crappish
    Commented Mar 4, 2013 at 9:35
  • If you have access to the iframed page you can do this: stackoverflow.com/questions/19667785/… Commented Oct 30, 2013 at 17:12
  • Here is a recent article from Chris Coyier that I found interesting: blog.codepen.io/2017/12/01/stupid-iframes-stupid-ios
    – Ryan
    Commented Jan 27, 2018 at 18:41
  • Look also here: stackoverflow.com/a/32993873/1480587 this has a nice solution that worked for me.
    – Peter T.
    Commented Feb 5, 2019 at 16:41

10 Answers 10

20

Yeah, you can't constrain the iframe itself with height and width. You should put a div around it. If you control the content in the iframe, you can put some JS within the iframe content that will tell the parent to scroll the div when the touch event is received.

like this:

The JS:

setTimeout(function () {
var startY = 0;
var startX = 0;
var b = document.body;
b.addEventListener('touchstart', function (event) {
    parent.window.scrollTo(0, 1);
    startY = event.targetTouches[0].pageY;
    startX = event.targetTouches[0].pageX;
});
b.addEventListener('touchmove', function (event) {
    event.preventDefault();
    var posy = event.targetTouches[0].pageY;
    var h = parent.document.getElementById("scroller");
    var sty = h.scrollTop;

    var posx = event.targetTouches[0].pageX;
    var stx = h.scrollLeft;
    h.scrollTop = sty - (posy - startY);
    h.scrollLeft = stx - (posx - startX);
    startY = posy;
    startX = posx;
});
}, 1000);

The HTML:

<div id="scroller" style="height: 400px; width: 100%; overflow: auto;">
<iframe height="100%" id="iframe" scrolling="no" width="100%" id="iframe" src="url" />
</div>

If you don't control the iframe content, you can use an overlay over the iframe in a similar manner, but then you can't interact with the iframe contents other than to scroll it - so you can't, for example, click links in the iframe.

It used to be that you could use two fingers to scroll within an iframe, but that doesn't work anymore.

Update: iOS 6 broke this solution for us. I've been attempting to get a new fix for it, but nothing has worked yet. In addition, it is no longer possible to debug javascript on the device since they introduced Remote Web Inspector, which requires a Mac to use.

12
  • Internal only. What problem are you having?
    – Case
    Commented May 26, 2011 at 18:39
  • Ok sure..i have a child iframe called on the main page..this child iframe does not scroll within the main page..you can check out ipad.atwebpages.com/table/try2.html Commented May 27, 2011 at 3:46
  • That link doesn't work. Did you put the JS within the iframe? And then put the "scroller" div around the iframe in the main document?
    – Case
    Commented May 27, 2011 at 17:36
  • Not sure why the link is not working for u..But yes, you are right, in the main page, i have the scroller div containing the iframe element and put the JS code inside iframe page...but still the iframe does not scroll...do u have a working example online so that i can use as ref...Thx a lot again for ur help on this.. Commented May 27, 2011 at 18:23
  • 4
    Your example above worked for me, but the scrolling jittered like crazy because you're using event.pageX and pageY, which includes scrolling info (which you are then changing, which causes shakes). Switching to using event.screenX and screenY produces smooth scrolling for me.
    – Martamius
    Commented May 17, 2012 at 0:40
12

This only works if you control both the outside page and the iframe page.

On the outside page, make the iframe unscrollable.

<iframe src="" height=200 scrolling=no></iframe>

On the iframe page, add this.

<!doctype html>
...
<style>
html, body {height:100%; overflow:hidden}
body {overflow:auto; -webkit-overflow-scrolling:touch}
</style>

This works because modern browsers uses html to determine the height, so we just give that a fixed height and turn the body into a scrollable node.

1
  • 3
    I love you, do u want to marry me? Commented Jul 18, 2018 at 14:39
11

If the iFrame content is not yours then the solution below will not work.

With Android all you need to do is to surround the iframe with a DIV and set the height on the div to document.documentElement.clientHeight. IOS, however, is a different animal. Although I have not yet tried Sharon's solution it does seem like a good solution. I did find a simpler solution but it only works with IOS 5.+.

Surround your iframe element with a DIV (lets call it scroller), set the height of the DIV and make sure that the new DIV has the following styling:

$('#scroller').css({'overflow' : 'auto', '-webkit-overflow-scrolling' : 'touch'});

This alone will work but you will notice that in most implementations the content in the iframe goes blank when scrolling and is basically rendered useless. My understanding is that this behavior has been reported as a bug to Apple as early as iOS 5.0. To get around that problem, find the body element in the iframe and add -webkit-transform', 'translate3d(0, 0, 0) like so:

$('#contentIframe').contents().find('body').css('-webkit-transform', 'translate3d(0, 0, 0)');

If your app or iframe is heavy on memory usage you might get a hitchy scroll for which you might need to use Sharon's solution.

4

I have put @Sharon's code together into the following, which works for me on the iPad with two-finger scrolling. The only thing you should have to change to get it working is the src attribute on the iframe (I used a PDF document).

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

<html xmlns="http://www.w3.org/1999/xhtml">
<head>
    <title>Pdf Scrolling in mobile Safari</title>
</head>
<body>
<div id="scroller" style="height: 400px; width: 100%; overflow: auto;">
<iframe height="100%" id="iframe" scrolling="no" width="100%" id="iframe" src="data/testdocument.pdf" />
</div>
<script type="text/javascript">
    setTimeout(function () {
        var startY = 0;
        var startX = 0;
        var b = document.body;
        b.addEventListener('touchstart', function (event) {
            parent.window.scrollTo(0, 1);
            startY = event.targetTouches[0].pageY;
            startX = event.targetTouches[0].pageX;
        });
        b.addEventListener('touchmove', function (event) {
            event.preventDefault();
            var posy = event.targetTouches[0].pageY;
            var h = parent.document.getElementById("scroller");
            var sty = h.scrollTop;

            var posx = event.targetTouches[0].pageX;
            var stx = h.scrollLeft;
            h.scrollTop = sty - (posy - startY);
            h.scrollLeft = stx - (posx - startX);
            startY = posy;
            startX = posx;
        });
    }, 1000);
    </script>
</body>
</html>
2
  • hello, i read somewhere that 2 finger scrolling was removed some few updates ago, is this solution still working for you?
    – somdow
    Commented Apr 9, 2012 at 18:00
  • Yes I can confirm that it works, I'm using it with the jQuery colorbox plugin. It works with one finger scrolling, although there is no scrollbar. Commented Jul 6, 2012 at 8:28
3
<div id="scroller" style="height: 400px; width: 100%; overflow: auto;">
<iframe height="100%" id="iframe" scrolling="no" width="100%" src="url" />
</div>

I'm building my first site and this helped me get this working for all sites that I use iframe embededding for.

Thanks!

0
1

Sharon's method worked for me, however when a link in the iframe is followed and then the browser back button is pressed, the cached version of the page is loaded and the iframe is no longer scrollable. To overcome this I used some code to refresh the page as follows:

if ('ontouchstart' in document.documentElement)
     {
        document.getElementById('Scrolling').src = document.getElementById('SCrolling').src;
    }
0

I implemented the following and it works well. Basically, I set the body dimensions according to the size of the iFrame content. It does mean that our non-iFrame menu can be scrolled off the screen, but otherwise, this makes our sites functional with iPad and iPhone. "workbox" is the ID of our iFrame.

// Configure for scrolling peculiarities of iPad and iPhone

if (navigator.userAgent.indexOf('iPhone') != -1 || navigator.userAgent.indexOf('iPad') != -1)
{
    document.body.style.width = "100%";
    document.body.style.height = "100%";
    $("#workbox").load(function (){ // Wait until iFrame content is loaded before checking dimensions of the content
        iframeWidth = $("#workbox").contents().width();
        if (iframeWidth > 400)
           document.body.style.width = (iframeWidth + 182) + 'px';

        iframeHeight = $("#workbox").contents().height();
        if (iframeHeight>200)
            document.body.style.height = iframeHeight + 'px';
     });
}
0

Purely using MSchimpf and Ahmad's code, I made adjustments so I could have the iframe within a div, therefore keeping a header and footer for back button and branding on my page. Updated code:

<script type="text/javascript">
    $("#webview").bind('pagebeforeshow', function(event){
        $("#iframe").attr('src',cwebview);
    });

    if (navigator.userAgent.indexOf('iPhone') != -1 || navigator.userAgent.indexOf('iPad') != -1)
    {
        $("#webview-content").css("width","100%");
        $("#webview-content").css("height","100%");
        $("#iframe").load(function (){ // Wait until iFrame content is loaded before checking dimensions of the content
            iframeWidth = $("#iframe").contents().width();
            if (iframeWidth > 400)
               $("#webview-content").css("width",(iframeWidth + 182) + 'px');

            iframeHeight = $("#iframe").contents().height();
            if (iframeHeight>200)
                $("#webview-content").css("height",iframeHeight + 'px');
         });
    }       
</script>  

and the html

<div class="header" data-role="header" data-position="fixed">
</div>

<div id="webview-content" data-role="content" style="height:380px;">
    <iframe id="iframe"></iframe>   
</div><!-- /content -->

<div class="footer" data-role="footer" data-position="fixed">
</div><!-- /footer -->   
0

Don't scroll the IFrame page or its content, scroll the parent page. If you control the IFrame content, you can use the iframe-resizer library to turn the iframe element itself into a proper block level element, with a natural/correct/native height. Also, don't attempt to position (fixed, absolute) your iframe in the parent page, or present an iframe in a modal window, especially if it has form elements.

I also suspect that iOS Safari has a non-standards behavior that expands your iframe's height to its natural height, much like the iframe-resizer library will do for desktop browsers, which seem to render responsive iframe content at height 0px or 150px or some other not useful default. If you need to contrain width, try a max-width style inside the iframe.

-2

The solution is to use Scrolling="no" on the iframe.

That's it.

3
  • 2
    I don't know if this is broken in the latest iOS but for the life of me, I can't get this to work with dynamically created iframes
    – crappish
    Commented Mar 4, 2013 at 9:40
  • 3
    To be more precise - With scrolling="no" and width:100% the iframe still resizes it self. Only when I set width to inherit or specific pixel value, does it stop resizing.
    – crappish
    Commented Mar 4, 2013 at 10:28
  • This doesn't have any effect for me
    – elsurudo
    Commented Jun 4, 2014 at 14:16

Not the answer you're looking for? Browse other questions tagged or ask your own question.