PrIma: Progressive Images in Jekyll 

Published 2017-06-19 on flowinho.io

category | code jekyll images javascript

This is something i wanted to have on the first iteration of this site for a long time. But as you might tell from the 6 months gap in my blog posts, i had to prioritize personal matters over the advancement of this website. This fog cleared and i’m now glad to announce: this blog finally features progressive image loading!

Please keep in mind: i’m still a rookie in web-development. If you have any comments to make, or improvements to suggest, please contact me. Thank you in advance.

What is progressive image loading?

Progressive image loading is a technique that displays a placeholder of an embedded picture to the user while loading the full-sized picture in the background. Over time, these placeholders have progressed from simple text, grey rectangles and small gifs to a blurred version of the original photo.

This guide aims to implement progressive image loading into jekyll-based blogs without causing too much friction, preferably by including only a single line of code inside the markdown files that are being rendered into images once jekyll compiles it.

The masterplan of progressive image loading

The way this guide implements progressive image-loading is inspired by the famous 200-byte-image preloading of Facebook. It”s pretty simple, yet very effective:

  • Use a heavily scaled down version of the original image, e.g. 32x18 pixels in size.
  • Display it to the user in the size the original image would take when fully loaded.
  • Blur things out, to prevent ackward looks.
  • Once loaded, fade the original, high resolution image back in.

That’s it.

There are tons of ways to achieve this. CSS only, javascript only, jpg-streaming and god knows what else. For the sake of simplicity, i’ll use the javascript + css-animations approach.

Actually implementing it

Beforehand: I’ll try to not explain most parts of the code in separate. Most information will be stored inside the comments of the specific code, as this helps understand how each commented line works.

Allright, let’s have a look at the HTML i’d like to use:

!<-- The data-large property supplies the URL to the original, full sized image -->
<div class ="placeholder" data-large="http://someurl.com/someimage.jpg">

    !<-- The img-small property supplies the URL to the scaled down placeholder -->
    <img src="http://someurl.com/someimage.jpg@xs.jpg" class="img-small"/>

    !<-- Add a 2/3 padding to stick the div to the original position -->
    <div style="padding-bottom: 66.6%;"></div>
</div>


  • This code places the very tiny placeholder image inside an <img> tag to trigger its immediate download. Since its very small, it should not affect loading time of your site that much.
  • The placeholder div stores the URL to the original, full-sized image file. This will be needed by the javascript, to add the actual image to the div.
  • A seperate div adding a padding of 2/3 is appended at the bottom of the img-file to retain the original aspect ratio of the image.

Simple and straight forward. Let’s have a look at the CSS:

.placeholder
{
  background-color: $content-title-background-color; /* Style for your own needs.*/
  background-size: cover;
  background-repeat: no-repeat;
  position: relative;
  overflow: hidden;
}

/* This is the container of the full-sized image*/
.placeholder img
{
  position: absolute;
  opacity: 0; /* hide it */
  top: 0;
  left: 0;
  width: 100%;
  transition: opacity 1s linear; /* Defining the transition animation once the image is loaded*/
}

/* The container of the full-sized image, in it's loaded state*/
.placeholder img.loaded
{
  opacity: 1;
}

/* The down-sized image, blurred out*/
.img-small
{
  filter: blur(50px);
  transform: scale(1);
}


And finally the javascript shudder:

// When loading the site...
window.onload = function() {

  // Find the elements that are marked as placeholder and img-small and store their reference
  var placeholder = document.querySelector('.placeholder'),
      small = placeholder.querySelector('.img-small')

  var img = new Image();
  img.src = small.src;
  img.onload = function () {
   small.classList.add('loaded'); // Change it"s opacity to 1.0 to display it immediately
  };

  var imgLarge = new Image();
  imgLarge.src = placeholder.dataset.large;
  imgLarge.onload = function () {
    imgLarge.classList.add('loaded'); // Display the full-res image once it"s loaded
  };
  placeholder.appendChild(imgLarge); // Add the loaded image to the div, to actually insert it into the website.
}


This code is enough to already make it work. Please keep in mind, this approach has one major downside: you need to provide two versions of the same image, one full-res, one scaled down version. When referencing too many images, this can turn out to be a burdern.


Creating a liquid tag from it

This blog runs on GitHub Pages (thank you for that, GitHub) and therefor i cannot utilize ruby-plugins to achieve a convenient way of adding progressive-images as a single-line insert. Thankfully, liquid-tags exist.

Be sure to store the above HTML code inside an html file in your _includes folder of jekyll. Our goal is to have

{% include prima.html image = "/images/code/progressive-images-in-jekyll/title" %}

being rendered to

<div class ="placeholder" data-large="https://flowinho.io/images/code/progressive-images-in-jekyll/title.jpg">
    <img src="https://flowinho.io/images/code/progressive-images-in-jekyll/title@xs.jpg" class="img-small"/>
    <div style="padding-bottom: 66.6%;"></div>
</div>  


This is done by accessing the includes properties and have liquid replace them inside the actually rendered HTML code.

<div class ="placeholder" data-large="{{ site.url }}{{ include.image }}.jpg">
    <img src="{{ site.url }}{{ include.image }}@xs.jpg" class="img-small"/>
    <div style="padding-bottom: 66.6%;"></div>
</div>


Please keep in mind: these properties are case-sensitive. This code additionally references the scaled-down version by appending @xs to the provided filename. In the current form of this code, the functionality is limited to “jpg” filetypes. However, adding support for multiple filetypes can be achieved by providing a separate type property to the liquid-include.

That’s it! All you need to do know is create two versions of your image, reference their locations, and your images should be loaded visually pleasing.

If you have any comments or feedback, please please contact me at contact [at] flowinho [dot] com, especially regarding code-matters.

Have a wonderful day!