I'm working on a web application for tablets(for android and ios) and I'm facing a problem which is
giving me trouble for 2 days already.
The problem is that on android when you are in portrait mode and for example you focus an input field so the soft keyboard pops up the css media query orientation changes to landscape.
I already have read this question : CSS Media Query - Soft-keyboard breaks css orientation rules - alternative solution? and came up with this :
var is_keyboard = false;
var is_landscape = false;
var initial_screen_size = window.innerHeight;
window.addEventListener("resize", function() {
is_keyboard = (window.innerHeight < initial_screen_size);
is_landscape = (screen.height < screen.width);
updateViews();
}, false);
function updateViews()
{
if(!is_landscape)
{
if(is_keyboard)
{
$("html").removeClass("landscape portrait");
$("html").addClass("portrait");
}
}
}
However this doesn't work for some reason.
Is there anyway to change the orientation to portrait mode so the css media query thinks
we are in portrait mode ? I prefer not the use max-width etc because I need to support multiple screen sizes.
Thanks in advance.
After some searching I came up with this :
#media screen and (min-aspect-ratio: 13/9){ } // landscape
#media screen and (max-aspect-ratio: 13/9){ } // portrait
instead of
#media (orientation : landscape){ }
#media (orientation : portrait){ }
So if you are in the same boat as me I would advise you to just go
with this, so you spare yourself the headache.
There is this good article to solve this problem.
But sometimes 13/9 is not enough.
#media screen and (min-aspect-ratio: 14/9){ } // landscape
Becareful, if you increase it to 16/9, iphone5 don't recognize the landscape.
Separate from min and max "-aspect-ratio" are min and max "-device-aspect-ratio" which are the parameters for the device rather than the screen.
#media only screen and (max-device-aspect-ratio:1/1){
section{background:purple;}
:root{--devmul:1;}
}
#media only screen and (min-device-aspect-ratio:1/1){
section{background:orange;}
:root{--devmul:0.5;}
}
Mozilla et al say the parameter is deprecated but they leave no note as to what it has been deprecated by. Im operating by the assumption that the deprecation is that the parameter is not supported on all devices (like desktop browsers) but is generally available on devices which have an orientation.
The above snippet operated in the context of assumption that the user is on a desktop which is then corrected according to "device-aspect-ratio" and the general aspect ratio. Havent had the opportunity to test my theory out on portrait based desktop devices but this seems to work on all the mobile devices I have.
Related
This is for a web app, targeting any mobile browser but mainly Chrome and Safari for iOS10.
The browser opens the built-in keyboard when the user clicks on any input, which is fine, but I am trying to resize/relocate the items more relevant to the user at that point in time.
Is there anyway to calculate the height of the keyboard so that I can adjust items accordingly? Generic solution would be better, as inputs may open different types of keyboard (text, numeric...) but hard coding options are also a valid response at this stage.
I have tested around window.screen, window.innerHeight and so on to no avail...
Thanks!
There is a web standards to tackle this issue.
The Visual Viewport API, which also allows to listen to the resize event.
The visual viewport is the visual portion of a screen excluding on-screen keyboards, areas outside of a pinch-zoom area, or any other on-screen artifact that doesn't scale with the dimensions of a page.
visualViewport.addEventListener('resize', () => {
const h = event.target.height
…
});
Note: The credit of this answer fully goes to the user Alex k. However his solution has already been accepted by OP within
comments section and I guess these should have to be in answer section
as this is a very useful question.
To solve the problem, try CSS media queries like this:
var d = document.getElementById('d');
checkDimensions();
window.onresize = checkDimensions;
function checkDimensions(prevHeight) {
d.innerHTML = 'Window dimensions: ' + window.innerWidth + ' x ' + window.innerHeight;
}
.media-query-test {
display: none;
color: red;
}
#media all and (max-height: 700px) {
.media-query-test {
display: block;
}
}
<input type="number" />
<div id="d">
</div>
<div class="media-query-test">
Height is less than 700px!
</div>
And here's the JSFiddle: https://jsfiddle.net/t3yn2yz1/6/
As mentioned by Alex K in a comment:
I tried #media all and (max-height: 700px), which triggered when
opening the soft keyboard. Tested on Chrome browser on a Samsung S6,
Android 6.0.1. You may also have success with window.onresize
Does anyone see anything wrong with setting the content css property on body when on retina device for the purpose of running retina-specific js?
This seems to me like the simplest, lightest, cross-browser solution for testing for retina devices. At least it's worked for me...
CSS:
#media
(-webkit-min-device-pixel-ratio: 2),
(min-resolution: 192dpi) {
body {
content: 'retina'
}
}
Test with jQuery:
var isRetina = $('body').css('content') == 'retina';
Use window.devicePixelRatio. If it more than 1 - it's retina display.
For IE 10+ (which IE are available on tablets and smartphones) you can relay on screen.deviceXDPI and screen.logicalXDPI:
window.devicePixelRatio = window.devicePixelRatio ||
window.webkitDevicePixelRatio ||
screen.deviceXDPI/screen.logicalXDPI ||
1;
I'm, setting up the mobile side of a website at the moment, and I need custom CSS and Javascript for mobile, so in the CSS I have rules using #media screen and (max-width: 500px) { and in Javascript I was going to use if ($(window).width() < 500.
However, if I resize my browser to the exact pixel the mobile CSS starts being used and I console.log($(window).width()); I get 485.
Is this normal behaviour or am I doing something wrong?
Update:
Using this, the values seem to be in sync, only tested in firefox though at the moment.
var scrollBarWidth = false;
function mobileRules() {
if (!scrollBarWidth) {
var widthWithScrollBars = $(window).width();
$('body').css('overflow', 'hidden');
var widthNoScrollBars = $(window).width();
$('body').css('overflow', 'scroll');
scrollBarWidth = widthNoScrollBars - widthWithScrollBars;
console.log('Width: '+widthWithScrollBars+'. Without: '+widthNoScrollBars+'. Scroll: '+scrollBarWidth);
}
console.log($(window).width()+scrollBarWidth+' vs '+globals.mobile_width);
if ($(window).width()+scrollBarWidth < globals.mobile_width) {
console.log('Running mobile rules in jQuery');
}
}
In firefox, media queries consider the width of the scrollbar to be inside the screen width.
This is what gives you the 15px wider screen width.
In webkit based browsers they don't.
If you're interested in why this thing happens, I'll quote this comment of this article :
A problem with Webkit browsers (that aren't following spec) is that the browser can encounter an infinite loop condition caused by media queries, which leads to a browser crash.
For example: >500px overflow-y: scroll, <500px overflow-y: hidden. Size your browser to 505px window width. Since the scroll bar subtracts 15 or so pixels from the width used by the media query, the media query flips you to < 500, but as soon as you hit <500 the scrollbar goes away, and the media query flips you to >500, and then the fun starts because now you have a scroll bar again and you're <500px and you get that style with no scroll bar... Rinse and repeat until the browser finally dies.
Now, write some javascript to calculate the media query max widths, and you have a page that will crash Chrome/Safari as soon as you load it.
My guess is that the spec was written the way it was to prevent this condition. Firefox & Opera are following spec, it's not really their fault you don't agree with spec.
Right now I am using this function:
function is_retina_device() {
return window.devicePixelRatio > 1;
}
But its simplicity scares me. Is there a more thorough check?
According to everything that I've read recently, browsers seem to be moving towards the resolution media query expression. This is instead of device-pixel-ratio that is being used in the currently accepted answer. The reason why device-pixel-ratio should only be used as a fallback is because it is not a standard media query.
According to w3.org:
Once upon a time, Webkit decided a media query for the screen resolution was needed. But rather than using the already-standardized resolution media query, they invented -webkit-device-pixel-ratio.
View Full Article
Resolution Media Query Documentation
Since resolution is standardized and therefore the future let's use that first in the detection for future proofing. Also because I'm not sure if you want to detect only high dppx devices or only retina(Apple only) devices, I've added one of each. Finally just as a note, the Apple detection is just user agent sniffing so can't be depended on. Note: for the isRetina function I'm using a dppx of 2 instead of 1.3 because all retina apple devices have a 2dppx.
Note it appears that MS Edge has some issues with non integer values
function isHighDensity(){
return ((window.matchMedia && (window.matchMedia('only screen and (min-resolution: 124dpi), only screen and (min-resolution: 1.3dppx), only screen and (min-resolution: 48.8dpcm)').matches || window.matchMedia('only screen and (-webkit-min-device-pixel-ratio: 1.3), only screen and (-o-min-device-pixel-ratio: 2.6/2), only screen and (min--moz-device-pixel-ratio: 1.3), only screen and (min-device-pixel-ratio: 1.3)').matches)) || (window.devicePixelRatio && window.devicePixelRatio > 1.3));
}
function isRetina(){
return ((window.matchMedia && (window.matchMedia('only screen and (min-resolution: 192dpi), only screen and (min-resolution: 2dppx), only screen and (min-resolution: 75.6dpcm)').matches || window.matchMedia('only screen and (-webkit-min-device-pixel-ratio: 2), only screen and (-o-min-device-pixel-ratio: 2/1), only screen and (min--moz-device-pixel-ratio: 2), only screen and (min-device-pixel-ratio: 2)').matches)) || (window.devicePixelRatio && window.devicePixelRatio >= 2)) && /(iPad|iPhone|iPod)/g.test(navigator.userAgent);
}
If you want it for images you can use retinajs or this code is a common response to detect it:
function isRetinaDisplay() {
if (window.matchMedia) {
var mq = window.matchMedia("only screen and (min--moz-device-pixel-ratio: 1.3), only screen and (-o-min-device-pixel-ratio: 2.6/2), only screen and (-webkit-min-device-pixel-ratio: 1.3), only screen and (min-device-pixel-ratio: 1.3), only screen and (min-resolution: 1.3dppx)");
return (mq && mq.matches || (window.devicePixelRatio > 1));
}
}
Actually, the code you're using in your question is just completely right if you care only about modern browsers. (See: http://caniuse.com/#feat=devicepixelratio)
All modern browsers have it implemented, and older browsers would be just served your lower resolution images. I don't expect IE10- to show up on a retina / high-res device. Also, is using CSS checks in JavaScript not more weird than using a native window property?
Heck, devicePixelRatio browser support is even better than the resolution spec. (See: http://caniuse.com/#feat=css-media-resolution)
I'd say it's actually very safe to use, we use it in production websites with over 10 million visitors a month. Works as expected.
The only thing I'd change is the function name, as the need to load high res images doesn't technically mean the screen is retina. Actually, you don't even need a number check, as undefined > 1 results in false.
function is_high_resolution_screen() {
return window.devicePixelRatio > 1;
}
devicePixelRatio is not reliable at all. when you zoom in to 200%, the window.devicePixelRatio will return you 2, but you are not on a retina display.
I'm having a problem detecting a retina iPad (and similar devices) using just screen.availWidth and window.devicePixelRatio. The problem is that iPhones and iPads give the number of dips for screen.availWidth whereas android devices seem to report the number of physical pixels so I can't reliably do screen.availWidth / window.devicePixelRatio to calculate if the screen is of a tablet size.
Is there some other DOM property I can use to help me?
edit - To sum up in a way which hopefully makes clear that the question isn't a duplicate
How can I tell if screen.availWidth reports a value that has already been adjusted to take account of window.devicePixelRatio
That should help
var retina = (window.retina || window.devicePixelRatio > 1);
UPDATE
Retina.isRetina = function(){
var mediaQuery = "(-webkit-min-device-pixel-ratio: 1.5),\
(min--moz-device-pixel-ratio: 1.5),\
(-o-min-device-pixel-ratio: 3/2),\
(min-resolution: 1.5dppx)";
if (root.devicePixelRatio > 1)
return true;
if (root.matchMedia && root.matchMedia(mediaQuery).matches)
return true;
return false;
};
I haven't tested this, but here's an approach I think might work. I'll do a jsbin for it when I get time.
Because all devices (to the best of my knowledge) adjust for devicePixelRatio before passing the value to CSS media queries we can (in slightly pseudo code)
measure window.devicePixelRatio and screen.availWidth
Write a style tag to the head which includes a media query something like the following
#my-test-el {
display: none;
visibility: visible;
}
#media screen and (min-device-width:screen.availWidth) {
#my-test-el {
visibility: hidden;
}
}
Append <div id="my-test-el"> to the page
Read off the style.visibility attribute. If it equals hidden then the css value is the same value as screen.availWidth => screen.availWidth has been preadjusted for dpr.
edit It works! http://jsbin.com/IzEYuCI/3/edit. I'll put together a modernizr plugin too
edit And here's the pull request to get it in Modernizr - https://github.com/Modernizr/Modernizr/pull/1139. please upvote if you'd find it useful
This Modernizr plugin may help : Modernizr Retina : HiDPI Test
Note: Requires Modernizr's Media Queries feature