Zoom issue in iPhone for iOS 10 - iphone

Is there any viewport meta tag available for iOS 10 ?
I am facing zoom issue on my iPhone. I am using <meta name="viewport" content="user-scalable=1.0,initial-scale=1.0,minimum-scale=1.0,maximum-scale=1.0"> and also user-scalable=no is not working.

It seems this meta is not taken into account anymore with iOS 10 RC.
Users are able to zoom in/out freely even when this meta.
I'm looking for a clean solution for that.
See disable viewport zooming iOS 10 safari?

fyi, It still works for home screen app

I have a fairly heavy GIS web-app that crashes when iOS devices with 1 Gigabyte of RAM try to zoom. After much experimentation, this is what works for me. Hope it helps. If anyone has any suggestions to improve this, then by all means enlighten us all! :)
// CSS (This prevents zoom on input)
input {
font-size: 16px!important;
}
// JavaScript (I use jQuery). This prevents pinch zoom.
var numTouches = 0;
$('body').on('touchmove', function(event){
numTouches = event.originalEvent.touches.length;
if(numTouches > 1){
event.preventDefault();
}
});
// And this prevents double tap zoom
var mylatesttap = new Date().getTime();
$('body').on('touchstart', function(event){
var now = new Date().getTime();
var timesince = now - mylatesttap;
if((timesince < 500) && (timesince > 0)){
// double tap
event.preventDefault();
event.stopPropagation();
event.stopImmediatePropagation();
//alert('You tapped me Twice !!!');
}else{
// too much time to be a doubletap
}
mylatesttap = new Date().getTime();
});
This code was built upon samples from this post:
Detect double tap on ipad or iphone screen using javascript

Related

ionic1 app : keyboard scroll issue - the cursor displays in the wrong place

When I focus input box, the cursor displayed in the wrong place.
It's built using Ionic 1.
Why?
Thank you
I used this at first
<ion-content class="side-menu">
But someone reported that they couldn't see what they are typing in inputbox.
Inputbox is hidden under the keyboard and the cursor displays in the wrong place.
Actually it worked will on Samsung Galaxy S3.
But to fix that issue(not on Samsung Galaxy S3, even it worked well on my iPhone), I added this.
<ion-content class="side-menu" delegate-handle="mainScroll" overflow-scroll="false">
After add above code, there was issue on Samsung Galaxy too.
So I removed this code again.
delegate-handle="mainScroll" overflow-scroll="false"
Could you please let me know how we can make sure it works on the other iPhone device too?
maybe iPhone 8
<input type="text" id="places" ng-focus="scrollTo('places')" />
$scope.scrollTo = function (elementId) {
$timeout(function() {
var elem = document.getElementById(elementId);
var yOffset = elem.offsetTop;
var elementHeight = elem.offsetHeight;
var scrollPoint = yOffset + elementHeight - 100;
console.log(yOffset, scrollPoint);
$ionicScrollDelegate.$getByHandle('mainScroll').scrollTo(0, scrollPoint, true);
//$ionicScrollDelegate.scrollTo(0, scrollPoint, true);
var quotePosition = $ionicPosition.position(angular.element(elem));
console.log(quotePosition);
//$ionicScrollDelegate.$getByHandle('mainScroll').scrollTo(0, quotePosition, true);
}, 200);
};
scrollTo is not working well.

inline html5 video on iphone

I want to play an HTML5 video on the iPhone but whenever I try to, the iPhone automatically pops out in fullscreen when the video '.play()' is called. How do I play the video inline without the iPhone changing the UI of it like these:
http://www.easy-bits.com/iphone-inline-video-autostart
http://www.takeyourdose.com/en (When you click "Start the 360 experience")
Edit: Here's my code:
<!DOCTYPE html>
<html lang="en">
<head>
<title>iPhone Test</title>
<meta charset="utf-8">
</head>
<body>
<button onclick="document.getElementById('vid').play()">Start</button>
<video id="vid">
<source src="/videos/tutorial.mp4" type="video/mp4">
Your browser does not support the video tag.
</video>
</body>
</html>
I'm working on a solution to this until Apple allows the "webkit-playsinline" to actually play inline.
I started a library here: https://github.com/newshorts/InlineVideo
It's very rough, but the basic gist is that you "seek" through the video instead of playing it outright. So instead of calling:
video.play()
You instead set a loop using request animation frame or setInterval, then set the:
video.currentTime = __FRAME_RATE__
So the whole thing might look like in your html:
<video controls width="300">
<source src="http://www.w3schools.com/html/mov_bbb.mp4">
</video>
<canvas></canvas>
<button>Play</button>
and your js (make sure to include jquery)
var video = $('video')[0];
var canvas = $('canvas')[0];
var ctx = canvas.getContext('2d');
var lastTime = Date.now();
var animationFrame;
var framesPerSecond = 25;
function loop() {
var time = Date.now();
var elapsed = (time - lastTime) / 1000;
// render
if(elapsed >= ((1000/framesPerSecond)/1000)) {
video.currentTime = video.currentTime + elapsed;
$(canvas).width(video.videoWidth);
$(canvas).height(video.videoHeight);
ctx.drawImage(video, 0, 0, video.videoWidth, video.videoHeight);
lastTime = time;
}
// if we are at the end of the video stop
var currentTime = (Math.round(parseFloat(video.currentTime)*10000)/10000);
var duration = (Math.round(parseFloat(video.duration)*10000)/10000);
if(currentTime >= duration) {
console.log('currentTime: ' + currentTime + ' duration: ' + video.duration);
return;
}
animationFrame = requestAnimationFrame(loop);
}
$('button').on('click', function() {
video.load();
loop();
});
http://codepen.io/newshorts/pen/yNxNKR
The real driver for Apple changing this will be the recent release of webGL for ios devices enabled by default. Basically there are going to be a whole bunch of people looking to use video textures. technically right now, that can't be done.
On IOS10 / Safari 10 you can now add the playsinline property to the HTML5 Video element, and it will just play inline.
If you create an audio element and a video element, you can play the audio via user interaction and then seek the video, rendering it to a canvas. This is something quick that I came up with (tested on iPhone iOS 9)
var canvas = document.getElementById("canvas");
var ctx = canvas.getContext('2d');
var audio = document.createElement('audio');
var video = document.createElement('video');
function onFrame(){
ctx.drawImage(video,0,0,426,240);
video.currentTime = audio.currentTime;
requestAnimationFrame(onFrame);
}
function playVideo(){
var i = 0;
function ready(){
i++;
if(i == 2){
audio.play();
onFrame();
}
}
video.addEventListener('canplaythrough',ready);
audio.addEventListener('canplaythrough',ready);
audio.src = video.src = "http://www.sample-videos.com/video/mp4/720/big_buck_bunny_720p_10mb.mp4";
audio.load();
video.load();
}
CodePen
Test Page
Apologies for writing this as an answer instead of a comment on the main thread, but I apparently do not have enough reputation points to comment!
Anyways, I am also looking to do exactly the same thing as the OP.
I noticed that there is a particular library, krpano, coupled with the krpano videoplayer plugin that allows for video to be played on iPhone INLINE! Some demos of this in action can be found here: http://krpano.com/video/
While I would prefer a simple 2D video example over these crazy panorama videos, this is the closest I have found while scouring the web. From what I can tell, they use a normal element not attached to the document:
var v = document.querySelector('video');
// remove from document
v.parentNode.removeChild(v);
// touch anywhere to play
document.ontouchstart = function () {
v.play();
}
Video element before it's removed:
<video playsinline webkit-playsinline preload="auto" crossorigin="anonymous" src="http://www.mediactiv.com/video/Milano.mp4" loop style="transform: translateZ(0px);"></video>
But that alone doesn't seem to be enough: when the video is played, it still goes fullscreen.
How do they manage to prevent the video from going fullscreen?
EDIT: After looking at both examples it looked like they both were leveraging the canvas element to render the video, so I went ahead and whipped up a demo showing off video rendering thru the canvas element. While the demo works great, it fails to deliver on iPhone (even tho the video element is completely removed from the DOM!) -- the video still jumps to full screen. I'm thinking the next step would be to apply these same principles to a WebGL canvas (that's what the krpano examples are doing), but in the meantime maybe this demo will spark some ideas in others...
http://jakesiemer.com/projects/video/index.htm

What is the "iPhone viewport scale bug"?

HTML5 mobile boilerplate recommends adding this snippet of Javascript to fix the "iPhone viewport scale bug":
// Fix for iPhone viewport scale bug
// http://www.blog.highub.com/mobile-2/a-fix-for-iphone-viewport-scale-bug/
MBP.viewportmeta = document.querySelector && document.querySelector('meta[name="viewport"]');
MBP.ua = navigator.userAgent;
MBP.scaleFix = function () {
if (MBP.viewportmeta && /iPhone|iPad|iPod/.test(MBP.ua) && !/Opera Mini/.test(MBP.ua)) {
MBP.viewportmeta.content = "width=device-width, minimum-scale=1.0, maximum-scale=1.0";
document.addEventListener("gesturestart", MBP.gestureStart, false);
}
};
MBP.gestureStart = function () {
MBP.viewportmeta.content = "width=device-width, minimum-scale=0.25, maximum-scale=1.6";
};
What is the "iPhone viewport scale bug"? (The linked blog post doesn't make sense to me)
How does this code go about fixing the bug? (I.e., what exactly does it do?)
View this demo page with an iPhone. Rotate the phone from portrait to landscape. Notice the page being scaled up on landscape view.
See here to know in Detail http://webdesignerwall.com/tutorials/iphone-safari-viewport-scaling-bug
check comments too.
This screenshot should help too:
(source: webdesignerwall.com)

iOS 5 fixed positioning and virtual keyboard

I have a mobile website which has a div pinned to the bottom of the screen via position:fixed. All works fine in iOS 5 (I'm testing on an iPod Touch) until I'm on a page with a form. When I tap into an input field and the virtual keyboard appears, suddenly the fixed position of my div is lost. The div now scrolls with the page as long as the keyboard is visible. Once I click Done to close the keyboard, the div reverts to its position at the bottom of the screen and obeys the position:fixed rule.
Has anyone else experienced this sort of behavior? Is this expected? Thanks.
I had this problem in my application. Here's how I'm working around it:
input.on('focus', function(){
header.css({position:'absolute'});
});
input.on('blur', function(){
header.css({position:'fixed'});
});
I'm just scrolling to the top and positioning it there, so the iOS user doesn't notice anything odd going on. Wrap this in some user agent detection so other users don't get this behavior.
I had a slightly different ipad issue where the virtual keyboard pushed my viewport up offscreen. Then after the user closed the virtual keyboard my viewport was still offscreen. In my case I did something like the following:
var el = document.getElementById('someInputElement');
function blurInput() {
window.scrollTo(0, 0);
}
el.addEventListener('blur', blurInput, false);
This is the code we use to fix problem with ipad. It basically detect discrepancies between offset and scroll position - which means 'fixed' isn't working correctly.
$(window).bind('scroll', function () {
var $nav = $(".navbar")
var scrollTop = $(window).scrollTop();
var offsetTop = $nav.offset().top;
if (Math.abs(scrollTop - offsetTop) > 1) {
$nav.css('position', 'absolute');
setTimeout(function(){
$nav.css('position', 'fixed');
}, 1);
}
});
The position fixed elements simply don't update their position when the keyboard is up. I found that by tricking Safari into thinking that the page has resized, though, the elements will re-position themselves. It's not perfect, but at least you don't have to worry about switching to 'position: absolute' and tracking changes yourself.
The following code just listens for when the user is likely to be using the keyboard (due to an input being focused), and until it hears a blur it just listens for any scroll events and then does the resize trick. Seems to be working pretty well for me thus far.
var needsScrollUpdate = false;
$(document).scroll(function(){
if(needsScrollUpdate) {
setTimeout(function() {
$("body").css("height", "+=1").css("height", "-=1");
}, 0);
}
});
$("input, textarea").live("focus", function(e) {
needsScrollUpdate = true;
});
$("input, textarea").live("blur", function(e) {
needsScrollUpdate = false;
});
Just in case somebody happens upon this thread as I did while researching this issue. I found this thread helpful in stimulating my thinking on this issue.
This was my solution for this on a recent project. You just need to change the value of "targetElem" to a jQuery selector that represents your header.
if(navigator.userAgent.match(/iPad/i) != null){
var iOSKeyboardFix = {
targetElem: $('#fooSelector'),
init: (function(){
$("input, textarea").on("focus", function() {
iOSKeyboardFix.bind();
});
})(),
bind: function(){
$(document).on('scroll', iOSKeyboardFix.react);
iOSKeyboardFix.react();
},
react: function(){
var offsetX = iOSKeyboardFix.targetElem.offset().top;
var scrollX = $(window).scrollTop();
var changeX = offsetX - scrollX;
iOSKeyboardFix.targetElem.css({'position': 'fixed', 'top' : '-'+changeX+'px'});
$('input, textarea').on('blur', iOSKeyboardFix.undo);
$(document).on('touchstart', iOSKeyboardFix.undo);
},
undo: function(){
iOSKeyboardFix.targetElem.removeAttr('style');
document.activeElement.blur();
$(document).off('scroll',iOSKeyboardFix.react);
$(document).off('touchstart', iOSKeyboardFix.undo);
$('input, textarea').off('blur', iOSKeyboardFix.undo);
}
};
};
There is a little bit of a delay in the fix taking hold because iOS stops DOM manipulation while it is scrolling, but it does the trick...
None of the other answers I've found for this bug have worked for me. I was able to fix it simply by scrolling the page back up by 34px, the amount mobile safari scrolls it down. with jquery:
$('.search-form').on('focusin', function(){
$(window).scrollTop($(window).scrollTop() + 34);
});
This obviously will take effect in all browsers, but it prevents it breaking in iOS.
This issue is really annoying.
I combined some of the above mentioned techniques and came up with this:
$(document).on('focus', 'input, textarea', function() {
$('.YOUR-FIXED-DIV').css('position', 'static');
});
$(document).on('blur', 'input, textarea', function() {
setTimeout(function() {
$('.YOUR-FIXED-DIV').css('position', 'fixed');
$('body').css('height', '+=1').css('height', '-=1');
}, 100);
});
I have two fixed navbars (header and footer, using twitter bootstrap).
Both acted weird when the keyboard is up and weird again after keyboard is down.
With this timed/delayed fix it works. I still find a glitch once in a while, but it seems to be good enough for showing it to the client.
Let me know if this works for you. If not we might can find something else. Thanks.
I was experiencing same issue with iOS7. Bottom fixed elements would mess up my view not focus properly.
All started working when I added this meta tag to my html.
<meta name="viewport" content="width=device-width,initial-scale=1.0,maximum-scale=1.0,user-scalable=no,height=device-height" >
The part which made the difference was:
height=device-height
Hope that helps someone.
I've taken Jory Cunningham answer and improved it:
In many cases, it's not just one element who goes crazy, but several fixed positioned elements, so in this case, targetElem should be a jQuery object which has all the fixed elements you wish to "fix". Ho, this seems to make the iOS keyboard go away if you scroll...
Needless to mention you should use this AFTER document DOM ready event or just before the closing </body> tag.
(function(){
var targetElem = $('.fixedElement'), // or more than one
$doc = $(document),
offsetY, scrollY, changeY;
if( !targetElem.length || !navigator.userAgent.match(/iPhone|iPad|iPod/i) )
return;
$doc.on('focus.iOSKeyboardFix', 'input, textarea, [contenteditable]', bind);
function bind(){
$(window).on('scroll.iOSKeyboardFix', react);
react();
}
function react(){
offsetY = targetElem.offset().top;
scrollY = $(window).scrollTop();
changeY = offsetY - scrollY;
targetElem.css({'top':'-'+ changeY +'px'});
// Instead of the above, I personally just do:
// targetElem.css('opacity', 0);
$doc.on('blur.iOSKeyboardFix', 'input, textarea, [contenteditable]', unbind)
.on('touchend.iOSKeyboardFix', unbind);
}
function unbind(){
targetElem.removeAttr('style');
document.activeElement.blur();
$(window).off('scroll.iOSKeyboardFix');
$doc.off('touchend.iOSKeyboardFix blur.iOSKeyboardFix');
}
})();
I have a solution similar to #NealJMD except mine only executes for iOS and correctly determines the scroll offset by measuring the scollTop before and after the native keyboard scrolling as well as using setTimeout to allow the native scrolling to occur:
var $window = $(window);
var initialScroll = $window.scrollTop();
if (navigator.userAgent.match(/iPhone|iPad|iPod/i)) {
setTimeout(function () {
$window.scrollTop($window.scrollTop() + (initialScroll - $window.scrollTop()));
}, 0);
}
I have fixed my Ipad main layout content fixed position this way:
var mainHeight;
var main = $('.main');
// hack to detects the virtual keyboard close action and fix the layout bug of fixed elements not being re-flowed
function mainHeightChanged() {
$('body').scrollTop(0);
}
window.setInterval(function () {
if (mainHeight !== main.height())mainHeightChanged();
mainHeight = main.height();
}, 100);
I had a similar problem to #ds111 s. My website was pushed up by the keyboard but didn't move down when the keyboard closed.
First I tried #ds111 solution but I had two input fields. Of course, first the keyboard goes away, then the blur happens (or something like that). So the second input was under the keyboard, when the focus switched directly from one input to the other.
Furthermore, the "jump up" wasn't good enough for me as the whole page only has the size of the ipad. So I made the scroll smooth.
Finally, I had to attach the event listener to all inputs, even those, that were currently hidden, hence the live.
All together I can explain the following javascript snippet as:
Attach the following blur event listener to the current and all future input and textarea (=live): Wait a grace period (= window.setTimeout(..., 10)) and smoothly scroll to top (= animate({scrollTop: 0}, ...)) but only if "no keyboard is shown" (= if($('input:focus, textarea:focus').length == 0)).
$('input, textarea').live('blur', function(event) {
window.setTimeout(function() {
if($('input:focus, textarea:focus').length == 0) {
$("html, body").animate({ scrollTop: 0 }, 400);
}
}, 10)
})
Be aware, that the grace period (= 10) may be too short or the keyboard may still be shown although no input or textarea is focused. Of course, if you want the scrolling faster or slower, you may adjust the duration (= 400)
really worked hard to find this workaround, which in short looks for focus and blur events on inputs, and scrolling to selectively change the positioning of the fixed bar when the events happen. This is bulletproof, and covers all cases (navigating with <>, scroll, done button). Note id="nav" is my fixed footer div. You can easily port this to standard js, or jquery. This is dojo for those who use power tools ;-)
define([
"dojo/ready",
"dojo/query",
], function(ready, query){
ready(function(){
/* This addresses the dreaded "fixed footer floating when focusing inputs and keybard is shown" on iphone
*
*/
if(navigator.userAgent.match(/iPhone/i)){
var allInputs = query('input,textarea,select');
var d = document, navEl = "nav";
allInputs.on('focus', function(el){
d.getElementById(navEl).style.position = "static";
});
var fixFooter = function(){
if(d.activeElement.tagName == "BODY"){
d.getElementById(navEl).style.position = "fixed";
}
};
allInputs.on('blur', fixFooter);
var b = d.body;
b.addEventListener("touchend", fixFooter );
}
});
}); //end define
This is a difficult problem to get 'right'. You can try and hide the footer on input element focus, and show on blur, but that isn't always reliable on iOS. Every so often (one time in ten, say, on my iPhone 4S) the focus event seems to fail to fire (or maybe there is a race condition), and the footer does not get hidden.
After much trial and error, I came up with this interesting solution:
<head>
...various JS and CSS imports...
<script type="text/javascript">
document.write( '<style>#footer{visibility:hidden}#media(min-height:' + ($( window ).height() - 10) + 'px){#footer{visibility:visible}}</style>' );
</script>
</head>
Essentially: use JavaScript to determine the window height of the device, then dynamically create a CSS media query to hide the footer when the height of the window shrinks by 10 pixels. Because opening the keyboard resizes the browser display, this never fails on iOS. Because it's using the CSS engine rather than JavaScript, it's much faster and smoother too!
Note: I found using 'visibility:hidden' less glitchy than 'display:none' or 'position:static', but your mileage may vary.
Works for me
if (navigator.userAgent.match(/iPhone|iPad|iPod/i)) {
$(document).on('focus', 'input, textarea', function() {
$('header').css({'position':'static'});
});
$(document).on('blur', 'input, textarea', function() {
$('header').css({'position':'fixed'});
});
}
In our case this would fix itself as soon as user scrolls. So this is the fix we've been using to simulate a scroll on blur on any input or textarea:
$(document).on('blur', 'input, textarea', function () {
setTimeout(function () {
window.scrollTo(document.body.scrollLeft, document.body.scrollTop);
}, 0);
});
My answer is that it can't be done.
I see 25 answers but none work in my case. That's why Yahoo and other pages hide the fixed header when the keyboard is on. And Bing turns the whole page non-scrollable (overflow-y: hidden).
The cases discussed above are different, some have issues when scrolling, some on focus or blur. Some have fixed footer, or header. I can't test now each combination, but you might end up realizing that it can't be done in your case.
Found this solution on Github.
https://github.com/Simbul/baker/issues/504#issuecomment-12821392
Make sure you have scrollable content.
// put in your .js file
$(window).load(function(){
window.scrollTo(0, 1);
});
// min-height set for scrollable content
<div id="wrap" style="min-height: 480px">
// website goes here
</div>
The address bar folds up as an added bonus.
In case anyone wanted to try this. I got the following working for me on a fixed footer with an inputfield in it.
<script>
$('document').ready(
function() {
if (navigator.userAgent.match(/Android/i) || navigator.userAgent.match(/webOS/i) || navigator.userAgent.match(/iPhone/i) || navigator.userAgent.match(/iPad/i)
|| navigator.userAgent.match(/iPod/i) || navigator.userAgent.match(/BlackBerry/i) || navigator.userAgent.match(/Windows Phone/i)) {
var windowHeight = $(window).height();
var documentHeight = $(document).height();
$('#notes').live('focus', function() {
if (documentHeight > windowHeight) {
$('#controlsContainer').css({
position : 'absolute'
});
$("html, body").animate({
scrollTop : $(document).height()
}, 1);
}
});
$('#notes').live('blur', function() {
$('#controlsContainer').css({
position : 'fixed'
});
$("html, body").animate({
scrollTop : 0
}, 1);
});
}
});
</script>
I have the same issue. But I realized that the fixed position is just delayed and not broken (at least for me). Wait 5-10 seconds and see if the div adjusts back to the bottom of the screen. I believe it's not an error but a delayed response when the keyboard is open.
I tried all the approaches from this thread, but if they didn't help, they did even worse.
In the end, I decided force device to loose focus:
$(<selector to your input field>).focus(function(){
var $this = $(this);
if (<user agent target check>) {
function removeFocus () {
$(<selector to some different interactive element>).focus();
$(window).off('resize', removeFocus);
}
$(window).on('resize', removeFocus);
}
});
and it worked like a charm and fixed my sticky login-form.
Please NOTE:
The JS code above is only to present my idea, to execute this snippet please replace values in angular braces (<>) with appropriate values for your situation.
This code is designed to work with jQuery v1.10.2
This is still a large bug for for any HTML pages with taller Bootstrap Modals in iOS 8.3. None of the proposed solutions above worked and after zooming in on any field below the fold of a tall modal, Mobile Safari and/or WkWebView would move the fixed elements to where the HTML body's scroll was situated, leaving them misaligned with where they actually where laid out.
To workaround the bug, add an event listener to any of your modal inputs like:
$(select.modal).blur(function(){
$('body').scrollTop(0);
});
I'm guessing this works because forcing the HTML body's scroll height re-aligns the actual view with where the iOS 8 WebView expects the fixed modal div's contents to be.
If anybody was looking for a completely different route (like you are not even looking to pin this "footer" div as you scroll but you just want the div to stay at the bottom of the page), you can just set the footer position as relative.
That means that even if the virtual keyboard comes up on your mobile browser, your footer will just stay anchored to the bottom of the page, not trying to react to virtual keyboard show or close.
Obviously it looks better on Safari if position is fixed and the footer follows the page as you scroll up or down but due to this weird bug on Chrome, we ended up switching over to just making the footer relative.
None of the scrolling solutions seemed to work for me. Instead, what worked is to set the position of the body to fixed while the user is editing text and then restore it to static when the user is done. This keeps safari from scrolling your content on you. You can do this either on focus/blur of the element(s) (shown below for a single element but could be for all input, textareas), or if a user is doing something to begin editing like opening a modal, you can do it on that action (e.g. modal open/close).
$("#myInput").on("focus", function () {
$("body").css("position", "fixed");
});
$("#myInput").on("blur", function () {
$("body").css("position", "static");
});
iOS9 - same problem.
TLDR - source of the problem. For solution, scroll to bottom
I had a form in a position:fixed iframe with id='subscribe-popup-frame'
As per the original question, on input focus the iframe would go to the top of the document as opposed to the top of the screen.
The same problem did not occur in safari dev mode with user agent set to an idevice. So it seems the problem is caused by iOS virtual keyboard when it pops up.
I got some visibility into what was happening by console logging the iframe's position (e.g. $('#subscribe-popup-frame', window.parent.document).position() ) and from there I could see iOS seemed to be setting the position of the element to {top: -x, left: 0} when the virtual keyboard popped up (i.e. focussed on the input element).
So my solution was to take that pesky -x, reverse the sign and then use jQuery to add that top position back to the iframe. If there is a better solution I would love to hear it but after trying a dozen different approaches it was the only one that worked for me.
Drawback: I needed to set a timeout of 500ms (maybe less would work but I wanted to be safe) to make sure I captured the final x value after iOS had done its mischief with the position of the element. As a result, the experience is very jerky . . . but at least it works
Solution
var mobileInputReposition = function(){
//if statement is optional, I wanted to restrict this script to mobile devices where the problem arose
if(screen.width < 769){
setTimeout(function(){
var parentFrame = $('#subscribe-popup-frame',window.parent.document);
var parentFramePosFull = parentFrame.position();
var parentFramePosFlip = parentFramePosFull['top'] * -1;
parentFrame.css({'position' : 'fixed', 'top' : parentFramePosFlip + 'px'});
},500);
}
}
Then just call mobileInputReposition in something like $('your-input-field).focus(function(){}) and $('your-input-field).blur(function(){})

How to set viewport meta for iPhone that handles rotation properly?

So I've been using:
<meta name="viewport" content="width=device-width; initial-scale=1.0; maximum-scale=1.0;"/>
to get my HTML content to display nicely on the iPhone. It works great until the user
rotates the device into landscape mode, where the display remains constrained to 320px.
Is there a simple way to specify a viewport that changes in response to the user changing the
device orientation? Or must I resort to Javascript to handle that?
Was just trying to work this out myself, and the solution I came up with was:
<meta name="viewport" content="initial-scale = 1.0,maximum-scale = 1.0" />
This seems to lock the device into 1.0 scale regardless of it's orientation. As a side effect, it does however completely disable user scaling (pinch zooming, etc).
For anybody still interested:
http://wiki.phonegap.com/w/page/16494815/Preventing-Scrolling-on-iPhone-Phonegap-Applications
From the page:
<meta name="viewport" content="user-scalable=no,width=device-width" />
This instructs Safari to prevent
the user from zooming into the page
with the "pinch" gesture and fixes the
width of the view port to the width of
the screen, which ever orientation the
iPhone is in.
You don't want to lose the user scaling option if you can help it. I like this JS solution from here.
<script type="text/javascript">
(function(doc) {
var addEvent = 'addEventListener',
type = 'gesturestart',
qsa = 'querySelectorAll',
scales = [1, 1],
meta = qsa in doc ? doc[qsa]('meta[name=viewport]') : [];
function fix() {
meta.content = 'width=device-width,minimum-scale=' + scales[0] + ',maximum-scale=' + scales[1];
doc.removeEventListener(type, fix, true);
}
if ((meta = meta[meta.length - 1]) && addEvent in doc) {
fix();
scales = [.25, 1.6];
doc[addEvent](type, fix, true);
}
}(document));
</script>
You're setting it to not be able to scale (maximum-scale = initial-scale), so it can't scale up when you rotate to landscape mode. Set maximum-scale=1.6 and it will scale properly to fit landscape mode.
I have come up with a slighly different approach that should work on cross platforms
http://www.jqui.net/tips-tricks/fixing-the-auto-scale-on-mobile-devices/
So far I have tested in on
Samsun galaxy 2
Samsung galaxy s
Samsung galaxy s2
Samsung galaxy Note (but had to change the css to 800px [see below]*)
Motorola
iPhone 4
#media screen and (max-width:800px) {
This is a massive lip forward with mobile development ...
I had this issue myself, and I wanted to both be able to set the width, and have it update on rotate and allow the user to scale and zoom the page (the current answer provides the first but prevents the later as a side-effect).. so I came up with a fix that keeps the view width correct for the orientation, but still allows for zooming, though it is not super straight forward.
First, add the following Javascript to the webpage you are displaying:
<script type='text/javascript'>
function setViewPortWidth(width) {
var metatags = document.getElementsByTagName('meta');
for(cnt = 0; cnt < metatags.length; cnt++) {
var element = metatags[cnt];
if(element.getAttribute('name') == 'viewport') {
element.setAttribute('content','width = '+width+'; maximum-scale = 5; user-scalable = yes');
document.body.style['max-width'] = width+'px';
}
}
}
</script>
Then in your - (void)didRotateFromInterfaceOrientation:(UIInterfaceOrientation)fromInterfaceOrientation method, add:
float availableWidth = [EmailVC webViewWidth];
NSString *stringJS;
stringJS = [NSString stringWithFormat:#"document.body.offsetWidth"];
float documentWidth = [[_webView stringByEvaluatingJavaScriptFromString:stringJS] floatValue];
if(documentWidth > availableWidth) return; // Don't perform if the document width is larger then available (allow auto-scale)
// Function setViewPortWidth defined in EmailBodyProtocolHandler prepend
stringJS = [NSString stringWithFormat:#"setViewPortWidth(%f);",availableWidth];
[_webView stringByEvaluatingJavaScriptFromString:stringJS];
Additional Tweaking can be done by modifying more of the viewportal content settings:
http://www.htmlgoodies.com/beyond/webmaster/toolbox/article.php/3889591/Detect-and-Set-the-iPhone--iPads-Viewport-Orientation-Using-JavaScript-CSS-and-Meta-Tags.htm
Also, I understand you can put a JS listener for onresize or something like to trigger the rescaling, but this worked for me as I'm doing it from Cocoa Touch UI frameworks.
Hope this helps someone :)
<meta name="viewport" content="width=device-width, minimum-scale=1, maximum-scale=1">
suport all iphones, all ipads, all androids.
just want to share, i've played around with the viewport settings for my responsive design, if i set the Max scale to 0.8, the initial scale to 1 and scalable to no then i get the smallest view in portrait mode and the iPad view for landscape :D... this is properly an ugly hack but it seems to work, i don't know why so i won't be using it, but interesting results
<meta name="viewport" content="user-scalable=no, initial-scale = 1.0,maximum-scale = 0.8,width=device-width" />
enjoy :)
Why not just reload the page when the user rotates the screen with javascript
function doOnOrientationChange()
{
location.reload();
}
window.addEventListener('orientationchange', doOnOrientationChange);