Pixel ratio detection with JavaScript (Become an expert)
There are a number of good options for implementing Retina images with CSS and vector images, but there isn't a simple solution available for the standard <img>
tag. Photographs and other images are typically added using HTML within the content of websites. Until we have a new HTML tag available or an update to the existing <img>
tag to reference high-resolution images, we'll have to find an alternative solution.
Currently, the best two options available are using either a front-end or server-side scripting solution. This recipe will discuss how to use JavaScript to replace your normal images with high-resolution images if a Retina Display is detected.
Getting ready
To get started, we'll need a photo to use for our test page. If you've been following along with the previous recipes you can reuse your myImage.jpg
photograph, or if not, any large photograph will work. I'll be using my original photo, which is 1400 x 800 pixels.
How to do it...
If you don't already have your photos saved from before, save your image as
myImage@2x.jpg
inside your/images/
folder within the/retina/
folder. Then resize the image to 50 percent and save it asmyImage.jpg
in the same folder.Create an HTML document called
javascript.html
inside the/retina/
folder. Inside the<head>
tag of the basic HTML structure, we'll start by including jQuery.<head> <script src="http://code.jquery.com/jquery-1.5.1.min.js"></script> </head>
Next we'll add some HTML code to the
<body>
tag to display two versions of our photo.<body> <img src="images/myImage.jpg" width="700" height="400" /> <img src="images/myImage.jpg" class="retina" width="700" height="400" />
Then we'll add some JavaScript to swap out the high-definition image.
<script type="text/javascript"> $(function () { if (window.devicePixelRatio >= 2) { var images = $("img.retina"), imageType, imageName; // loop through the images and make them hi-res for(var i = 0; i < images.length; i++) { // create new image name imageType = images[i].src.substr(-4); imageName = images[i].src.substr(0, images[i].src.length - 4); imageName += "@2x" + imageType; //rename image images[i].src = imageName; } } }); </script> </body>
If you are working on a Retina device you should be able to open this page locally; if not, upload the folder to your web server and open the page on your device. You will notice how much sharper the second image is than the first image. On a device without a Retina Display, both images will look the same.
How it works...
To get started, we included the jQuery library to make it easy to select all the image elements on the page that we want to replace with high-resolution versions. This example is only replacing a single image, but the code can be applied to any images you add to your page.
Next, we added two HTML <img>
tags. Make sure to specify the width and height of the images so when we replace the image with the Retina version the size remains the same. In the second <img>
tag we included class="retina"
because our JavaScript will use this class to determine which images to replace with a high-resolution version.
Then we created a JavaScript function to load our Retina images. Our script started with an if
statement to check if the window.devicePixelRatio
is equal to or greater than two, so we only run the script if the user is on a Retina device.
If a Retina device is being used, then we create three variables. The first variable, called images
, is set to contain all the images with the retina
class. Then we created two additional variables, imageType
and imageName
, to store the image's extension (for example, .jpg
) and the filename without the extension.
The next section of code is a for
loop, which continues to run until all the images with the retina
class (that were stored in our images
variable) have been updated. The first line of the loop sets imageType
to the image's extension, which are the final four characters of the filename. Then it sets imageName
to the initial part of the filename, without the extension. Now that we have the image name split into two variables, we're ready to add in our Retina image.
The next line of the loop adds @2x
to the imageName
property and then completes the filename by adding the extension. For this code to work, you'll need to name all your high-resolution images the same as your normal images, but add @2x
at the end of the name like we've done here. Finally the src
value of the <img>
tag is set to our new high-resolution filename.
There's more...
When using this script you'll need to make sure to only add class="retina"
to the images that you have supplied a high-resolution image for. If you add it to an image without a corresponding @2x
image you'll have a broken <img>
tag. You could also use this script without that class to simplify it, but you would have to be very careful to ensure that you have two versions of every image on your site.
There are a couple of downsides to this JavaScript based Retina image implementation. First, if the user has JavaScript disabled they will only see the regular images. Second, Retina browsers may begin to download the small images first before they are replaced, which may use extra bandwidth. This wouldn't be much of a concern on a small page with few images, but it could cause issues on larger sites.
Loading only the correct image
Making some changes to our code could solve the image loading issue (starting to load the small image before it has been replaced). Instead of setting src
in our <img>
tag, we could use data-src
so the browser doesn't see the image location. Then in our JavaScript we would create a variable with the data-src
attribute, set it to the image's src
if the device is non-Retina, or add @2x
and set that to src
on a Retina device.
To make this work for browsers with JavaScript disabled, we'd also want to add our normal image tag wrapped inside of a <noscript>
tag. You can download an implementation of this script called retinise from dahliacreative.com/retinisejs or try out Adam Bradley's foresight.js
at https://github.com/adamdbradley/foresight.js. Adam's script also includes additional features, such as network connection testing to see if the user has a fast enough connection before loading the larger images.
Targeting other high-density devices
This script uses window.devicePixelRatio >= 2
to detect Retina devices. All of Apple's Retina Displays have a pixel-ratio of 2
, which this code corresponds to, however other manufacturers may make displays with pixel-ratios below this value. If you'd like to target those devices as well, you could lower this value. For example, window.devicePixelRatio >= 1.5
. This will depend on if you feel that it is worth serving the larger files to these devices, or you could create another if
statement and corresponding images to target these devices specifically.