Reputation: 846
I am experiencing a very strange and unique issue.
All my pages are using vh and vw CSS units instead of px due to the nature of the project.
Issue: On Android tablets, when you touch the input field the default keyboard pushes the view port which is causing the page and all the elements in the page to shrink.
On ipad this issue does not exist since the keyboard overlaps the screen and does not push the screen.
Looking for any solution to avoid the Android keyboard not to push the viewport of the browser and keep the original size.
Note: The only option i am left with is to avoid keyboard to push viewport, i won't be able to change the CSS units or use xml, manifest. These are web pages which experiencing this issue.
Upvotes: 50
Views: 36188
Reputation: 691
I'm creating a PWA where I disabled all scrolling and zooming, the problem for me is, that I use viewport units to calculate my layout exactly the way I want.
Setting the viewport with events helped, but the window.onresize
event is also called when the android keyboard comes up. My solution is to check if the keyboard is used by checking if document.activeElement
is a textarea or input field.
the only downside for me is, that when the user changes screen orientation while using the keyboard (which is not very common) the viewport resizing fails. To prevent this the element is blured and thus the keyboard closes, before the layout breaks. Not very user-friendly, but would be considered a rather rare edge-case.
final code:
window.onresize = () => {
if(document.activeElement.tagName !== 'TEXTAREA' && document.activeElement.tagName !== 'INPUT'){
setViewportSize();
}
}
window.onorientationchange = () => {
// check if keyboard is used
if(document.activeElement.tagName === 'TEXTAREA' || document.activeElement.tagName === 'INPUT'){
document.activeElement.blur();
}
setViewportSize();
}
function setViewportSize(){
document.documentElement.style.setProperty('--screen-width', `${window.innerWidth}px`);
document.documentElement.style.setProperty('--screen-height', `${window.innerHeight}px`);
}
keep in mind that window.onorientationchange
is deprecated and may not work at some point
Upvotes: 0
Reputation: 39
just using 'shrink-to-fit=no' in meta like :
<head>
<meta content="width=device-width, initial-scale=1, shrink-to-fit=no" name="viewport">
</head>
Upvotes: -1
Reputation: 81
Maybe viewport height (or just height) need to be refresh using a function?
const reference = useRef();
function avoidKeyboard(){
setTimeout(
()=>{reference.current.querySelector("#win").style.height = "calc(100vh)"}
,2000)
}
return (
<ref={reference} div>
<input type="text" id="win" onFocus={avoidKeyboard} onBlur={avoidKeyboard}/>
</div>
)
Upvotes: 0
Reputation: 865
Here's a more complete solution that takes screen orientation changes into account:
// Original viewport definition, note the "id" that I use to modify the viewport later on:
<meta name="viewport" id="viewport" content="width=device-width, height=device-height, initial-scale=1.0, maximum-scale=1.0, user-scalable=0"/>
<script>
// Global boolean variable that holds the current orientation
var pageInPortraitMode;
// Listen for window resizes to detect orientation changes
window.addEventListener("resize", windowSizeChanged);
// Set the global orientation variable as soon as the page loads
addEventListener("load", function() {
pageInPortraitMode = window.innerHeight > window.innerWidth;
document.getElementById("viewport").setAttribute("content", "width=" + window.innerWidth + ", height=" + window.innerHeight + ", initial-scale=1.0, maximum-scale=1.0, user-scalable=0");
})
// Adjust viewport values only if orientation has changed (not on every window resize)
function windowSizeChanged() {
if (((pageInPortraitMode === true) && (window.innerHeight < window.innerWidth)) || ((pageInPortraitMode === false) && (window.innerHeight > window.innerWidth))) {
pageInPortraitMode = window.innerHeight > window.innerWidth;
document.getElementById("viewport").setAttribute("content", "width=" + window.innerWidth + ", height=" + window.innerHeight + ", initial-scale=1.0, maximum-scale=1.0, user-scalable=0");
}
}
</script>
Upvotes: 8
Reputation:
Based on Tyler's question, this is a vanilla version of the script which seems to be a bit cleaner:
addEventListener("load", function() {
var viewport = document.querySelector("meta[name=viewport]");
viewport.setAttribute("content", viewport.content + ", height=" + window.innerHeight);
})
If you use it after the meta tag declaration you can even get rid of that addEventListener
:
var viewport = document.querySelector("meta[name=viewport]");
viewport.setAttribute("content", viewport.content + ", height=" + window.innerHeight);
Upvotes: 21
Reputation: 136
Nearly a year to late, but I want also to share my solution to this more and more important problem.
I created a small JS function SET which is loaded after the DOM is completed.
Every Element assigned to a special class (In this case ".pheight") doesn't resized when the viewport height decreases. It is only allowed to resize if the viewport height increases or the viewport width changes.
So in my Applications it works perfectly!
var docwidth = window.innerWidth;
var docheight = window.innerHeight;
function pheigt_init() {
pheigt_set_prevent_height();
window.onresize = function() {
if (docwidth !== window.innerWidth || docheight < window.innerHeight) {
pheigt_upd_prevent_height();
}
};
}
function pheigt_set_prevent_height() {
document.querySelectorAll('.pheight').forEach(function(node) {
node.style.height = node.offsetHeight + 'px';
});
}
function pheigt_upd_prevent_height() {
document.querySelectorAll('.pheight').forEach(function(node) {
node.style.removeProperty('height');
});
setTimeout(function(){ pheigt_set_prevent_height(); }, 100);
docheight = window.innerHeight;
docwidth = window.innerWidth;
}
document.addEventListener('DOMContentLoaded', pheigt_init());
Upvotes: 1
Reputation: 142
You could use % which doesn’t seems to be affected by this issue or vw (not affected for obvious reason) even for height.
Upvotes: 0
Reputation: 2351
In Angular 4+
import {Meta} from "@angular/platform-browser";
constructor(private metaService: Meta){}
ngOnInit() {
this.metaService.updateTag({
name: 'viewport',
content: `height=${this.height}px, width=${this.width}px, initial-scale=1.0`
},
`name='viewport'`
);
}
Upvotes: 0
Reputation: 696
I know this is an old question, but I had the exact same problem in my app. The solution I found was fairly simple. (My app is in Angular so I put this in the app.component's ngOnInit
function, but document.ready()
or any other "initialization complete" callback should work just fine with the proper experimentation)
setTimeout(function () {
let viewheight = $(window).height();
let viewwidth = $(window).width();
let viewport = document.querySelector("meta[name=viewport]");
viewport.setAttribute("content", "height=" + viewheight + "px, width=" + viewwidth + "px, initial-scale=1.0");
}, 300);
This forces the viewport meta to explicitly set viewport height, whereas hardcoding
<meta name="viewport"
content="width=device-width, height=device-height, initial-scale=1">
doesn't work because the device-width and device-height change when Android's soft keyboard is opened.
Upvotes: 58
Reputation: 2148
I faced the same problem recently, and it took me time to find a nice solution. I am designing a cordova app using html5, css3 and angularjs. But moreover, I want my app to fit every screen without writing tones of media queries and finally unreadable css. I started with viewport and vh, but the keyboard broke eveything.
So what you have to use is just a little javascript (here is jquery) like this :
$("html").css({"font-size": ($(window).height()/100)+"px"});
Here you go, now you can use "rem" exactly the same way you are using "vh", except that the viewport will not affect font-size. "Rem" is set on html root font-size only and we just set it with jquery to 1% of the screen height.
Hope this will help!
EDIT 1 : I just faced a new problem, so I give my solution here. Most of the time, smartphones have a minimum limit for font-size. You have to change all your rem value, and divide it by 3 or 4. Then, you have to change the javascript with :
$("html").css({"font-size": ($(window).height()/25)+"px"}); /*(this is if you divide with 4)*/
It is even easier if you are using SASS, you can create a rem function which will do division for you.
Upvotes: 3
Reputation: 1
The same problem put me off my stride. I don't found any css solution or workaround in order to solve the problem.
However, if you can use JQuery (or almost JavaScript), I have some code you can use to resolve it.
var viewportHeight = $(window).height();
var viewportWidth = $(window).width();
$('body,html').css("height",viewportHeight); //if vh used in body/html too
$('.someClass').css("height",viewportHeight*0.12);// or whatever percentage you used in vh
$('#someId').css("width",viewportWidth*0.12);
Well, this is just an example, you can use the percentage, jQuery identifiers you need to.
PD: there's one tip for your info. put the code at the final of your html or inside $(document).ready(function() { }); , and if you need responsive design, put it also inside orientationchange event; this way.
$(window).on("orientationchange",function(event){ window.setTimeout(function(){
var viewportHeight = $(window).height();
var viewportWidth = $(window).width();
$('body,html').css("height",viewportHeight);
$('.someClass').css("height",viewportHeight*0.12);
$('.someClass').css("width",viewportWidth*0.09);
$('#someId').css("height",viewportHeight*0.1);
}}, 450);});
I put a 450ms Timer above because of the delay of devices doing the orientationchange.
Sorry for my english, cheers
Upvotes: -1