The webserver wants to get info like screen size from each mobile handset that browses a webpage. The Javascript functions screen.width and screen.height return wildly inaccurate values.
Is there a way for the webserver to detect the screen size of the mobile handset? The client browser is webkit on Android.
You could try using CSS media queries, which should hopefully use the correct values.
<link rel="stylesheet" type="text/css"
media="only screen and (min-device-width: 320px)"
href="logResolutionScript?width=320" />
Use basically the same rules for other width and heights and a cookie to check if a client loads more than one stylesheet to get the correct value. You won't get the exact resolution, but it should be close enough. You can also check for orientation and use combinations of max-/min-(device)-width. Your file might end up with quite a lot of css-imports, but you should be able to pin down the resolution of the client quite accurately, unfortunately at the cost of a few HTTP-requests.
CSS3 Media Queries (Specification)
I found this article useful which mentions a meta tag that affects android and iPhone browsers which did what I needed:
<meta name="viewport" content="initial-scale=1.0">
From Mislav's article: What Mobile Safari does by default (i.e. without this directive) is display a zoomed-out, 980px-wide version of the page even if the layout itself is narrower. As content authors, with this directive we're saying "trust me, zoom to natural scale and I'll make sure it fits"
It seems to make the screen size and width values correct when read from JavaScript because the page is not zoomed (and if page is larger you can still scroll it). Alternatively there is probably a javascript variable to read out the zoom level.
For the iPhone this is documented here.
On Android you can work out the current zoom by adding an absolute div to the body of width 100%, and dividing the div's offsetWidth by window.innerWidth.
var iPadMeasureWidthNode = window.iPadWNode;
if (!iPadMeasureWidthNode) {
iPadMeasureWidthNode = window.iPadWNode = document.createElement('div');
// .ipad-measure-w {position:absolute; width:100%; top:-1px}
iPadMeasureWidthNode.className = 'ipad-measure-w';
document.body.insertBefore(iPadMeasureWidthNode, document.body.firstChild);
}
var zoominverse = 1000 / Math.round(1000 * iPadMeasureWidthNode.offsetWidth / window.innerWidth);
You can keep an element at 1:1 zoom by inverting (undoing) the amount of zoom:
// Not using scale3d because is hardware zooming which is ugly unreadable blurry pixel magnification.
node.style.webkitTransform = (zoominverse > 1) ? 'scale(' + zoominverse + ')' : '';
node.style.webkitTransformOrigin = (zoominverse > 1) ? '0 0 0' : '';
Zoom change is detected by window.onresize event (although resize event is delayed until after resize is completed... you can detect zoom start using the gesturestart event on iPad, or document.touchstart event and detect 2 fingers down).
Edit: After three corrections saying it doesn't work, I thought I better add an example showing it working. Tested works on: Android 4.1.2 normal browser, Android 4.1.2 Chrome, Android Opera Mobile 12.10, iPad 2 iOS4. (Didn't work on Android Firefox Mobile, and won't work in an iframe so jsfiddle won't work).
<!DOCTYPE html>
<html>
<head>
<style>
.ipad-measure-w {
position: absolute;
width: 100%;
top: -1px;
};
</style>
</head>
<body>
<button onclick="alertWidth()">alertWidth</button>
<div style="width: 1600px; height: 100px; background-color: blue;"></div>
<script>
function alertWidth() {
var iPadMeasureWidthNode = window.iPadWNode;
if (!iPadMeasureWidthNode) {
iPadMeasureWidthNode = window.iPadWNode = document.createElement('div');
iPadMeasureWidthNode.className = 'ipad-measure-w';
document.body.insertBefore(iPadMeasureWidthNode, document.body.firstChild);
}
var zoominverse = 1000 / Math.round(1000 * iPadMeasureWidthNode.offsetWidth / window.innerWidth);
alert(zoominverse);
}
</script>
</body>
</html>
Related
I am trying to add some conditional css for device detecting, because of some conflicts in desktop and android tablet landscape res
- if android_device?
:css
#media only screen and (max-width : 1280px) and (-webkit-device-pixel-ratio:2) {
/*CSS*/
}
can any one help me out where I have to use this check?
I got this reference from this url:
URL
There is no way of detecting an operating system or web browser using strictly CSS.
I'd recommend using Javascript to detect the web browser / operating system, and attach classes to the <body> of the page accordingly.
You can then write CSS specific to each use case, which will only be honored if that particular criteria is encountered.
Here is an example on jsFiddle.
There is actually a nice way I found useful which is to use conditional media queries in your HTML as follows:
<html>
<head>
<link rel="stylesheet" media="(max-width: 640px)" href="max-640px.css">
<link rel="stylesheet" media="(min-width: 640px)" href="min-640px.css">
<link rel="stylesheet" media="(orientation: portrait)" href="portrait.css">
<link rel="stylesheet" media="(orientation: landscape)" href="landscape.css">
</head>
</html>
I believe it solves your problem
Source: github.com/googlesamples/web-fundamentals
Following code is invalid css:
- if android_device?
:css
If you want to write specific styles for desktop and android devices then:
1) you can directly include desktop styles without any media query in css.
2)Styles which ar specific to android device can be included in between media queries as
#media only screen and (min-device-width : 480px) and (max-device-width : 800px) {
/*CSS */
}
As I've understood it, there are two supposedly reliable methods of determining the viewport width and the screen width on mobile displays. The viewport width is the virtual pixels, and screen width is the physical pixels. That's how it's supposed to be. But it's all the same for me. I've tested the following page with Android 4.0.4's stock browser and Chrome for Android in its current version (it won't tell me its number).
<!doctype html>
<html>
<head>
<meta name="viewport" content="width=device-width, initial-scale=1.0">
</head>
<body>
<div id="info"></div>
<script type="text/javascript">
setTimeout(function () {
var virtualWidth = document.documentElement.clientWidth;
var physicalWidth = screen.availWidth;
document.getElementById("info").innerHTML =
'Virtual width: ' + virtualWidth + '<br>' +
'Physical width: ' + physicalWidth + '<br>' +
'Ratio: ' + (physicalWidth / virtualWidth);
}, 500);
</script>
</body>
</html>
Here's the results:
The stock browser reports 540 for both values, Chrome reports 360 for both. The ratio is always 1. My phone is a Motorola RAZR i, and it should have a physical width of something around 540 (portrait orientation) and a ratio of 1.5. What's wrong with this page or the method, or the browsers? It's really hard to do mobile webdesign if the tools are totally unreliable. (And I'm not yet speaking of the random text size in Chrome...)
Update: Here's a live URL for you to do your own testing. Get a QR code of it.
I am working on a web app which has a width of 640px.
In the document head I set
<meta name="viewport" content = "width=640, user-scalable=no" />
so the content is nicely displayed and stretched horizontally.
This works perfectly on iOS but in Android the browser opens the website zoomed in so the user has to double click to zoom out and the entire page.
When I change the viewport setting to leave out the user-scalable tag like this:
<meta name="viewport" content="width=640" />
the Android browser adjusts nicely to the 640px - so it works.
The problem however now is, that users can zoom in and out on Android and iOS since the user-scalable tag is not set.
How can I forbid the scaling and at the same time set the viewport width to 640px on Android?
Trying rendering the viewport meta tag like so:
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" />
Setting scale settings will set user restrictions on how far they can zoom, and so if you set the initial and maximum to the same amount, this should fix the problem.
UPDATE: I was able to fix my bug for android devices all together by setting the below:
<meta name="viewport" content="width=640px, initial-scale=.5, maximum-scale=.5" />
I also noticed that some content, such as p tags were not flowing across the screen, so the hack for that would be to add the background-image property with empty string to any content that is stuck and is not going across the layout view. Hope this helps this time for you.
I wanted mobile to always show a website 640px wide because of a design that would break otherwise. (a design I did not make..) Thereby I wanted to disable zooming for mobile users. What worked for me me is the following:
- UPDATED 2013-10-31
First of all, there is no way you can do this without Javascript. You will have to check the user agent string. Therefore I created a mobile-viewport.js and included the script just before the closing tag:
function writeViewPort() {
var ua = navigator.userAgent;
var viewportChanged = false;
var scale = 0;
if (ua.indexOf("Android") >= 0 && ua.indexOf("AppleWebKit") >= 0) {
var webkitVersion = parseFloat(ua.slice(ua.indexOf("AppleWebKit") + 12));
// targets android browser, not chrome browser (http://jimbergman.net/webkit-version-in-android-version/)
if (webkitVersion < 535) {
viewportChanged = true;
scale = getScaleWithScreenwidth();
document.write('<meta name="viewport" content="width=640, initial-scale=' + scale + ', minimum-scale=' + scale + ', maximum-scale=' + scale + '" />');
}
}
if (ua.indexOf("Firefox") >= 0) {
viewportChanged = true;
scale = (getScaleWithScreenwidth() / 2);
document.write('<meta name="viewport" content="width=640, user-scalable=false, initial-scale=' + scale + '" />');
}
if (!viewportChanged) {
document.write('<meta name="viewport" content="width=640, user-scalable=false" />');
}
if (ua.indexOf("IEMobile") >= 0) {
document.write('<meta name="MobileOptimized" content="640" />');
}
document.write('<meta name="HandheldFriendly" content="true"/>');
}
function getScaleWithScreenwidth() {
var viewportWidth = 640;
var screenWidth = window.innerWidth;
return (screenWidth / viewportWidth);
}
writeViewPort();
The script checks if the visitor has an android (not chrome) or firefox browser. The android browser does not support the combination of width=640 and user-scalable=false, and the firefox browser does have a double screen width for some strange reason. If the visitor has a windows phone IE browser MobileOptimized is set.
I had the same situation, if you want the content to always fit the screen width without allowing the user to zoom in/out, use the following meta tags (this will work no matter what width you give)
<meta name="viewport" content="initial-scale=1.0, user-scalable=no" />
I am trying to figure out whether an effect seen in Mobile Safari, Android Browser, and Firefox for Android ("Fennec") is a bug or expected behavior. Basically, the issue is that an <input> element can receive the keyboard focus in certain scenarios even if the <input> element was not originally tapped by the user.
Here is a reproducible test case:
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="content-type" content="text/html; charset=UTF-8">
<meta name="apple-mobile-web-app-capable" content="yes">
<meta name="viewport" content="width=device-width, user-scalable=no">
<style>
#screenCover {
position: absolute;
top: 0;
right: 0;
bottom: 0;
left: 0;
background-color: rgba(0, 0, 0, 0.3);
z-index: 1;
}
#newItemInput {
position: absolute;
width: 200px;
height: 20px;
}
</style>
</head>
<body>
<div id="screenCover"></div>
<input id="newItemInput" type="text">
<script type="text/javascript" language="JavaScript">
var screenCoverEl = document.getElementById("screenCover"),
newItemInputEl = document.getElementById("newItemInput");
newItemInputEl.value = String(navigator.userAgent);
screenCoverEl.addEventListener("touchend", function (event) {
// Move the <input> element in the way.
newItemInputEl.style.top = (event.changedTouches[0].clientY - 10) + "px";
newItemInputEl.style.left = (event.changedTouches[0].clientX - 10) + "px";
// Hide the screen cover. Note that the screen cover was the original target of the tap.
screenCoverEl.style.display = "none";
}, false);
newItemInputEl.addEventListener("click", function (event) {
this.setSelectionRange(0, this.value.length);
}, false);
</script>
</body>
</html>
http://jsfiddle.net/f7TKc/2/
Or directly in a mobile browser:
http://fiddle.jshell.net/f7TKc/2/show/
In this example, a screen cover lies on top of the document, but on touchend, the screen cover is hidden and the <input> element is moved to where the user tapped the screen cover. In Mobile Safari, Android Browser, and Fennec 13.0b1, the <input> element inexplicably receives keyboard focus.
I am not able to test this in a newer version of Fennec because it crashes within the emulator.
Is this a bug or expected behavior?
Yes I think this is a bug and this might be on the same lines here :
http://code.google.com/p/android/issues/detail?id=6721 and if its so, then the link has a few workarounds others have tried out which you can try
I imagine that this is expected behavior.
The developers of a mobile web browser would need to make special accommodations for poorly designed web pages on small displays.
If a web page doesn't scale properly, a UI element could easily be covered by another layer.
Allowing the click event to propagate down through the layers would prevent the UI element from being unusable.
I don't have any documentation to support this theory.
------EDIT--------
After playing around with your code, I realized that the sceencover is being removed and the input is moved to the touch location at "touchstart". The input then receives focus at "touchend".
You can verify this by setting a delay before the input is moved. If your finger has been removed before the input is placed, it will not receive focus. If you are still touching the screen when the input is moved it will receive focus.
screenCoverEl.addEventListener("touchstart", function (event) {
screenCoverEl.style.display = "none";
setTimeout(function() {
newItemInputEl.style.top = (event.touches[0].clientY - 10) + "px";
newItemInputEl.style.left = (event.touches[0].clientX - 10) + "px";
},250);
}, false);
Touch events get called in this order: touchstart, touchend, click. You need to know if your touch event's touchstart happened on your input. You can implicitly declare a object variable (this is allowed in javascript) and initialize it to false. In the touchstart event set it to true. Then check for it's value in touchend and/or click events. The following code works exactly as expected.
<script type="text/javascript" language="JavaScript">
var screenCoverEl = document.getElementById("screenCover"),
newItemInputEl = document.getElementById("newItemInput");
newItemInputEl.value = String(navigator.userAgent);
screenCoverEl.addEventListener("touchstart", function (event) {
// Move the <input> element in the way.
screenCoverEl.style.display = "none";
newItemInputEl.style.top = (event.touches[0].clientY - 10) + "px";
newItemInputEl.style.left = (event.touches[0].clientX - 10) + "px";
// Hide the screen cover. Note that the screen cover was the original target of the tap.
}, false);
newItemInputEl.touchStartedHere = false; // remember if a touch event started on this input
newItemInputEl.addEventListener("touchstart", function (event) {
this.touchStartedHere = true; //set true if did..
}, false);
newItemInputEl.addEventListener("click", function (event) {
if(this.touchStartedHere){ // do what you want if touch started here
this.setSelectionRange(0, this.value.length);
}else{ // prevent from receiving focus if touch didn't start here.
this.blur();
}
this.touchStartedHere = false; // reset to false to prepare for next touch event.
}, false);
</script>
I am working on an app for android tablets using phonegap. I am converting an existing webpage, so I would prefer to not change the css or html. I am testing using a galaxy tab (android 2.2)
I want to set the viewable width to be 800px and disable the user from zooming in/out.
If I use <meta name="viewport" content="width=800px>, then the width is set correctly but the user can still scale the page.
If I use <meta name="viewport" content="width=800px, user-scalable=no">, then the user can't scale the page, but the width will be smaller (ie. the page is zoomed in, and the user has to scroll around to view the page)
The only other thing I can think of is using <meta name="viewport" content="minimum-scale=x, maximum-scale=x"> where x is some magical number which I don't know how to find.
Any help would be appreciated.
this worked for me:
<script>
$(document).ready(function(){
var vpScale = window.outerWidth/800/window.devicePixelRatio;
var metas = document.getElementsByTagName('meta');
var i;
for (i=0; i< metas.length; i++) {
if (metas[i].name == "viewport") {
metas[i].content = "minimum-scale=" + vpScale + ", maximum-scale=" + vpScale;
}
}
});
</script>