IFRAMEs and the Safari on the iPad, how can the user scroll the content?

HtmlIpadIframeSafariScroll

Html Problem Overview


According to the Apple iOS mantra it should be possible to scroll the contents of an IFRAME by dragging it with two fingers. Unfortunately running the latest version of iOS on the iPad I have yet to find a single website with an IFRAME that scrolls using this method - no scrollbars appear either.

Does anyone know how a user is supposed to scroll the contents of an IFRAME with the mobile Safari?

Html Solutions


Solution 1 - Html

iOS 5 added the following style that can be added to the parent div so that scrolling works.

-webkit-overflow-scrolling:touch

Solution 2 - Html

-webkit-overflow-scrolling:touch as mentioned in the answer is infact the possible solution.

<div style="overflow:scroll !important; -webkit-overflow-scrolling:touch !important;">
     <iframe src="YOUR_PAGE_URL" width="600" height="400"></iframe>
</div>

But if you are unable to scroll up and down inside the iframe as shown in image below, enter image description here

you could try scrolling with 2 fingers diagonally like this,

enter image description here

This actually worked in my case, so just sharing it if you haven't still found a solution for this.

Solution 3 - Html

It doesn't appear that iframes display and scroll properly. You can use an object tag to replace an iframe and the contents will be scrollable with 2 fingers. Here's a simple example:

<html>
    <head>
        <meta name="viewport" content="minimum-scale=1.0; maximum-scale=1.0; user-scalable=false; initial-scale=1.0;"/>
    </head>
    <body>
        <div>HEADER - use 2 fingers to scroll contents:</div>
        <div id="scrollee" style="height:75%;" >
            <object id="object" height="90%" width="100%" type="text/html" data="http://en.wikipedia.org/"></object>
        </div>
        <div>FOOTER</div>
    </body>
</html>

Solution 4 - Html

This is not my answer, but I just copied it from https://gist.github.com/anonymous/2388015 just because the answer is awesome and fixes the problem completely. Credit completely goes to the anonymous author.

<script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.7.1/jquery.min.js"></script>
<script type="text/javascript">
    $(function(){
        if (/iPhone|iPod|iPad/.test(navigator.userAgent))
            $('iframe').wrap(function(){
                var $this = $(this);
                return $('<div />').css({
                    width: $this.attr('width'),
                    height: $this.attr('height'),
                    overflow: 'auto',
                    '-webkit-overflow-scrolling': 'touch'
                });
            });
    })
</script>

Solution 5 - Html

As mentioned in other posts, the combination of css values of overflow: auto; & -webkit-overflow-scrolling: touch;

works when applied to BOTH the iframe in question AND its parent div

With the unfortunate side-effect of double scrollbars on non-touch browsers.

The solution I used was to add these css values via javascript/jquery. Which allowed me to use a base css for all browsers

if (isSafariBrowser()){
    $('#parentDivID').css('overflow', 'auto');
    $('#parentDivID').css('-webkit-overflow-scrolling', 'touch');
    $('#iframeID').css('overflow', 'auto');
    $('#iframeID').css('-webkit-overflow-scrolling', 'touch');
}

where isSafariBrowser() is defined as foll...

var is_chrome = navigator.userAgent.indexOf('Chrome') > -1;
var is_safari = navigator.userAgent.indexOf("Safari") > -1;

function isSafariBrowser(){
    if (is_safari){
        if (is_chrome)	// Chrome seems to have both Chrome and Safari userAgents
            return false;
        else
            return true;
    }
    return false;
}

This allowed my application to work on an iPad Note

  1. Not tested on other ios systems
  2. Not tested this on Android browsers on tablets, may need additional changes

(so this solution may not be complete)

Solution 6 - Html

This is what I did to get iframe scrolling to work on iPad. Note that this solution only works if you control the html that is displayed inside the iframe.

It actually turns off the default iframe scrolling, and instead causes the body tag inside the iframe to scroll.

main.html:

<!DOCTYPE html>
<html>
<head>
<style type="text/css">
#container {
	position: absolute;
	top: 50px;
	left: 50px;
	width: 400px;
	height: 300px;
	overflow: hidden;
}
#iframe {
	width: 400px;
	height: 300px;
}
</style>
</head>
<body>

	<div id="container">
		<iframe src="test.html" id="iframe" scrolling="no"></iframe>
	</div>

</body>
</html>

test.html:

<!DOCTYPE html>
<html>
<head>
<style type="text/css">
html { 
	overflow: auto; 
	-webkit-overflow-scrolling: touch; 
}
body {
	height: 100%;
	overflow: auto; 
	-webkit-overflow-scrolling: touch;
	margin: 0;
	padding: 8px;
}
</style>
</head>
<body></body>
</html>

The same could probably be accomplished using jQuery if you prefer:

$("#iframe").contents().find("body").css({
	"height": "100%",
	"overflow": "auto", 
	"-webkit-overflow-scrolling": "touch"
});

I used this solution to get TinyMCE (wordpress editor) to scroll properly on the iPad.

Solution 7 - Html

The code below works for me (thanks to Christopher Zimmermann for his blog post http://dev.magnolia-cms.com/blog/2012/05/strategies-for-the-iframe-on-the-ipad-problem/). The problems are:

  1. There are no scroll bars to let the user know that they can scroll

  2. Users have to use two-finger scrolling

  3. The PDF files are not centered (still working on it)

     <!DOCTYPE HTML>
     <html>
     <head>
       <title>Testing iFrames on iPad</title>
       <style>
       div {
         border: solid 1px green;
         height:100px;
       }
    
     .scroller{
         border:solid 1px #66AA66;
         height: 400px;
         width: 400px;
         overflow: auto;
         text-align:center;
        
     }
     </style>
    

     <table>
       <tr>
         <td><div class="scroller">
         <iframe width="400" height="400" src="http://www.supremecourt.gov/opinions/11pdf/11-393c3a2.pdf" ></iframe>
     </div>
         </td>
         <td><div class="scroller">
         <iframe width="400" height="400" src="http://www.supremecourt.gov/opinions/11pdf/11-393c3a2.pdf" ></iframe>
     </div>
         </td>
       </tr>
       <tr>
       <td><div class="scroller">
         <iframe width="400" height="400" src="http://www.supremecourt.gov/opinions/11pdf/11-393c3a2.pdf" ></iframe>
     </div>
         </td>
         <td><div class="scroller">
         <iframe width="400" height="400" src="http://www.supremecourt.gov/opinions/11pdf/11-393c3a2.pdf" ></iframe>
     </div>
         </td>
       </tr>
     </table>
     <div> Here are some additional contents.</div>
    

Solution 8 - Html

Based on this article, I have put together the following snippet that provides some very basic functionality:

<div id = "container"></div>
<script>
function setPDFHeight(){ 
        $("#pdfObject")[0].height = $("#pdfObject")[0].offsetHeight;   
}   
$('#container').append('<div align="center" style="width: 100%; height:100%; overflow: auto !important; -webkit-overflow-scrolling: touch !important;">\
      <object id="pdfObject" width="100%" height="1000000000000" align="center" data="content/lessons/12/t.pdf" type="application/pdf" onload="setPDFHeight()">You have no plugin installed</object></div>');  
</script>

Obviously it is far from perfect (given that it practically expands your page height to infinity), but it's the only viable workaround I've found so far.

Solution 9 - Html

None of the solutions so far completely worked for me when I tried (sometimes, only buggy on secondary loads), but as a workaround, using an object element as described here, then wrapping in a scrollable div, then setting the object to a very high height (5000px) did the job for me. It's a big workaround and doesn't work incredibly well (for starters, pages over 5000px would cause issues -- 10000px completely broke it for me though) but it seems to get the job done in some of my test cases:

var style = 'left: ...px; top: ...px; ' +
        'width: ...px; height: ...px; border: ...';

if (isIOs) {
    style += '; overflow: scroll !important; -webkit-overflow-scrolling: touch !important;';
    html = '<div style="' + style + '">' +
           '<object type="text/html" data="http://example.com" ' +
           'style="width: 100%; height: 5000px;"></object>' +
           '</div>';
}
else {
    style += '; overflow: auto;';
    html = '<iframe src="http://example.com" ' +
           'style="' + style + '"></iframe>';
}

Here's hoping Apple will fix the Safari iFrame issues.

Solution 10 - Html

The Problem

I help maintain a big, complicated, messy old site in which everything (literally) is nested in multiple levels of iframes-- many of which are dynamically created and/or have a dynamic src. That creates the following challenges:

  1. Any changes to the HTML structure risk breaking scripts and stylesheets that haven't been touched in years.
  2. Finding and fixing all of the iframes and src documents manually would take way too much time and effort.

Of the solutions posted so far, this is the only one I've seen that overcomes challenge 1. Unfortunately, it doesn't seem to work on some iframes, and when it does, the scrolling is very glitchy (which seems to cause other bugs on the page, such as unresponsive links and form controls).

The Solution

If the above sounds anything like your situation, you may want to give the following script a try. It forgoes native scrolling and instead makes all iframes draggable within the bounds of their viewport. You only need to add it to the document that contains the top level iframes; it will apply the fix as needed to them and their descendants.

Here's a working fiddle*, and here's the code:

(function() {
  var mouse = false //Set mouse=true to enable mouse support
    , iOS = /iPad|iPhone|iPod/.test(navigator.platform);
  if(mouse || iOS) {
    (function() {
      var currentFrame
        , startEvent, moveEvent, endEvent
        , screenY, translateY, minY, maxY
        , matrixPrefix, matrixSuffix
        , matrixRegex = /(.*([\.\d-]+, ?){5,13})([\.\d-]+)(.*)/
        , min = Math.min, max = Math.max
        , topWin = window;
      if(!iOS) {
        startEvent = 'mousedown';
        moveEvent = 'mousemove';
        endEvent = 'mouseup';
      }
      else {
        startEvent = 'touchstart';
        moveEvent = 'touchmove';
        endEvent = 'touchend';
      }
      setInterval(scrollFix, 500);
      function scrollFix() {fixSubframes(topWin.frames);}
      function fixSubframes(wins) {for(var i = wins.length; i; addListeners(wins[--i]));}
      function addListeners(win) {
        try {
          var doc = win.document;
          if(!doc.draggableframe) {
            win.addEventListener('unload', resetFrame);
            doc.draggableframe = true;
            doc.addEventListener(startEvent, touchStart);
            doc.addEventListener(moveEvent, touchMove);
            doc.addEventListener(endEvent, touchEnd);
          }
          fixSubframes(win.frames);
        }
        catch(e) {}
      }
      function resetFrame(e) {
        var doc = e.target
          , win = doc.defaultView
          , iframe = win.frameElement
          , style = getComputedStyle(iframe).transform;
        if(iframe===currentFrame) currentFrame = null;
        win.removeEventListener('unload', resetFrame);
        doc.removeEventListener(startEvent, touchStart);
        doc.removeEventListener(moveEvent, touchMove);
        doc.removeEventListener(endEvent, touchEnd);
        if(style !== 'none') {
          style = style.replace(matrixRegex, '$1|$3|$4').split('|');
          iframe.style.transform = style[0] + 0 + style[2];
        }
        else iframe.style.transform = null;
        iframe.style.WebkitClipPath = null;
        iframe.style.clipPath = null;
        delete doc.draggableiframe;
      }
      function touchStart(e) {
        var iframe, style, offset, coords
          , touch = e.touches ? e.touches[0] : e
          , elem = touch.target
          , tag = elem.tagName;
        currentFrame = null;
        if(tag==='TEXTAREA' || tag==='SELECT' || tag==='HTML') return;
        for(;elem.parentElement; elem = elem.parentElement) {
          if(elem.scrollHeight > elem.clientHeight) {
            style = getComputedStyle(elem).overflowY;
            if(style==='auto' || style==='scroll') return;
          }
        }
        elem = elem.ownerDocument.body;
        iframe = elem.ownerDocument.defaultView.frameElement;
        coords = getComputedViewportY(elem.clientHeight < iframe.clientHeight ? elem : iframe);
        if(coords.elemTop >= coords.top && coords.elemBottom <= coords.bottom) return;
        style = getComputedStyle(iframe).transform;
        if(style !== 'none') {
          style = style.replace(matrixRegex, '$1|$3|$4').split('|');
          matrixPrefix = style[0];
          matrixSuffix = style[2];
          offset = parseFloat(style[1]);
        }
        else {
          matrixPrefix = 'matrix(1, 0, 0, 1, 0, ';
          matrixSuffix = ')';
          offset = 0;
        }
        translateY = offset;
        minY = min(0, offset - (coords.elemBottom - coords.bottom));
        maxY = max(0, offset + (coords.top - coords.elemTop));
        screenY = touch.screenY;
        currentFrame = iframe;
      }
      function touchMove(e) {
        var touch, style;
        if(currentFrame) {
          touch = e.touches ? e.touches[0] : e;
          style = min(maxY, max(minY, translateY + (touch.screenY - screenY)));
          if(style===translateY) return;
          e.preventDefault();
          currentFrame.contentWindow.getSelection().removeAllRanges();
          translateY = style;
          currentFrame.style.transform = matrixPrefix + style + matrixSuffix;
          style = 'inset(' + (-style) + 'px 0px ' + style + 'px 0px)';
          currentFrame.style.WebkitClipPath = style;
          currentFrame.style.clipPath = style;
          screenY = touch.screenY;
        }
      }
      function touchEnd() {currentFrame = null;}
      function getComputedViewportY(elem) {
        var style, offset
          , doc = elem.ownerDocument
          , bod = doc.body
          , elemTop = elem.getBoundingClientRect().top + elem.clientTop
          , elemBottom = elem.clientHeight
          , viewportTop = elemTop
          , viewportBottom = elemBottom + elemTop
          , position = getComputedStyle(elem).position;
        try {
          while(true) {
            if(elem === bod || position === 'fixed') {
              if(doc.defaultView.frameElement) {
                elem = doc.defaultView.frameElement;
                position = getComputedStyle(elem).position;
                offset = elem.getBoundingClientRect().top + elem.clientTop;
                viewportTop += offset;
                viewportBottom = min(viewportBottom + offset, elem.clientHeight + offset);
                elemTop += offset;
                doc = elem.ownerDocument;
                bod = doc.body;
                continue;
              }
              else break;
            }
            else {
              if(position === 'absolute') {
                elem = elem.offsetParent;
                style = getComputedStyle(elem);
                position = style.position;
                if(position === 'static') continue;
              }
              else {
                elem = elem.parentElement;
                style = getComputedStyle(elem);
                position = style.position;
              }
              if(style.overflowY !== 'visible') {
                offset = elem.getBoundingClientRect().top + elem.clientTop;
                viewportTop = max(viewportTop, offset);
                viewportBottom = min(viewportBottom, elem.clientHeight + offset);
              }
            }
          }
        }
        catch(e) {}
        return {
          top: max(viewportTop, 0)
          ,bottom: min(viewportBottom, doc.defaultView.innerHeight)
          ,elemTop: elemTop
          ,elemBottom: elemBottom + elemTop
        };
      }
    })();
  }
})();

* The jsfiddle has mouse support enabled for testing purposes. On a production site, you'd want to set mouse=false.

Solution 11 - Html

After much aggravation, I discovered how to scroll in iframes on my ipad. The secret was to do a vertical finger swipe (single finger was fine) on the LEFT side of the iframe area (and maybe slightly outside of the border). On a laptop or PC, the scroll bar is on the right, so naturally, I spent of lot of time on my ipad experimenting with finger motions on the right side. Only when I tried the left side would the iframe scroll.

Solution 12 - Html

Add overflow: auto; to the style and the two finger scroll should work.

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionKarlthView Question on Stackoverflow
Solution 1 - HtmlKarlthView Answer on Stackoverflow
Solution 2 - HtmlDipin KrishnanView Answer on Stackoverflow
Solution 3 - HtmldeCastongreneView Answer on Stackoverflow
Solution 4 - HtmlForeeverView Answer on Stackoverflow
Solution 5 - HtmlToothless SeerView Answer on Stackoverflow
Solution 6 - HtmlccallendarView Answer on Stackoverflow
Solution 7 - HtmlWhatsInANameView Answer on Stackoverflow
Solution 8 - HtmlperiklisView Answer on Stackoverflow
Solution 9 - HtmlPhilipp LenssenView Answer on Stackoverflow
Solution 10 - HtmlDoctorDestructoView Answer on Stackoverflow
Solution 11 - HtmlccsView Answer on Stackoverflow
Solution 12 - Htmlske57View Answer on Stackoverflow