Skip to content

Latest commit

 

History

History
237 lines (209 loc) · 9.66 KB

lazy-loading-images.md

File metadata and controls

237 lines (209 loc) · 9.66 KB
layout title desc hide_markbot hide_show_for_marks extra_tutorials goal fork steps
lesson
Lazy-loading images
Look at creating a fast website with lots of images, some loaded without JavaScript & more loaded with JavaScript.
true
true
title url highlight
Progressive enhancement
progressive-enhancement
true
title url
Accessibility
accessibility
title url
Accessibility cheat sheet
accessibility-cheat-sheet
title url
Accessibility checklist
accessibility-checklist
title url
JavaScript, jQuery & accessibility
javascript-jquery-accessibility
title url
PE & a11y slide deck
/courses/web-dev-5/pe-a11y/
no_image before notes
true
We’re going to look at making a basic gallery user experience without JavaScript then enhance it with some JavaScript to substantially improve the loading and rendering performance. **This lesson is completely optional—do it only if you’re interested in making your portfolio much faster.** The idea is to make the website load really quickly—by default only showing a few images. - When JavaScript isn’t enabled we use the `<noscript>` tag to show more images. - When JavaScript is enabled we go and download more images to the page after it has already loaded.
label text
Type it, type it real good
Remember the purpose of this lesson is to type the code out yourself—build up that muscle memory in your fingers!
title before folders after notes
Set up the project
The basic starter repo that has the CSS files & images we need inside it—we’re going to work from that.
label type
lazy-loading-images
folder
label type indent
_data
folder
1
label indent
images.yml
2
label type indent notes
css
folder
1
Lots of CSS in this folder—we’re not going to touch it
label indent
_config.yml
1
label type indent notes
images
folder
1
Lots of images in this folder
label indent
index.html
1
**Start Jekyll in this folder.** Open the `lazy-loading-images` into your code editor.
label text
Naming conventions
Don’t forget to follow the [naming conventions](/topics/naming-paths-cheat-sheet/#naming-conventions).
title before code_lang code_file code lines after
Prioritize the images
To start we need to group the images into 3 categories: 1. **Critical images** — these images will always load 2. **Must-have images** — these images should always load 3. **Non-critical images** — it’d be nice if these images loaded *Of course—the images themselves may just fail to load because of slow Internet—there’s nothing we can do to mitigate that.* For this exercise, we’ll say the first 2 images are critical, the next 2 are must-have, and the rest are non-critical images. Let’s adjust our HTML to group the images into our three categories.
html
index.html
⋮ </head> <body> <div class="grid"> {% for img in site.data.images limit:2 %} <div class="critical-img unit xs-1 m-1-2"> <div class="embed embed-16by9"> <img class="embed-item" src="images/{{img}}" alt=""> </div> </div> {% endfor %} {% for img in site.data.images offset:2 limit:2 %} <div class="non-critical-img unit xs-1 m-1-2"> <div class="embed embed-16by9"> <img class="embed-item" data-src="images/{{img}}" alt="" hidden> <noscript> <img class="embed-item" src="images/{{img}}" alt=""> </noscript> </div> </div> {% endfor %} {% for img in site.data.images offset:4 %} <div class="non-critical-img unit xs-1 m-1-2" hidden> <div class="embed embed-16by9"> <img class="embed-item" data-src="images/{{img}}" alt=""> </div> </div> {% endfor %} </div> </body> </html>
num fade
2-3
true
num fade
32-33
true
num text
6
In this loop we’re limiting the output to the first two images. These are the “critical” images—they will always be shown.
num text
7
There’s a new class here, `.critical-img`, to help us remember the importance of this image.
num text
13
This is a duplicate of the previous loop with a few small modifications: - It has `offset:2`, meaning it will start at the item with the index of 2, aka the 3rd item. - It has `limit:2`, again, here we only want to spit two images out—these are our “must-have” images.
num text
14
There’s new class here, `.non-critical-img`, to denote these as being “non critical” images—we’ll be using this in JavaScript later.
num text
16
Notice how the `src="…"` attribute doesn’t exist any more, now it’s `data-src="…"`, this is to prevent the image from downloading. It can’t download unless it has an `src="…"` attribute. It also has the `hidden` attribute so that it doesn’t accidentally get shown.
num text
17-19
The `<noscript>` tag is here to force the image to be visible if JavaScript isn’t available. Inside the `<noscript>` tag is a standard `<img src="…">` tag that will only get triggered if JavaScript is disabled in the browser.
num text
23
We’ll start outputting the images with the fifth one.
num text
24
Here we have a `hidden` attribute so this whole grid unit isn’t shown until the JavaScript executes.
num text
26
Again we’re using the `data-src="…"` attribute to prevent these images from loading.
In the code above we have three loops, each has a specific purpose and ties to the our image categories: 1. The images in the first loop will always show. 2. The images in the second loop will show with or without JavaScript, essentially they will also *always* show. 3. The images in the third loop will only show when the JavaScript is triggered. **Why bother having the second loop at all if they’re always going to show?** To help the page load faster. Most (almost all) browsers have JavaScript enabled, so these images can be triggered later with JavaScript. The page will load super quick, showing only the images in the first loop, then the JavaScript will kick in and start downloading the rest—but our user will already have a nice, complete page.
title before multi_code after
Lazy load non-critical images with JavaScript
Now for a little titch of JavaScript to make the whole thing work together.
code_before code_lang code_file code lines
First make a new JS file: `js/image-loader.js` *We’re not going to use jQuery for this code because it will just slow our website down. There’s so little JavaScript that jQuery adds a massive, unnecessary overhead.*
js
js/image-loader.js
window.addEventListener('load', function (e) { var imgs = document.querySelectorAll('.non-critical-img'); [].forEach.call(imgs, function (img) { var imgTag = img.querySelector('img'); imgTag.src = imgTag.dataset.src; imgTag.removeAttribute('hidden'); img.removeAttribute('hidden'); }); });
num text
1
Wait for the website to finish loading before this JavaScript is triggered.
num text
2
Find all the elements on the page with the class `.non-critical-img`
num text
4
Start a loop, that will traverse over all those non-critical images that JavaScript found.
num text
5
Inside the “non-critical” element, find the `<img>` tag itself.
num text
7
Convert the `data-src="…"` attribute into a `src="…"` attribute so the image will start downloading.
num text
8-9
Remove the `hidden` attributes from the `<img>` and the surrounding `<div>` tags.
code_before code_lang code_file code lines
Now we need to connect the JavaScript to our HTML file. Instead of using the standard `<script src="…">` tag, we’re going to embed the JavaScript right on the page. This will help mitigate the possibility of the external JavaScript file not downloading.
html
index.html
⋮ {% endfor %} </div> <script>{% include_relative js/image-loader.js %}</script> </body> </html>
num fade
2-3
true
num fade
6-7
true
num text
5
We’ll use Jekyll’s `include_relative` function to read all the JavaScript from our file and output it into the page itself.
**That’s it. Give it a try in your browser with JavaScript on & off and with the network speed throttle to see how it significantly improves performance.**