Responsive Images
AAHC

Click on A to make all fonts on the page smaller.

Click on A to make all fonts on the page larger.

Click on HC to toggle high contrast mode. When you move your mouse over some bold words in high contrast mode, related words are automatically highlighted. Text is shown in black and white.


Lesson Objectives
When you complete this lesson, you will be able to:

Problems with Images in Responsive Design

In our design and implementation of the Dandelion Tours web page, we've used several images, including:

Most websites include lots of images; in fact, downloading images for web pages is typically the most time consuming part of loading a web page in the browser. Fortunately, browsers are implemented so that images are downloaded separately from the HTML and CSS of the page so your page can begin loading and even displaying without waiting for all the images to load.

Many of us have high-speed internet connections and use modern browsers, so even image-heavy websites load fairly quickly. However, more and more users are accessing the web via mobile, and despite all their incredible powers, mobile devices are limited by the speed of their network connections. Even the fastest mobile network is still far slower than ethernet network connections. For instance, in 2014, average download speed on 3G networks is 2.5 Mbps on AT&T in the United States. Average download speed on 4G/LTE AT&T networks is 6.2Mbps. Compare these numbers to the average download speed of 32.1Mbps for ethernet connections in the United States, and 26.5Mbps in Europe. In South Korea, the average download speed is a mind-blowing 88.68Mbps; and while their mobile speeds are also much higher than the United States, their mobile speeds are still only a quarter of their ethernet speeds at 14.31Mbps.

Note To explore download speeds across the world, check out http://explorer.netindex.com/maps; my source for the data cited above.

With higher internet speeds, as well as computers with retina screens (screens with higher pixel densities), we're tempted to put bigger images into our pages because they look great on big, modern screens. However, if users with slower internet connections or mobile connections try to download those images, your website is going to be a frustrating user experience.

So, image size is a major consideration when developing a responsive website that you intend to be viewed on both mobile devices and desktop computers. We want to make sure that the images we provide for download by a web page to your browser are appropriate for the screen size and device you're using. Even though mobile screens are often high-density screens (with more pixels so the content looks sharp), we don't necessarily want to use large images for mobile because they will take too long to download. So we need to find ways to make sure that the right images are being downloaded depending on the user's screen and device.

First, we'll take a look at how to use the browser developer tools to inspect the resources that our web page is downloading. We'll then look at a couple of different strategies for making sure we're downloading the right images to the user. Finally, we'll talk about what high density screens (retina screens) actually are, and what they mean for your web page design.

Inspecting Your Web Page Resources and Network Use

All the major browsers come with a developer tool called Network to let you inspect the resources your page is downloading (including the HTML, CSS, JavaScript, images, and fonts), and how much time the download is taking. I've included screenshots from the Chrome and Firefox Network tools below. If you access the Network tool in your own browser, it might look a bit different from these screenshots, but the basic idea should be the same.

Here's Chrome:

Here's Firefox:

Try to access the Network tool yourself in the browser. Open a new browser window, and load the Dandelion Tours page you've been working on. Open up the developer tools, and select the Network tab. Then Shift+Reload the page (hold down Shift while you click the Reload button). This will sometimes force the browser to download the resources for the page again rather than using versions cached on your local machine.

Note The URLs for the resources in your page will likely be different from mine, but the names of the resources should mostly be the same.

In either Chrome or Firefox, you can tell which resources your page is downloading when you load the page: the HTML (shown as just / in Firefox and DTours/ in Chrome), the CSS, the Google-hosted fonts, the images, and in my case, some JavaScript that I've added to the page (you'll be adding your own later).

Take note of which resources take the longest to download, and which are the biggest. You can look under the Size column to see the size of each resource, and time columns give you an indication of how long the page takes to load. Whether the browser has cached a resource greatly impacts the load time.

You can see from the screenshots above that the largest resources being downloaded by my Dandelion Tours page are the images and the jQuery library. We have four fairly large images on the page, so if anything is going to slow down the load time of the page, it will be those images. These images are local to my computer so they don't require a network download, but if you were hosting the web page online, they would take quite a bit longer for the user to download the first time (before they are cached). On my desktop computer (where I took the screenshots), the load time is not that noticeable, but on my iPhone (a 4s iPhone with 4G network, but usually with only 2 or 3 bars of connectivity here at home), the page load definitely takes longer.

If I'm on a big screen desktop computer and reduce the width of the browser below 640 pixels, it doesn't make any a difference on the load times of the resources, because I'm still using a computer with a fast connection. I may not get the most out of the big images I'm using (because of the narrow width of the browser), but the page isn't going to take any longer to load. However, if you that narrow browser window is on a mobile device connected via mobile network (not wifi), those big images will make a real difference in load time. So let's take a look at a couple of different ways we can change which images are loaded if we're accessing the web page from a mobile device.

Strategies for Smart Image Loading

To reduce the wait time for users when downloading a web page, particularly on mobile devices, we want to make sure they aren't downloading images they don't need, or images that are the wrong size for the device.

One strategy we can use is with media queries. You've already used media queries to change the layout of a page depending on the width of the user's screen; we can also use media queries to determine which images to download into the background of an element. However, this is a useful strategy for background images only; if you specify an image in your HTML using the <img> tag, that image will be downloaded regardless of what media queries you have in your CSS. We'll discuss a strategy for handling images loaded with the <img> tag next. For now, we'll focus on the background images we're loading in the CSS.

So far, we've styled the experience <section> of the site like this (in main.css):

OBSERVE:
section#experience {
    position: relative;
    color: white;
    padding: 0em 60% 2em 2%;
    background-size: cover;
    background-image: url("https://courses.oreillyschool.com/html5_responsive_design/images/TourGuide.jpg");
    min-height: 400px;
}            

The TourGuide.jpg image file is about 100Kb, and 960 pixels wide. That's not too bad, but far more than we need for a mobile page that is 640 pixels wide. So, we created a version of the image that is 640 pixels wide and only about 50Kb that we can use if the screen is less than or equal to 640 pixels wide. Update your file, main.css:

CODE TO TYPE:
section#experience {
    position: relative;
    color: white;
    padding: 0em 60% 2em 2%;
    background-size: cover;
    background-image: url("https://courses.oreillyschool.com/html5_responsive_design/images/TourGuide.jpg");
    min-height: 400px;
}

@media only screen and (min-width: 641px) {
    section#experience {
        background-image: url("https://courses.oreillyschool.com/html5_responsive_design/images/TourGuide.jpg");
        min-height: 400px;
    }
}
@media only screen and (max-width: 640px) {
    section#experience {
        background-image: url("https://courses.oreillyschool.com/html5_responsive_design/images/TourGuide_sm.jpg");
        background-position: center top;
        padding: 0em 30% .8em 2%;
    }
}             
         

main.css and your index.html file. Narrow your browser to 640 pixels or less, and you should see the smaller image appear in the browser window. It's cropped a bit differently so you should be able to tell when the image loads.

Now, when you do this in your browser (that is, start with a wider browser window, and then narrow the window so that the browser loads the smaller image), you are actually requesting and loading both images, so you haven't saved any network downtime at all. In fact, you are increasing the number of requests you're making because you end up loading both images. However, on mobile devices, you'll only request one of these images: if the screen width is 640 pixels or less, you'll request and download the smaller image; otherwise, you'll request and download the larger image (same as if you don't resize your browser window). This makes your web page "responsive" in a new way: for smaller, narrower screens, your web page will take less time to load, because you are downloading almost 50Kb less data!

Let's take a closer look at a couple of other things we did in this new CSS code:

OBSERVE:
@media only screen and (min-width: 641px) {
    section#experience {
        background-image: url("https://courses.oreillyschool.com/html5_responsive_design/images/TourGuide.jpg");
        min-height: 400px;
    }
}
@media only screen and (max-width: 640px) {
    section#experience {
        background-image: url("https://courses.oreillyschool.com/html5_responsive_design/images/TourGuide_sm.jpg");
        background-position: center top;
        padding: 0em 30% .8em 2%;
    }
}             

First, notice that we moved the min-height property into the wide media query (that is, the media query that specifies properties for when the page is greater than 640 pixels wide). For the narrow version of the page, we don't need to set a min-height for the background image, because the text will never get wide enough that we lose too much height in the element to see the image properly.

Also notice that for the narrow media query, we specify a position for the image at the center and top of the element. That means that if any part of the image needs to be cropped to fit into the element, it will be cropped from the sides and bottom. That keeps the image of the woman more centered in the element, and generally looking better. We've also changed the padding a bit for the mobile version, to give the text a bit more room on the right hand side so it stays balanced on the page.

If you make your browser narrow (less than 640 pixels wide) and Shift+Reload the page again, and look at the Network tab in the developer tools, you see that the smaller version of the Tour Guide photo has been loaded instead of the larger version.

The other background image we use in the page is the image for the <header> of the details <section>. This image is already fairly small, around 50Kb, so we're not going to bother creating a separate media query for it (although that will make a good exercise for you).

You might be tempted at this point to try to make all your images background images: don't. As we discussed in an earlier lesson, there is a significant semantic difference between background images and content images. For instance, the two images we're using in our web page for the experience section and the header of the details section truly are background images. If they weren't there, there would be very little difference to the end user in terms of understanding the page and the message we're trying to convey. However, the photos, which are specified with the <img> tag, truly are content images. There, we want the user to get a sense of the kinds of things they'll be seeing on the tour, so if those images disappeared, the user experience would be downgraded. Also, keep in mind that for accessibility, background images are often ignored by screen readers, whereas screen readers will usually read the content of the alt property of an <img> tag to users so they get a sense of what the image is about. So choosing the right kind of image is also important for accessibility.

Using JavaScript to Load Image Content

When the browser sees an <img> tag, it sends a request to the server asking for the image. So, if you specify an image in the <img> tag, the browser will always request it—even if that image is hidden with CSS or replaced later with JavaScript or a CSS media query.

If we want to make sure the browser loads the correct image based on the width of the screen (or even the screen's pixel density, which we'll look at later), we have to request and load the images using JavaScript instead. We're going to do that for the three photos in the details <section> using jQuery. First, we'll modify the index.html file to link to the jQuery library, as well as a new JavaScript file that you're going to write, main.js. We'll modify the <img> elements in the photos section so we're not directly linking to the images with the src attribute. Finally, we'll add our own code to load the images for the photos section. Edit index.html and add the following two script links in the <head>:

CODE TO TYPE:
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<title>Dandelion Tours</title>

<script src="http://code.jquery.com/jquery-latest.min.js"></script>
<script src="main.js"></script>

<link href='http://fonts.googleapis.com/css?family=Nunito' rel='stylesheet' type='text/css'>
<link href='http://fonts.googleapis.com/css?family=Dancing+Script' rel='stylesheet' type='text/css'>

<link rel="stylesheet" href="reset.css">
<link rel="stylesheet" href="dt.css">
<link rel="stylesheet" href="main.css">

</head> 
...  

We haven't created main.js, so don't reload the page just yet. Instead, continue editing index.html to modify the <img> tags in the photos <section> where we are linking to the three images. Change the src attribute of each image into a data-src attribute and modify the image URLs. Edit index.html and modify the images in the photos <section>:

CODE TO TYPE:
...
<section id="photos">
    <header>
        <h1>A sampling of photos from our tours</h1>
    </header>
    <div class="photo-container">
        <div class="photo-with-caption">
            <figure>
                <img data-src="https://courses.oreillyschool.com/html5_responsive_design/images/Brazil_Butterfly_m.jpg" alt="photo of mountains and rainbow">
                <figcaption>Exciting wildlife</figcaption>
            </figure>
        </div>
        <div class="photo-with-caption">
            <figure>
                <img data-src="https://courses.oreillyschool.com/html5_responsive_design/images/Iceland_Road_m.jpg" alt="photo of road in snow">
                <figcaption>Spectacular landscapes</figcaption>
            </figure>
        </div>
        <div class="photo-with-caption">
            <figure>
                <img data-src="https://courses.oreillyschool.com/html5_responsive_design/images/Alaska_View_m.jpg" alt="photo of rainbow and waterfall">
                <figcaption>Incredible views</figcaption>
            </figure>
        </div>
    </div>
</section>             
...             

and your index.html file. The images disappear for now; we'll fix that in just a moment by writing code in the main.js file to load the images. But first, let's take a closer look at what we did to modify the <img> tags.

What exactly is data-src, or more generally, data-*? data-* is a way for you to specify a custom attribute in your HTML elements. HTML5 added support for these arbitrary attributes in elements. As long as the attribute you add begins with "data-" you can create any new attributes you want and add them to the opening tags of any HTML element. You don't want to use custom attributes to do the work that an existing attribute would do; however, if you need to add custom data to your HTML elements that doesn't fit into an existing attribute, this is a really handy feature of HTML. Adding a custom attribute doesn't change how the element is rendered or styled in any way, so these attributes are useful primarily when you are using them with JavaScript.

In our case, we want to store the image URL to use in the page in the <img> elements, but we don't want to use the src attribute, because using the src attribute will cause the image to be loaded by the browser. By moving the image URLs to the data-src attribute, we can store this data in the <img> element without the browser loading the image (it will only load images specified in the src attribute), and then use JavaScript to load the images using the URLs in the data-src attributes. Notice that we removed the extension "_m.jpg" from each of the images in the code above. We did this because we want to get the name of the image, and then decide, based on the width of the screen, whether to load the medium sized images (with the "_m.jpg" extension) or the small images (with the "_s.jpg" extension). We've created both medium and small versions of each of the images we're using in the page, so we can load either one.

Let's take a look at the code, so you can see what all this means. Create or open up your main.js file, and add the following JavaScript (jQuery):

CODE TO TYPE:
$(document).ready(function () {
             
    // returns a list of images in the photos section
    var $imgs = $("#photos .photo-with-caption figure img");
             
    if ($(window).width() > 640) {
        $imgs.each(function(i, theImage) {
             theImage.src = $(theImage).data("src") + "_m.jpg";
        });
    } else {
        $imgs.each(function(i, theImage) {
             theImage.src = $(theImage).data("src") + "_sm.jpg";
        });
    }
});             
 

main.js and your index.html file. Now the images appear in the page. If you load the page when the browser is wide, the medium-sized images load (check out the network tab to see which images are loading). If you narrow your browser, and Shift+Reload, the small images load. You can also use the Elements tab to inspect your HTML. After loading the page, you see both the data-src and src attributes in the images, and you can check to see the value of the src attribute directly. For instance, after reloading the page with the window opened wide, I see:

Here, the <img> tag I'm inspecting has the src attribute set to a medium size image. After loading the page on my iPhone, I see:

And here, the <img> src attribute is set to a small image because the width of the iPhone screen is less than or equal to 640 pixels.

Let's step through the jQuery code in detail. First, we have defined this code to execute once the page is fully loaded, by specifying a ready function. This is a jQuery method that is equivalent to specifying a load handler on the window in plain JavaScript. It says, once the page has completely loaded and the DOM is ready, then execute the function we're passing into the ready method.

The first thing we do is get all the images that are in the photos section using a selector:

OBSERVE:
var $imgs = $("#photos .photo-with-caption figure img");             

This says: get all the images that are nested within a <figure> element which is nested in an element with the class ".photo-with-caption" that is, in turn, nested within an element with the id "#photos." The resulting $imgs object is a jQuery list of all the images.

We then test to see the width of the browser window, using the jQuery method, width().

OBSERVE:
if ($(window).width() > 640) {
    $imgs.each(function(i, theImage) {
        theImage.src = $(theImage).data("src") + "_m.jpg";
    });
} else {
    $imgs.each(function(i, theImage) {
         theImage.src = $(theImage).data("src") + "_sm.jpg";
    });
}             

If the width is greater than 640 pixels, we loop through all the images, get the value of the image's data-src attribute using the jQuery data() method, append the string "_m.jpg" to the URL, and update the src attribute to contain the URL for the medium size images. Similarly if the width is equal to or less than 640 pixels, we loop through all the images, get the value of the image's data-src attribute using the jQuery data method, append the string "_sm.jpg" to the URL, and update the src attribute of the images. As soon as the src attribute of an image is added or changed, the browser will load that image. This code runs as soon as the page is loaded, and you see these images load.

When you use the jQuery data method to get the value of the data-src attribute, notice that you don't have to specify the "data-" part of the attribute name, only the part that is appended to it, in this case "src." The data method is specifically for getting data-* attributes from your HTML.

Note The data-src attribute we added to the <img> tags (replacing the src attribute) is a feature of HTML5, and has solid support in all browsers, going back a few versions—except for IE. IE11 fully supports the new data-* attributes, as they are called, but previous versions of IE did not. However, because we're using the jQuery data method to access the data-src attribute, the code should work in IE going back to IE8 (jQuery handles the browser support issues for us).
Solutions Coming in the Future with New HTML Support
The Srcset Attribute of the <img> Element

We've solved the problem of deciding which images to load into the <img> elements in our page using JavaScript. However, not everyone is happy with this solution because it requires writing code. It would be better if we could handle this "image problem" using HTML, without the JavaScript coding.

Now that mobile devices are so popular, the "image problem" is high on the list of problems to solve at the W3C and with responsive designers. There are two proposals that are in development at the W3C to create HTML elements that will allow the browsers to make a smart decision about which images to download and display, depending on the characteristics of the user's display and network connection. Unfortunately, these proposals are still in the development stage, so you won't be able to actually use them right now for public web pages.

Note There are a couple of browsers that do already support the two solutions we're going to discuss: Chrome (version 39) and Opera (version 25). So if you have either of these browsers installed, you can run this code! However, because the majority of users are probably not be using these browsers, it's not a good idea to use either of these solutions yet for pages you plan to post on the internet. Until there is widespread support for the solutions in all browsers, we are stuck playing with them on our local computers only.

The first solution we'll discuss is a new attribute proposed for the <img> element: the srcset attribute. To use this, we'll first remove the jQuery code we just wrote, and then update the <img> elements in the photos section once again. Edit index.html and modify your HTML:

CODE TO TYPE:
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<title>Dandelion Tours</title>

<script src="http://code.jquery.com/jquery-latest.min.js"></script>
<script src="main.js"></script>

<link href='http://fonts.googleapis.com/css?family=Nunito' rel='stylesheet' type='text/css'>
<link href='http://fonts.googleapis.com/css?family=Dancing+Script' rel='stylesheet' type='text/css'>

<link rel="stylesheet" href="reset.css">
<link rel="stylesheet" href="dt.css">
<link rel="stylesheet" href="main.css">

</head>  

...

<section id="photos">
    <header>
        <h1>A sampling of photos from our tours</h1>
    </header>
    <div class="photo-container">
        <div class="photo-with-caption">
            <figure>
                <img data-src="https://courses.oreillyschool.com/html5_responsive_design/images/Brazil_Butterfly" alt="photo of mountains and rainbow">
                <img srcset="https://courses.oreillyschool.com/html5_responsive_design/images/Brazil_Butterfly_m.jpg 1400w,
                            https://courses.oreillyschool.com/html5_responsive_design/images/Brazil_Butterfly_sm.jpg 640w">
                <figcaption>Exciting wildlife</figcaption>
            </figure>
        </div>
        <div class="photo-with-caption">
            <figure>
                <img data-src="https://courses.oreillyschool.com/html5_responsive_design/images/Iceland_Road" alt="photo of road in snow">
                <img srcset="https://courses.oreillyschool.com/html5_responsive_design/images/Iceland_Road_m.jpg 1400w,
                            https://courses.oreillyschool.com/html5_responsive_design/images/Iceland_Road_sm.jpg 640w">
                <figcaption>Spectacular landscapes</figcaption>
            </figure>
        </div>
        <div class="photo-with-caption">
            <figure>
                <img data-src="https://courses.oreillyschool.com/html5_responsive_design/images/Alaska_View" alt="photo of rainbow and waterfall">
                <img srcset="https://courses.oreillyschool.com/html5_responsive_design/images/Alaska_View_m.jpg 1400w,
                            https://courses.oreillyschool.com/html5_responsive_design/images/Alaska_View_sm.jpg 640w">
                <figcaption>Incredible views</figcaption>
            </figure>
        </div>
    </div>
</section>             
...             
         

and your index.html file using either Chrome (version 38 or greater) or Opera (version 25 or greater). The images load just fine in either one of these browsers. If you try another browser, you will not see the images! Notice that this solution requires no JavaScript at all, so when (or if) this solution is implemented in all browsers, it will be easier than our previous jQuery-based solution. Right now, this is a proposed solution, so it remains to be seen if it's adopted across the board by browsers.

The nice thing about this solution is that it makes use of the existing <img> element, so no new element needs to be added to HTML. However, some web designers have complained that the syntax is a little hard to understand. The value of the srcset attribute is a comma-delimited list of image URLs, each followed by a number with the "suggested" width for using the image. It's important to note that the srcset is a "suggestion" for the browser, but under some circumstances the browser may choose to load whichever image it wants. Also, note that the widths that follow the images are suggestions; in our case, the smaller image will probably be loaded up to 640 pixels wide, and the medium image will probably be loaded for any widths above 640 pixels. However, we must specify a width to follow the medium size image. We used 1400, however, if you open your browser wider than that and reload the page, the medium images are still used. Again, srcset is considered a suggestion to the browser.

The <picture> Element

A second proposed solution to the "image problem" is a whole new element for HTML: the <picture> element. The <picture> element is more flexible than the srcset attribute on the <img> element, and rather than being a suggestion like the <img> srcset, it clearly defines what browsers should do in a variety of situations. Let's change the code to use the <picture> element, and then we'll step through how it works. Modify index.html as shown:

CODE TO TYPE:
...
<section id="photos">
    <header>
        <h1>A sampling of photos from our tours</h1>
    </header>
    <div class="photo-container">
        <div class="photo-with-caption">
            <figure>
                <img srcset="https://courses.oreillyschool.com/html5_responsive_design/images/Brazil_Butterfly_m.jpg 1400w,
                            https://courses.oreillyschool.com/html5_responsive_design/images/Brazil_Butterfly_sm.jpg 640w">
                <picture>
                    <source media="(min-width: 641px)"
                            srcset="https://courses.oreillyschool.com/html5_responsive_design/images/Brazil_Butterfly_m.jpg">
                    <source media="(max-width: 640px)"
                            srcset="https://courses.oreillyschool.com/html5_responsive_design/images/Brazil_Butterfly_sm.jpg">
                    <img src="https://courses.oreillyschool.com/html5_responsive_design/images/Brazil_Butterfly_sm.jpg"
                         alt="photo of mountains and rainbow">
                </picture>
                <figcaption>Exciting wildlife</figcaption>
            </figure>
        </div>
        <div class="photo-with-caption">
            <figure>
                <img srcset="https://courses.oreillyschool.com/html5_responsive_design/images/Iceland_Road_m.jpg 1400w,
                            https://courses.oreillyschool.com/html5_responsive_design/images/Iceland_Road_sm.jpg 640w">
                <picture>
                    <source media="(min-width: 641px)"
                            srcset="https://courses.oreillyschool.com/html5_responsive_design/images/Iceland_Road_m.jpg">
                    <source media="(max-width: 640px)"
                            srcset="https://courses.oreillyschool.com/html5_responsive_design/images/Iceland_Road_sm.jpg">
                    <img src="https://courses.oreillyschool.com/html5_responsive_design/images/Iceland_Road_sm.jpg"
                         alt="photo of mountains and rainbow">
                </picture>
                <figcaption>Spectacular landscapes</figcaption>
            </figure>
        </div>
        <div class="photo-with-caption">
            <figure>
                <img srcset="https://courses.oreillyschool.com/html5_responsive_design/images/Alaska_View_m.jpg 1400w,
                            https://courses.oreillyschool.com/html5_responsive_design/images/Alaska_View_sm.jpg 640w">
                <picture>
                    <source media="(min-width: 641px)"
                            srcset="https://courses.oreillyschool.com/html5_responsive_design/images/Alaska_View_m.jpg">
                    <source media="(max-width: 640px)"
                            srcset="https://courses.oreillyschool.com/html5_responsive_design/images/Alaska_View_sm.jpg">
                    <img src="https://courses.oreillyschool.com/html5_responsive_design/images/Alaska_View_sm.jpg"
                         alt="photo of mountains and rainbow">
                </picture>
                <figcaption>Incredible views</figcaption>
            </figure>
        </div>
    </div>
</section>             
...             

Also make a small change to your CSS in main.css:

CODE TO TYPE:
section#photos div.photo-with-caption img {
    width: 100%;
}
section#photos div.photo-with-caption picture {
    width: 100%;
}             

and your index.html file using either Chrome (version 38 or greater) or Opera (version 25 or greater). Again, you see either the medium images if your browser is open wide, or the small images if your browser is narrow or you are on your mobile device with a screen width of less than 640 pixels.

The <picture> element contains one or more <source> elements that you use to specify the images, only one of which will be used. Each <source> element has a media attribute that specifies a media query—just like the media queries we're using in our CSS—that determines under which conditions the image will be loaded and used in the page. Each <source> element also has a srcset attribute that you use to specify the image to use when the media query's feature is true. So, in this example:

OBSERVE:
<picture>
    <source media="(min-width: 641px)"
               srcset="https://courses.oreillyschool.com/html5_responsive_design/images/Brazil_Butterfly_m.jpg">
    <source media="(max-width: 640px)"
               srcset="https://courses.oreillyschool.com/html5_responsive_design/images/Brazil_Butterfly_sm.jpg">
    <img src="https://courses.oreillyschool.com/html5_responsive_design/images/Brazil_Butterfly_sm.jpg"
            alt="photo of mountains and rainbow">
</picture>   

...if the width of the browser is 641 pixels or greater, the medium image will be used, and if the width of the browser is 640 pixels or less, the small image will be used.

We also provide an <img> element inside the <picture> element that can be used as a fallback for older browsers (although I've noticed this fallback doesn't seem to work in Firefox).

Both the srcset attribute of the <img> element and the picture element are much-anticipated additions to HTML to give us a lot more flexibility and control over how images are loaded by the browsers. Unfortunately, for now, these are still experimental and can't be used in public web pages on the internet because of lack of support in browsers.

Note Check the http://caniuse.com/ website when you take this course as more browsers may be supporting the srcset attribute and the picture element by the time you take the course.
Use the data-src Option with JavaScript for Now

Because we can't use either the srcset attribute on the <img> element, or the <picture> element quite yet, let's switch our code back to using the <img> element with the data-src attribute, plus a little JavaScript to load the correct images (as we did above). In this version, we'll take a quick look at how to rewrite the code using plain JavaScript instead of jQuery in case you want to try that (and also reduce the total download size by eliminating jQuery). Edit index.html and modify the images in the photos <section>:

CODE TO TYPE:
...
<section id="photos">
    <header>
        <h1>A sampling of photos from our tours</h1>
    </header>
    <div class="photo-container">
        <div class="photo-with-caption">
            <figure>
                <picture>
                    <source media="(min-width: 641px)"
                            srcset="https://courses.oreillyschool.com/html5_responsive_design/images/Brazil_Butterfly_m.jpg">
                    <source media="(max-width: 640px)"
                            srcset="https://courses.oreillyschool.com/html5_responsive_design/images/Brazil_Butterfly_sm.jpg">
                    <img src="https://courses.oreillyschool.com/html5_responsive_design/images/Brazil_Butterfly_sm.jpg"
                         alt="photo of mountains and rainbow">
                </picture>
                <img data-src="https://courses.oreillyschool.com/html5_responsive_design/images/Brazil_Butterfly" alt="photo of mountains and rainbow">
                <figcaption>Exciting wildlife</figcaption>
            </figure>
        </div>
        <div class="photo-with-caption">
            <figure>
                <picture>
                    <source media="(min-width: 641px)"
                            srcset="https://courses.oreillyschool.com/html5_responsive_design/images/Iceland_Road_m.jpg">
                    <source media="(max-width: 640px)"
                            srcset="https://courses.oreillyschool.com/html5_responsive_design/images/Iceland_Road_sm.jpg">
                    <img src="https://courses.oreillyschool.com/html5_responsive_design/images/Iceland_Road_sm.jpg"
                         alt="photo of mountains and rainbow">
                </picture>
                <img data-src="https://courses.oreillyschool.com/html5_responsive_design/images/Iceland_Road" alt="photo of road in snow">
                <figcaption>Spectacular landscapes</figcaption>
            </figure>
        </div>
        <div class="photo-with-caption">
            <figure>
                <picture>
                    <source media="(min-width: 641px)"
                            srcset="https://courses.oreillyschool.com/html5_responsive_design/images/Alaska_View_m.jpg">
                    <source media="(max-width: 640px)"
                            srcset="https://courses.oreillyschool.com/html5_responsive_design/images/Alaska_View_sm.jpg">
                    <img src="https://courses.oreillyschool.com/html5_responsive_design/images/Alaska_View_sm.jpg"
                         alt="photo of mountains and rainbow">
                </picture>
                <img data-src="https://courses.oreillyschool.com/html5_responsive_design/images/Alaska_View" alt="photo of rainbow and waterfall">
                <figcaption>Incredible views</figcaption>
            </figure>
        </div>
    </div>
</section>             
...             

Change your CSS back too:

Update the CSS in your main.css file:
section#photos div.photo-with-caption img {
    width: 100%;
}
section#photos div.photo-with-caption picture {
    width: 100%;
}                        

Now, update the JavaScript in main.js to use plain JavaScript. (We'll be using more jQuery later, so it's up to you if you want to change your code at this point; if you don't, just take a look at the code below so you know how you can implement this feature with either jQuery or plain JavaScript). Edit your main.js file and modify your JavaScript code:

CODE TO TYPE:
// We haven't deleted the jQuery code below; rather we've just commented it out
// in case you want to go back to using it later.
/*
$(document).ready(function () {

    // returns a list of images
    var $imgs = $("#photos .photo-with-caption figure img");

    if ($(window).width() > 640) {
        $imgs.each(function(i, theImage) {
            theImage.src = $(theImage).data("src") + "_m.jpg";
        });
    } else {
        $imgs.each(function(i, theImage) {
            theImage.src = $(theImage).data("src") + "_sm.jpg";
        });
    }
});
*/

window.onload = function() {
    var images = document.querySelectorAll("#photos .photo-with-caption figure img");
    if (!images) {
        return;
    }

    for (var i = 0; i < images.length; i++) {
        var theImage = images[i];
        var url = theImage.getAttribute("data-src");
        if (window.innerWidth > 640) {
            url = url + "_m.jpg";
        } else {
            url = url + "_sm.jpg";
        }
        theImage.src = url;
    }
};             

If you're not going to use jQuery, then you can delete or comment out the line in your index.html that links to jQuery. Modify your index.html file again as shown:

CODE TO TYPE:
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<title>Dandelion Tours</title>

<!--
<script src="http://code.jquery.com/jquery-latest.min.js"></script>
-->
<script src="main.js"></script>             

and your index.html. Your images load as before, but now, you are once again using the data-src attribute and some JavaScript to load the images. Notice we just commented out the link to jQuery for now because we'll be using it again later. Removing the link to jQuery will reduce the total amount of data to download from the server by a bit (check the Network tab in the console to see the difference). However, notice that jQuery is a fairly common library on the internet. Chances are your browser may already have this file cached! Even if it doesn't, the browser will cache it after the first download, so unlike images which are different from page to page in your site, the jQuery library will only need to be downloaded once per session (this is true for both desktop and mobile browsers).

High Density Pixel (Retina) Displays

You may have heard about "retina displays," "high DPI screens," or "high density pixel displays" recently. All these terms basically mean the same thing: screens with higher pixel density than was standard for a long time on computers (in particular, CRT desktop monitors).

What is "pixel density?" It's the number of pixels that can fit into a distance. For instance, the iPhone 4s has a screen resolution of 640 pixels by 960 pixels. The phone screen is about 2 inches across, so those 640 pixels across fit into 2 inches. Now imagine an old SVGA CRT monitor from the 1990s. Those screens typically had a resolution of 800 pixels across, and the monitors were about 9-10 inches across. A pixel on the SVGA display is much bigger than a pixel on the iPhone display. We compute "pixels per inch" or "dots per inch" (DPI) by measuring the number of pixels that fit into a square inch of display. For the iPhone 4s, the pixels per inch is 326. For that old SVGA monitor, the pixels per inch is only about 72. We say that a high density display (or "high DPI" display) is a device with a pixel display density of 200 pixels per inch or more.

Note We write "pixels per inch" as "ppi" and "dots per inch" as "dpi." This comes from the old days of graphic design when we printed our designs, and we measured resolution in terms of the number of ink dots a printer could put onto a square inch of paper.
Note The term "retina display" is actually an Apple-specific term used initially for the iPhone, and now used on other Apple devices with high-density displays. Other companies tend not to use the term "retina display" to avoid infringing on Apple's trademarks.

So how do high-density displays affect your design? The first thing to remember is that users are going to be viewing your web page on vastly different displays with vastly different capabilities. An 800 x 600 image in your page will look much bigger on an older monitor, or a monitor that has been set to a lower screen resolution, than it will on a high-resolution screen. As you've seen, we can address some issues of varying screen sizes using media queries. However, on mobile, these issues are a little more complex because the high pixel densities of modern phones and tablets mean that you have to choose between a crisp but big image, and a less-crisp (potentially slightly blurry) but smaller and faster-to-download image. The varying sizes of mobile device displays also mean that you should rarely if ever design your page for a specific resolution or a specific pixel density. We picked a break point of 640 pixels for our design to switch from the wide view to the narrow view. Why 640 pixels? Because until recently that is a fairly common resolution width for some of the devices on the market. However, that is changing rapidly, especially as displays get better and better with more resolution, so using 640 pixels simply because it matches the width of a specific device isn't necessarily the best choice.

One thing to know about mobile devices is that some, like Apple's, use a trick called the "device pixel ratio" to make sure that the text of web pages doesn't become too small to see. Remember when we added the viewport <meta> tag to the index.html file?

OBSERVE:
<meta name="viewport" content="width=device-width, initial-scale=1, user-scalable=no">            

This tag ensures that devices will scale their displays using the device pixel ratio assigned to that device. In the case of the iPhone 4s, the device pixel ratio is 2. That means that, when the viewport width is set to "device-width", the page is displayed as if it is 320 pixels wide instead of 640 pixels (640 divided by the device pixel ratio of 2 is 320). When you display text on a screen that has twice the pixels in width and height, you're actually quadrupling the number of pixels used to display the text, so letters looks crisp and sharp. Here's how it works:

Text scales well in this situation because of how the browser renders text in the display. The letters remain the same size visually (because they are displaying as if the screen was 320 pixels wide), but they actually have four times the pixels for that display. However, if you have a 320-pixel image that you scale up by 2 (to make the image look like it's 320 pixels, but on a 640-pixel display), that image is going to look blurry. Why? Because the rendering engine has to double every pixel for both the width and the height of the image, essentially quadrupling every pixel in the image:

So does that mean you need to create separate images for all the difference devices, and all their different device pixel ratios? If you want your images to look perfect on every device, yes! But that's unrealistic. At some point, we have to compromise and choose a design that is going to work well across a variety of devices, using media queries and JavaScript to help load images that are appropriate for a group of devices that are similar in size and resolution.

As you can probably see, that can get incredibly complex, depending on how far you go. We're going to keep our design fairly simple, but there are always tradeoffs. For instance, we chose to create images that are 640 pixels wide for the narrow design for Dandelion Tours, instead of 320 pixels, so the images will be crisp on a device like the iPhone 4s.

We chose to use 640 pixels as our breakpoint. However, newer devices like the iPhone 6 and iPhone 6+, along with various other newer Android phones, have larger resolutions, so the width is greater than 640 pixels across. For instance, the iPhone 6 is 750w x 1334h with a device pixel ratio of 2, and iPhone 6+ is 1242w x 2208h with a device pixel ratio of 3, so images that are 640 pixels across won't look crisp on these phones. Also, notice that if you turn your iPhone 6 or iPhone 6+ into landscape mode, you will switch into the wide view from the narrow view (can you see why?), which you might not want.

The Width We Use in Media Queries and the Width of a Device

It's important to notice here that the width we specify in the media queries to select style is based on the width of a device and the device pixel ratio. We've been using 640 pixels as our breakpoint so that all current iPhone and Android mobile phones use the narrow view, even including the iPhone 6+ in portrait mode (1242/3 = 414, so pages on the iPhone 6+ display as if the screen was 414 pixels wide, well below our 640-pixel breakpoint). Using 640 pixels means the narrow view will be used for landscape mode for the iPhone 4, 4s, 5, and 5s, but not the 6 and 6s. Similarly, some Android devices will be wider than 640 pixels in landscape mode. However, if you load Dandelion Tours into your iPad, even an older model iPad, like the iPad 2, you'll see the wide view of the page, because the iPad 2 has a width of 768 pixels and a device pixel ratio of 1 (so the device width and the apparent width are the same).

Until recently there was no way for you to detect the device pixel ratio or resolution of a display using JavaScript or CSS media queries. However, these features are being added, which means we'll have more flexibility in our designs in the future. For instance, the <picture> element allows you to specify more than one image in the srcset attribute of the <source> element, with an optional device pixel ratio for each image:

OBSERVE:
<picture>
    <source media="(max-width: 640px)"
        srcset="image1.jpg, image2.jpg 2x">
    ...
</picture>

This says "load image2.jpg if the device has an device pixel ratio of 2; otherwise load image1.jpg."

Unfortunately, various devices (for example, Apple and Android) don't yet have a common way of specifying device pixel ratio or resolution in CSS and JavaScript, so we're still very much in the development stages of the technology for dealing with the wide variety of displays that are out there. However, you can probably imagine we'd like to be able to write media queries based on both the resolution of the display and the width and device pixel ratio of the device. Support for these features is spotty and a bit inconsistent at the moment, but we're getting there.

If you're interested in delving more deeply into this incredibly complex and rapidly evolving topic, check out the links below:

Setting breakpoints in a world full of different devices

Now that there are so many devices on the market with such a huge variety of displays and sizes, it doesn't really make much sense to set breakpoints based on the width of a specific phone. We've been using 640 pixels because that size corresponds to a set of devices that were on the market for a while. Picking a position for a breakpoint (or perhaps more than one) these days is a lot more complex.

Some developers advocate for choosing a breakpoint based purely on how your design looks. Create a design, say a narrow design, and expand your browser until that design stops looking nice and is no longer easy to use or easy to read. That's where a breakpoint should go. You can do this one or more times depending on how many breakpoints you think you need for your design.

With images specifically, it's important to keep in mind the tradeoff between an image size that will look good on high density displays and a size that will be quick to download on mobile devices. Some images are less important than others, so you can get away with either eliminating the download for the mobile version of a site (using media queries for background images, or using JavaScript to load only the important images), or using lower-resolution images. Ultimately, it's up to you as the page designer and implementor to make these decisions.

Next lesson we'll look at more design choices we might make for mobile devices (narrower screens). Before you go on, get some practice thinking about responsive images by doing the quizzes and the project.