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
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.
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.