how to detect, in a cross-browser compatible way, the pixel density of the device visiting a webpage so that one can either serve standard or highres images without forcing doubled images to any device?
Is there any javascript library that automates this?
Why setting for Retina
iPhone 4s, iPhone 5, iPad3, iPad4, Macbook 15", Macbook 13" all use Retina display.
Android also support high resolution display, as well as Windows 8(Lumia 920) as mentioned by #JamWaffles.
Adding high resolution support is good for user experience but it definitely add load for developer, as well as bandwidth for mobile. Somebody don't suggest doing that.(Peter-Paul Koch, see the bottom "further reading")
Breifing
There are two methods to implement this function. One is Javascript and the other is CSS. All current solutions are for Retina, but could extend to Android high resolution easily.
CSS solution is about Media Query and -webkit-min-device-pixel-ratio or -webkit-device-pixel-ratio
Simple to use.
Apply to all browsers.
Disadvantage: Good for background. Harder for <img> tag
Javascript solution is about window.devicePixelRatio property.
Advantage: Javascript could manipulate image source. So, if you are going to serve direct image instead of background, better to use Javascript
Could not apply to all browsers but current support is good enough. See below for list.
Need a bit more setting.
CSS Solution
For normal images, say an icon
.sample-icon {
background-image: url("../images/sample-icon.png");
background-size: 36px 36px;
}
For Retina, add those below
#media only screen and (-webkit-min-device-pixel-ratio: 2), /* Webkit */
(min-resolution: 192dpi) /* Everyone else */ {
.sample-icon {
background-image: url("../images/sample-icon-highres.png");
background-size: 18px 18px;
}
You can use min-resolution: 2dppx to replace min-resolution: 192dpi, for those who don't want to remember numbers
Note the difference:
Two different icons, one normal, one high res. High res icon is double size than normal one.
The background size. The later is half. But you need test it in your real use.
Resource:
+ http://www.w3.org/blog/CSS/2012/06/14/unprefix-webkit-device-pixel-ratio/
+ http://view.jquerymobile.com/master/demos/widgets/buttons/#CustomIcons
Javascript Solution
Use window.devicePixelRatio property to detect resolution.
if (window.devicePixelRatio >= 2) {
alert("This is a Retina screen");
//Do something to manipulate image url attribute
//for example add `#2x-` before all image urls
}
Browser Support
Safari, Android WebKit, Chrome 22+ and on Android, Opera Mobile, BlackBerry WebKit, QQ, Palm WebKit,
Ref: http://www.quirksmode.org/blog/archives/2012/06/devicepixelrati.html
For Android
Android device use 1.5 as high resolution instead of 2 in Retina.
http://developer.android.com/guide/webapps/targeting.html --#Targeting Device Density with CSS, #Targeting Device Density with JavaScript
Further Good Reading
http://www.quirksmode.org/blog/archives/2012/07/more_about_devi.html
"I’m not a big fan of serving special retina images because it makes the web too heavy — especially over a mobile connection. Nonetheless people will do it." -- Peter-Paul Koch
Update 2013-04-18 Update jQuery mobile link
I found this:
var retina = window.devicePixelRatio > 1;
this should make retina return true, which you could use an if function to serve the right images.
Source: http://briancray.com/posts/detect-retina-displays-with-javascript
-InfiniDaZa
You need to be aware of network capabilities, responsive images and resource loading.
So far the javascript solutions are not quiet there as they generally require both resources (images) to be downloaded before the image swap.
Your best bet is background css media queries unless you go with a full solution like this:
http://adaptive-images.com
explained here: https://vimeo.com/38947881
Related
Consider the following site: 200minus.com
This site looks good on both a mobile phone and a desktop. It's as if when you view the site on a mobile phone, everything is appropriately shrunken. Where in the source code (HTML/CSS/JavaScript) is this being dealt with (or is this typically dealt with)?
In the CSS as media queries.
You can adapt the layout of CSS styling, depending on what size the browser window it's being viewed with is.
https://developer.mozilla.org/en-US/docs/Web/Guide/CSS/Media_queries, and
the official W3C documentation: http://www.w3.org/TR/css3-mediaqueries/
Theres 2 typical approaches to page design in mobile/desktop situations.
Adjust the page to suit the size of the display at load.
or...
Make the page respond to it's size dynamically( This is referred to as responsive design).
It is considered good form to use responsive design, as it has obvious advantages for screen resizes. Such as if a tablet/mobile was rotated or if a desktop user resized their window.
A good design should be implemented predominately in css as it is the fastest part of the page to update/ evaluate, and is simpler to implement than modifying the page style than JS. Proportional layout and forward planning help considerably when it comes to producing a layout that works well on many screen sizes and many guides will instruct you to design for mobiles first, then adjust for desktop sites. Personally I try to think of them as one part that is never a fixed size.
The bread and butter of responsive design is media queries; they allow you to only active certain css rules under one or more conditions. For example:
#media (min-width:650px){
.about_tablet{height:175px;}
}
#media (min-width:650px) and (max-width: 675px){ /* both conditions must be met */
.about_tablet{height:175px;}
}
#media (min-width:650px) , (max-width: 675px){ /* one or both conditions must be met */
.about_tablet{height:175px;}
}
Another very useful trick is viewports
<meta name="viewport" content="initial-scale=1.0,maximum-scale=1.0,user-scalable=no,width=device-width" />
They can be a little strange, behaviour isn't uniform across older mobile devices and they don't work at all on desktops, but they are quite useful. You can force a page width and scale the page on a pixel by pixel basis (800 px image on a 400px wide screen for instance). Prevent the user from being able to zoom in, or limit the zoom. Most useful is the width=device-width" which prevents the horrible zoomed out effect you get on non optimised webpages when you view them on a phone.
If theres a more specific concept you want to talk about I'm happy to help, a vast majority of my work is done for mobiles.
This is simple Bootstrap. Just try it out! I think it's really good. Also mentioned in the answer above this is everything done with CSS. For example this:
#media (min-width:768px){.container{width:750px}
}
#media (min-width:992px){.container{width:970px}
}
#media (min-width:1200px){.container{width:1170px}
}
When the width is smaller than 992 px the container will be set to 750px.
Greetings
In CSS using media queries, as explained here:
http://css-tricks.com/css-media-queries/
For example:
#media screen and max-width:600px { /* CSS here */ }
Is a common brakepoint that I use.
For some specific handling I've found I have to use javascript or jQuery to really get the effect I want, but generally CSS media queries and some intelligent and creative use of showing/hiding objects with the display property will get you 99% of the way there.
Everywhere I look, people keep saying stay away from UA-sniffing, even JQuery dropped that feature...
So how in the world does viewport-detection fill this gap of tablet vs phone (since newer models overlap in resolution)?
Can anyone explain how viewport detection is THE WAY to go on this...
According to your comments, what you really want to be doing is setting
<meta name="viewport"
content="width=device-width,
initial-scale=1.0,
minimum-scale=1.0">
In your <head>, which will force the pages to render at the device's width (or individual browser's width on that device, if you have, say Opera+Chrome+Android+etc installed on an Android, which will all likely be the same viewport, anyway).
For most 10" tablets, the viewport is the resolution of the device.
For most phones, the viewport-width (in portrait) is 320px - 360px.
So even though the Galaxy S4 and Note 3 might be 1920x1080, they're really 640x360 when the viewport is enabled (where each CSS pixel has 9 [3x3] screen-pixels worth of "sub-pixel" anti-aliasing applied, and where fonts/images are rendered at the full-resolution of the screen-pixels [a 200x100 image in CSS width/height could actually be a 600x300 image]).
Instead of 3:1, "retina" iDevices have a 2:1 viewport DPPX (dot-per-pixel).
Other Androids/BBs/browsers have wonky, potentially-fractional DPPXs (so depending on Int-based DPPXs in equality tests should be avoided, too).
Then, you can test in JS, by using document.body.getBoundingClientRect().width, assuming that your body and layout are set appropriately in CSS.
If you aren't already doing this, then you probably have more to worry about in your responsive-design than whether an ad seems too big or too small.
In terms of UA-sniffing, it is occasionally valid.
That is, it's valid when you sniff the UA from your server-side language, against a DB of all-known UAs for phones/tablets, and then pre-compose an HTML page with baked-in CSS/JS, and pre-optimize images for that exact model...
However, that's not sniffing for specific sections of a UA string, that's searching every UA string for an exact match, and building to the specs of that device.
The reasoning is simple here:
How do you differentiate, in a UA, between all Android phones, all Android tablets and all other "miscellaneous" devices which run on the Android platform, when there is no standard way of defining every facet of the device+browser?
You might know the difference between an iPhone and an iPad, but how do you know the difference between Chrome on a generic 6" Taiwanese cell-phone and a generic 7" Taiwanese tablet?
The one instance of UA-sniffing that I can think of being valid is the .toDataURL() method of canvas in Android 2.2, which was technically added (you can detect it), but was never implemented (it just returns blank data, because the function was never actually written).
EDIT
SASS example, assuming Meta-Viewport is set properly
// my-widget.scss
// assuming a 1920x1080 phone (Samsung S4, Note 3 / Google Nexus 5 / HTC One)
// assuming 1920x1080 is max tablet size (with portrait width 1080)
// assuming that larger than 1080 is desktop
.my-widget {
// default experience
background-color : grey;
color : black;
font-size : 1rem;
// 1080px phone
#media screen and (max-width : 360px) {
font-size : 2rem;
background-color : white;
color : black;
}
// tablets up to 1080px, or phones in landscape-mode
// (or really-really big phones... none in North America, that I know of)
#media screen and (min-width : 361px) and (max-width : 1080px) {
background-color : black;
color : white;
}
}
Don't want to allow phones in landscape to move to a different layout, then increase it to 640px, or add an orientation-check to the #media rule.
Realistically, you shouldn't be targeting device-constraints. What you should be doing is targeting content-restraints.
Where does it make sense for your widgets to flow out into three or four columns, and where does it make sense (at what pixel/em-widths) to make everything break down into single-column mode, because your experience breaks otherwise.
But the point is that with the viewport set, you can now serve the super-small experience to the Samsung Galaxy Gear, the Nintendo 3DS, etc... a small experience to phones, a medium experience to iPads and 7"/8" tablets, a larger-medium experience to full-sized tablets, a desktop/TV experience, and a stupid-huge experience to people with multiple-monitors, or the Nexus 10, or 4k TVs, et cetera.
And it all comes at no extra sniffing...
Just a little planning, and some media queries.
You can add as many or as few breakpoints as you like.
Heck, knowing that the iPhone is 320px, you could serve an experience specifically to things sized exactly like iPhones, and then serve generic mobile from 321 up to 480 (or 640), or just make one responsive-grid flow all the way from 320px to 768px (iPad width), and have that be your "small", and serve everything larger as desktop...
Sky is the limit.
You just need to know what breakpoints you care about.
SO now that the new ipad is on the market what's the best practice to deal with the images for your website?
there is there any script that automatically choose the versione x1 or x2 based on screen resolution?
You can do it a few different ways- either in the CSS directly using a different media query, or in Javascript, using the window.devicePixelRatio property.
This post has a good explanation of how to do it:
http://mikepultz.com/2012/03/how-to-make-images-look-good-on-the-ipad-3/
I'm using a media query. It's the most simple way. Here's the code I'm using:
#media only screen and (-webkit-min-device-pixel-ratio: 2) {
.your-class-name {
background: url("images/your-image#2x.png");
background-size: 18px 18px;
}
}
Remember:
• Add the '#2x' before your extension
• Use the same 'background-size' as your non-retina images
• Retina images are twice the size as non-retina
Exp:
non-Retina = 100px x 100px
Retina = 200px x 200px
Was just looking into this myself a few days ago - you might be interested in an adaptive route. Check this out http://adaptive-images.com/ pretty straightforward to implement. It shows you how to change it based on the display - be aware that means you'll have to save out higher res images and these devices might not have the best internet connection.
Ive mobile optimised my site with media queries. Everything looks how I would like it to but unnecessary images (as they're hidden with css) are being downloaded, slowing page loading times.
The easiest solution to this seems to be to replace as many inline images as I can with divs with background images. Then I can hide the div's with media query css for the mobile version.
I know there are potential downsides to this, outlined well in this post:
When to use IMG vs. CSS background-image?
So the company logo, pictures of staff, etc will stay as inline images.
Are there any issues to my approach I havn't considered? Ive read a lot about mobile optimisation, particularly with media queries, and I havn't heard of anyone doing this even though it seems quite an obvious solution where images could be inline or background.
Note, ive done some experiments with iPhones and Android (im waiting to get some Blackberrys) and I know to stop background images downloading I need to set display none to the div's parent, not the div with the background image itself.
Note2, in an ideal world sites would probably have been built as mobile first but in this situation (and often in others) there is a limit to how much the original site can be modified.
Thanks
Unfortunately, there are no great answers for the problems you’re trying to solve.
First, you have the option of moving everything from img tags to css background images. As you note, you have to be careful of losing semantic meaning by doing so.
But even if you can move to background images without losing semantic value, it is still not going to be 100% reliable. I wrote a series of tests last summer. I retested them last week in preparation for the chapter in our book on mobile first responsive web design. The tests are located at http://www.cloudfour.com/examples/mediaqueries/image-test/.
Unfortunately, Android fails every one of those techniques. You can see it downloading multiple copies of the image files via Blaze’s mobile test:
www.blaze.io/mobile/result/?testid=111031_96_316
UPDATE 3 Nov 2011: I’m currently trying to reconcile inconsistent results between what I see on Blaze and what I see using the same device in person. On my local Nexus S, it passes the fifth css test which limits the imgs by putting them inside the media queries. I watched the apache logs and confirmed the device only downloads one image instead of two for test 5. Blaze is running 2.3.3. My phone is running 2.3.6.
This is true for Android 2.2, 2.3 and 3.0. Hopefully, 4.0 will incorporate the webkit fixes that prevent this behavior:
bugs.webkit.org/show_bug.cgi?id=24223
BTW, this seems to conflict with your comment about testing setting the parent div to display:none on Android. If you’re getting different results, I’d love to hear about it.
If you keep them as img tags, what are your options? On this topic, I have written a multi-part series. The second part in the series provides an in-depth look at the different techniques:
http://www.cloudfour.com/responsive-imgs-part-2/
Again, none of the solutions are great. If you want something that is simple and will work most of the time, I’d go for adaptive-images.com. Or route images through Sencha.io SRC until we have a better solution for this problem.
BTW, sorry for having so many links that aren’t actually links. This is my first response on stackoverflow and it will only allow me to include two links.
Why not do a mobile first approach and then use media queries to enhance bigger screens.
Also you can use media queries to serve specific CSS files.
With the inline images I have tried a script block in the head and immediately after the opening body tag, which runs only for mobile devices (detect via classname added to body, or presence of a media query CSS file) that find all inline images with a certain class and empty the src attribute.
see here Prevent images from loading
<script type="text/javascript" charset="utf-8">
$(document).ready( function() { $("img").removeAttr("src"); } );
</script>
another way is to use url re-writing with mod rewrite and .htaccess or url rewrite module for iis. redirect user agent strings for mobiles to a small blank image.
see:
A way to prevent a mobile browser from downloading and displaying images
RewriteCond %{HTTP_USER_AGENT} (nokia¦symbian¦iphone¦blackberry) [NC]
RewriteCond %{REQUEST_URI} !^/images/$
RewriteRule (.*) /blank.jpg [L]
you can improve the above by loading your inline images from a different sub-domain and rewriting only those for mobile, as you don't want to rewrite all images (logo etc.)
If I'm understanding your question correctly, this seems like a perfect use case for srcset and sizes. This MDN article is a great post for learning the concept in-depth, but I will also summarize here. Here is a full, kind of complicated example:
<img srcset="elva-fairy-320w.jpg 320w,
elva-fairy-480w.jpg 480w,
elva-fairy-800w.jpg 800w"
sizes="(max-width: 320px) 280px,
(max-width: 480px) 440px,
800px"
src="elva-fairy-800w.jpg"
alt="Elva dressed as a fairy">
This code says:
If my browser doesn't support srcset use what is in src by default. Don't leave this out.
Hey browser, in srcset, here are 3 files and their natural widths separated by commas.
Hey browser, in sizes here are the widths of the space I want my image to take up depending on the media query. Use the one that matches first.
Then the browser itself will calculate which is the best image to use based on size AND screen resolution of the user then ONLY downloads that one which is pretty awesome in my book.
Ok, important thing to note is that mobile != low bandwidth != small screen and desktop != high bandwidth != large screen.
What you probably want is to make a decision based on client bandwidth and client screen size. Media queries only help with the latter.
David Calhoun has a great writeup on how to do this here: http://davidbcalhoun.com/2011/mobile-performance-manifesto
Highly recommended.
I stumbled recently on a great blog article on the subject, adressing the problem of responsive images (ie serving smaller images on smaller devices). The comments of the article are the most interesting part, and I think the replacement technique coined by Weston Ruter is a promising one :
http://jsbin.com/ugodu3/13/edit#javascript,html,live
(look at the other iterations in the comments).
It has lots of caveat (and is maybe difficult, but not impossible, to merge in an existing website, as the affect all your non absolute links, not only imgs), but I will try it (merged with a lazy loading) on my next project, which is a responsive website that my designer made quite heavy (and he does not want to make it lighter). Note that you could combine this to a php script resizing the images on demand if your server supports it.
The others common solutions for responsive imgs are cookie based (check Filament Group Responsive images plugin).
I prefer responsive images to css background because they're more correct semantically and parse-able SEO-wise. If I agree that we should not assume that bigger screen = more bandwidth, we lacks tools to address this (navigator.connection is Android only.) so assuming that most mobile users have a crappy 2G/3G connection is the safest way.
I'm not sure if you have thought about doing this but one of the things you can do is check for the width of the screen resolution with javascript and then if the resolution is less than some number (I use 480 because at that point that site looks bad) then switch the css templates from the default to the mobile themed template.
function alertSize() {
var myWidth = 0, myHeight = 0;
if( typeof( window.innerWidth ) == 'number' ) {
//Non-IE
myWidth = window.innerWidth;
myHeight = window.innerHeight;
} else if( document.documentElement && ( document.documentElement.clientWidth || document.documentElement.clientHeight ) ) {
//IE 6+ in 'standards compliant mode'
myWidth = document.documentElement.clientWidth;
myHeight = document.documentElement.clientHeight;
}
if (myWidth < 48)
{
switch css template to mobile template
}
}
Adapting to an existing site sucks but we do what me must. Here is how I solved it when importing a blog's feed into a mobile site. It scales the existing image on the page to the page's width ONLY if it's larger than the screen.
var $images = $("img[width]");
$images.each(function(){
try{
var $image = $(this);
var currentWidth = Number($image.attr("width"));
if(currentWidth > screen.width){
$image.width("100%");
$image.removeAttr("height");
}
}
catch(e)
{/*alert("image scale fail\n"+e)*/}
});
By making the width 100% and removing any height attribute, the image will scale perfectly to take up the full width whether you're in landscape or portrait orientation.
Chances are, the images on your regular site are already web optimized. There's usually not that much performance boost to be gained by loading smaller images. Reducing HTTP requests will make much more of a performance boost than bringing over smaller images. It may not be the perfect solution but it is certainly going to be the least maintenance. If you can't reasonably control what images are going to be used on the site, this is a pretty reasonable solution. If nothing else, maybe this will spark another idea for you.
For a site I'm working on I'm implementing image preloading with javascript however i most certainly do not want to call my preload_images() function if someone is on slow bandwidth.
For my market the only people with slow bandwidth are those using mobile internet on a smartphone.
What's the best approach for detecting these users so i can avoid image preloading for them?
option 1 : detect browser width
if($(window).width() > 960){ preload... }
option 2: detect user-agent with a list of browser to preload for
if($.browser in array safelist){ preload... }
are there any better options?
I find that I dislike sites that decide which version of the site I should access on a particular device or environment. It's great to make an initial decision based on whatever criteria you settle on, but for goodness sake, give me a link I can click so I can choose the "Higher Bandwidth Site" or vice versa and save it to a cookie. Then I can override any error the automated script makes with my own judgement.
Maybe using the CSS #media directive, along with hidden objects?
.imagesToPreload {display:none;}
#media screen {
#imageToPreload1 {background-image:url('blah.img');}
}
#media handheld {
#imageToPreload1 {background-image:none;}
}
Then in Javascript, you can fetch all objects in "imagesToPreload" class, read the backgroundImage property and preload it if its not none.
Admittedly, this is off the top of my head, I didn't test this idea. As usual, there must be something I am not thinking about...
I think edeverett's point about mobile not necessarily being slow (and similarly desktop not necessarily being fast) is a good one.
Remember not to remove choice for your visitors - i.e. most decent mobile browsers have an option not to load images (or specifically not to load large ones) and also avail of proxy services that compress images before downloading - some mobile visitors may want the full preloaded experience on your site.
Another solution might be something like:
if($(window).width() < 320){ preload_smaller_images(); }
There's less reason than there used to for the mobile browsing experience to be more limited than that of the desktop.
R. Hill's CSS suggestion is not the worst though (if it can be done in CSS, it should imo), it can also be done per-screen-size:
#media handheld, screen and (max-width: 320px){ /* */ }