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?
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:
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.
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.
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)
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:
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>
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>
-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,
you could try scrolling with 2 fingers diagonally like this,
This actually worked in my case, so just sharing it if you haven't still found a solution for this.
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>
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.
Add overflow: auto;
to the style and the two finger scroll should work.
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.
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.
Source: Stackoverflow.com