iframe on iOS (iPad) content cropping issue - javascript

I'm looking for the correct way to assure that dynamically revealed content is visible after scrolling in an iframe on ipad / iOS5.
Oh Iframes and iPad - what a wonderful old chesnut you are. I know that:
iPad expands iframes to the full height of the content within it (almost like it was using HTML5's "seamless" property, but not quite since it doesn't inherit styles or events from the parent). Bizarre, annoying to many, but true.
<iframe height="100%"> is therefore useless since it sizes to its content not to the container
I need to wrap my iframe in a div - a la
<div id="wrapper" style="-webkit-overflow-scrolling:touch;overflow:auto;">
<iframe width="100%" height="100%" src="about"blank"></iframe>
</div>
or else introduce some trickery to set the scroll position of the frame (which I think is based on tricks mentioned in this article)
My issue is that content that is dynamically shown inside the iframe body (e.g. jquery tabs, accordion, etc) may cause the browser to crop the content at the display extent of the page.
E.g. if my "tabs" are most of the way down the visible viewport inside the iframe and I perform a two-finger scroll (or implement the one finger scrollTop hack) then after that content is scrolled into view, some of its content that was previously not drawn remains undrawn. Clicking to a second tab / back again causes the content to appear as if the page doesn't draw off-screen content. After this, if I then scroll back up to the top of the page the content isn't drawn for the start of the page (which was previously visible). Scrolling the page up and down a few times with a two-finger scroll resolves the issue.
I had read this article that stated that the problem was fixed. But it doesn't seem to be fully fixed; and still doesn't get around the issue that because you have to wrap your iframe in a div and put scrollbars on that div, desktop browsers may show a double scrollbar depending on how they interpret overflow:auto.
p.s. I'm using HTML5 boilerplate page both inside and outside the iframe, with the correct meta viewport settings.

I found I was also able to solve the problem by making the document as tall as the iframe content. (As suggested Iframe Content Not Rendering Under Scroll In iOs5 iPad/iPhone) But in my case I didn't want the user to be able to scroll down in the now tall app, because its supposed to be a fullscreen application. I used this code to prevent vertical scrolling:
/*
Prevent Scrolling down.
*/
$(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);
});
}
}
I evaluated a lot of options and wrote this blog post, including test code.
http://dev.magnolia-cms.com/blog/2012/05/strategies-for-the-iframe-on-the-ipad-problem/
Hope this helps,
Topher

I'm assuming there is a bug in iOS safari in how it treats iframes with defined width / height. Without width / height being defined it tries to scale them automatically to fit their content without any scrolling needed.
The best workaround I've found is to not scroll the iframe at all, but rather to scroll a wrapper div inside the framed-in page.
Here's an example:
<iframe id="stupid-iframe" width="600" height="200" src="a-file.html"></iframe>
a-file.html:
<html>
<body>
<div id="wrapper" style="width: 100%; height: 100%; overflow: auto; -webkit-overflow-scrolling: touch;">
...all my normal content...
</div>
</body>
</html>

This is a very tedious problem, especially if you are in a scenario where you must use a dynamically scaling iframe, in my case with the YouTube iframe API. You are not able to control the scroll properties of the iframe. It doesn't even work if you modify the iframe elements in the ios simulator/safari debug window.
The best solution that I found was to use negative positioning to remove the excess whitespace. Android may have mixed results so you have to use browser detection and apply that way.

Related

Prevent iframe from loading responsive design

My app has a functionality that loads another route in a iframe. The intention is to change some layout settings, colors etc and see how that page will look in the browser in its final and original version (100% in a desktop or laptop).
The problem is that the iframe is loaded in a div that has something like 2/3 of the system's width (it's a Bootstrap column). This is smaller than our media-query breakpoint and the iframe content is loading the responsive design. But that breaks the rule in paragraph one.
I needed it to be a miniaturized version of the original page.
Is there a way to achieve this result?
What I am trying to do is somehow similar to this on Google's PageSpeed:
https://developers.google.com/speed/pagespeed/insights/?url=http%3A%2F%2Fstackoverflow.com%2F&tab=desktop
THe difference is that Google takes a picture and in my app the user must be allowed to interact with the page, browse other links, click buttons etc. It is a screen simulator/previewer but not responsive.
As CBroe mentioned, the problem is that the CSS for the page loaded within the iframe is using the size of the iframe as it's viewport size. You'll want to size the iframe according to how you want the actual page to display (1200px wide, for example) then use a scale transform to reduce the size of the iframe.
Your HTML could look like:
<iframe width="1200" height="600" src="https://example.com"></iframe>
Then rescale using CSS:
iframe {
transform: scale(0.3);
transform-origin: top left;
}
Here's a live example: https://codepen.io/JoshuaToenyes/pen/gMMLze

Simulate scrolling on iOS, with Javascript, on an iframe with hidden scrollbars

I have an iframe with scrolling="no" and overflow: hidden;
I need to be able to simulate scrolling with JavaScript alone from either the parent window or within the iframe (it doesn't matter).
Am testing on iOS 8 (iPhone) and I can't seem to be able to move the iframe through a touchmove event handler (or any way for that matter - even tried a setInterval).
For the code that moves the iFrame, I tried both window.scrollBy() and window.scrollTo() from within the iframe. I debugged and had no exceptions. I may be missing something.
Thanks in advance.
You could achieve it by css only. try the following to the selectors where you want that touch move.
webkit-overflow-scrolling: touch; /* lets it scroll lazy */
but you've to use the overflow: auto to get it working. If you wish to keep it within certain width or height, use fix values.

Javascript or CSS fix for 100% <div> that doesn't fill page when IE11 is zoomed

I have a scrolling <div> that is supposed to fill the visible area of the page:
<div id=allcontent style="width:100%;height:100%;overflow:scroll">
(all page content)
</div>
When I zoom in with ctrl+ IE11, there is extra space below and to the right of my div, where I see the <body> background color.
I would like to resize the "allcontent" div to fill the page when IE zooms in, but I can't find a way to get the actual page size using javascript.
Is there either:
a way to get the real page size from IE11 after clicking ctrl+, or
a way to specify the <div> dimensions in CSS that will cause it to resize correctly with the page?
I have tried all sorts of measurements like ocument.body.clientWidth but not found anything that applies to the newly visible area outside of my <div>.
Even weirder: the page slowly scrolls to cover up the (wrongly) exposed body color.
The problem was the "auto zoom to viewport width" feature of IE11. The fix was to add the following to my CSS:
#media screen {
#-ms-viewport {
width: device-width;
}
}
Microsoft: Internet Explorer... automatically scales content when the window is narrower than 1024 pixels... in cases where this automatic scaling is not needed or desired, the device-width keyword... signifies that the page is optimized to work well regardless of the width of the device.
I found the answer in this question.

Detecting doubletap on app top in mobile Safari via JavaScript

iOS has a convention that double-tapping on the top bar (i.e., where the current time is displayed) scrolls the app to the top state. For example, double-tapping the top bar in Safari brings you to the top of the current web page, and double-tapping the top in Facebook/Twitter brings you to the top of the feed. It's a very useful navigation shortcut.
Let's call it a TopTap for purposes of this question.
I'm wondering how I can detect TopTaps in a JavaScript app in mobile Safari -- that is, NOT in an iOS app, but in a web page that happens to be viewed in mobile Safari.
In my particular case, I can't rely on the built-in mobile Safari TopTap behavior because my document consists of a single <canvas> element that implements its own scrollable interface. I want to be able to detect a TopTap so that I can scroll that <canvas> to its top state.
I've experimented with adding an onscroll event handler, but there's no distinguishing information in that event that would let me isolate the TopTaps. Also, I can't use touch events (touchstart, etc.), because a TopTap happens in the browser/OS chrome, outside the scope of the web page.
Any ideas?
As it turns out double tapping the native status bar will trigger a scroll event on document.body which you can in fact listen for. Trick is, as you mentioned, how can you determine if it's from a double tap or not?
In order to detect it, the body has to be scrolled down to begin with. And setting the scroll position causes a scroll event.
While it's a bunch of hacks, I've been able to get this working:
Code: https://github.com/HenrikJoreteg/holy-grail-ios-layout
Demo: http://ios-layout.surge.sh
Basically, you don't really use the <body> as a container. You set it as position: absolute and full height/width. Then you have some kind of container element that you set to position: fixed that you use as your actual container for your content.
Doing this lets you programmatically scroll the body without affecting anything visual on the page at all.
Now you're set up to listen for scroll events on the body and ideally the user can't actually cause a scroll on the body except via double tapping the status bar.
Unfortunately you have to do all sorts of silly things to make this work.
Put something taller than 100% in the body so the body can actually has something to scroll.
Programmatically set the scroll position to 1 to start and after each status bar double tap.
Set a debounced scroll handler on the body that only fires if it knows the event wasn't caused by setting the position to 1.
As it turns out, iOS also likes to break thing when you rotate the phone, etc.
Use the following CSS to make the contents of your actual content containers scrollable with momentum and rubber-banding:
overflow-y: scroll; /* has to be scroll, not auto */
-webkit-overflow-scrolling: touch;
Anyway, it's something :)
More info in the github repo, but anyway, this really does seem to work quite well for iOS 8 (haven't tested other versions of iOS).
If your target is recent iOS, you could put an element over your canvas that is position: fixed to the top of the viewport and use that to detect double-taps.
EDIT: I was thinking something like the below, but as Adrian points out he needs it to happen when the native browser chrome is double-tapped as well.
<!doctype html>
<html>
<head>
<style type="text/css">
canvas {
height: 1000px;
width: 320px;
}
#top-tap {
height: 16px;
left: 0;
position: fixed;
right: 0;
top: 0;
}
</style>
</head>
<body>
<canvas></canvas>
<a id="top-tap"></a>
<script type="text/javascript">
function secondTap() {
window.scrollTo(0,0)
}
document.querySelector('#top-tap').addEventListener('touchend', function (e) {
var self = this
this.addEventListener('touchend', secondTap)
setTimeout(function () {
self.removeEventListner('touchend', secondTap)
}, 100)
})
</script>
</body>
</html>
Is it possible to create a hidden native html element, the same height as the content in your canvas. Then map the scroll position on the native element to the same position within the canvas. Could also hide the canvas scroll widget.
So as far as the users concerned they only see the native scroll bar... but all the scroll events map to the canvas - including the status bar tap.
May not work if you have other HTML content on the page but might if there's only the canvas visible.
Edit: Here's a very crude prototype http://jsbin.com/cisizopi/3
I'm simulating a canvas and it's contents with divs
After doing a little research, I came across this post: http://prud.github.io/ios-overflow-scroll-to-top/. I'm not sure if this will do what you are looking for. Also, I'm fairly certain that you cannot intercept the status bar touch with JS in the browser for IOS.

How to prevent "jerking" of page in browser

What is this "Jerking" - On this page scroll right to the top, then if you're using a mac use two fingers and scroll up more... the page will scroll further up (page moves downwards) revealing a gray area and bounce back up after you lift your fingers off the touchpad.
Question is how do i prevent this "jerking" effect with CSS or JavaScript?
I know it's possible because if you go to facebook or pinterest and click on an image you get a lightbox. In that lightbox "mode" you won't get this "jerking" effect.
I initially thought it's something to do with lightboxes or css fixed positioning but even in http://lokeshdhakar.com/projects/lightbox2/ "mode" it still "jerks". How did fb and pinterest do it?
ps. use chrome or safari. firefox doesn't have this effect.
Facebook seems to accomplish this by setting the CSS attribute "overflow: hidden" on the document body when a picture is displayed. I just tested it myself by adding the following CSS to an HTML file and the bounceback scrolling was disabled.
<style type="text/css">
body {
overflow: hidden;
}
</style>
Of course, this will prevent your page from scrolling at all. I agree with Quentin, this is standard UI behavior and you shouldn't change it unless you have a good reason.

Categories

Resources