The brain is a muscle, and as all muscles, it needs regular exercise to keep sharp.

Thats why I decided to take very old (but efficient) web optimization technique and implement it in new crazy way. You most likely heard about loading images (and other resources) on demand – which is not only a common sense, but also a good way to speedup initial page load. Usually it is implemented via JavaScript, but today I’ll share with you how you can achieve the same effect by using pure CSS.
The problem
Few more words about the problem: we have a set of images to show to a user, but only some of them will be visible to the person after initial page load. The rest of the images will be below the fold and user may never scroll down. So it make sense to load those images after user start scrolling the page down.
Prevent images to load
First problem we need to solve is how to prevent browser from loading all the images from start? I’ve tried several approaches here, but the one which worked for me is to have images as background images for some div elements and set its parent display: none. Almost all modern browsers are smart enough to do not load background images for hidden elements. When we decide its time to load/show the image we will set display: block to its parent.
When to start load images?
Next let’s think about what styles or CSS selectors are changes whiles user scrolls? I.e. how can we catch the moment when to start particular image request? My first approach was to use position: sticky, as it behaves like position: relative within its parent, until a given offset threshold is met in the viewport (in other words user scrolled to some position). After this point element start behave as position: fixed. Unfortunately, surrounding elements doesn’t note the change and hold their position and styling.
Then I’ve noted that user’s cursor hover different elements while scrolling the page. So, what if we put several page-wide div elements (with class .hoverer) on a page and start loading the image when user hover it? It may work, but there is no guarantee that user will hover all div elements on first page (so we can load images on a second page). This leads us to the next idea – what if we load all images for first two pages from very beginning. Then put div.hoverer on top of images on a second page. Once user will hover it – we will load images for the third page. Third page will also contain div.hoverer for fourth page and so on.
Next question is how can we determine what is the size of the page, so we can load first two screens of images initially? Unfortunately there is no way to do so, but we can make them big enough to fit majority of the screens (assuming all images are in equal, known in advance size). Examine HTML/CSS/Result tabs below in order to get a feeling of how it will work :
Keep image visible when cursor left
But here is a problem, even if we show/load image when user hover related div.hoverer, the image will disappear when user remove cursor from the div.hoverer. So how can we preserve hover state? My first idea was to have div.hoverer elements overlapping each other, so when you position cursor over one element, it will be positioned over all elements we’ve hovered already. But hover state is not propagated over the elements, i.e. only top element will receive :hover state. And even pointer-events: none won’t help us in this situation.
Okay, what if just delay appearance of display: none on image parent’s element? I’ve tried to use keyframes animation, but it didn’t work out for me. Fortunately for me, transition does exactly what I need. Instead of another paragraph of explanation – checkout this demo.
But guess what, here is another problem we can’t delay display: block state as it is not animatable property in CSS. And transitions works only with animatable properties. Another dead end? I don’t think so. Here is another trick we can do – let’s transition width of the element and let’s apply media query to the element. In media query we can specify relationship between element width and its visibility. Oh, but it is not allowed to set media query for particular HtmlElement. Media queries are applicable to viewport only… Then we have to put images into separate viewports, i.e. iframes. We can use iframe.srcdoc in order to avoid to have separate url for each image we might have.
Results and some comments
Here is our final result (make your viewport height equal to 5 pumpkins in order to get the best experience). The demo works in Webkit only because of iframe.srcdoc support, but you can easily make it work cross-browser if put html of iframes into separate files.
Wow, this is pretty long path to somewhat working solution. Would I even recommend to use it on production – no way. This is definitely not a best practice, but is an interesting task and some parts of it might be applicable in real-world web applications. I hope you had as much fun as I had, while working on this challenge. And if you have any ideas of how to improve my approach – please do not hesitate to contact me.
UPDATE:
Looks like there is a way to solve a problem with first two screens: we may have div element of viewport size (by using height: 100vh;). The element will contain set of images (wrapped into iframe as in my demo). The set should be big enough to fill frist two screens. And we will have a set of media queries to show different subsets of images depending on viewport height.


Pingback: Loading Images on Demand with Pure CSS | Flippin' Awesome