10 January 2021- implementing Isotope using ImagesLoaded for synchronisation

The following thoughts are pitched squarely at those who are quite new to Web development, or just interested in learning more about it. If you are already a seasoned Web Developer, then you probably aren't going to find it very interesting or enlightening.

Isotope is a fantastic commercial JavaScript library which helps you to create highly functional tile-based layouts with dynamic sorting and filtering. It is free to use for non-commercial websites under the GNU GPL license V3. I recently incorporated it, in conjunction with several other libraries, to jazz up my Blog page. You can find it HERE, where the aforementioned seasoned Web Developers will also find far more detailed information by David Desandro, the founder and lead developer of the software house that made Isotope

Having finished my own development, or so I thought, I noticed a bug which arose when I cleared my browser cache and hence viewed the page as if for the first time. The problem was that rather than all being lined up nicely as they should, some of the tiles were misplaced or overlapping each other. Viewing the page any number of times subsequently, it worked just fine. Therefore, it might have been quite easy for me to miss the bug, but all my site users would have seen it the first time they viewed the page, and first impressions count. So, the moral of the story is, always test your page with an empty cache before pushing any changes to the Prod site !

As with debugging any software problem, it always pays to start by thinking about the use case i.e. in what circumstances does the problem manifest itself. If it happens in some circumstances and not others, this can start to give you clues about the root cause. In this case the starting point was obvious. First view - problem. Second and subsequent views - no problem. The WWW is basically stateless, so what is the difference between the first view of a page and the second ? The Browser Cache !

For those readers who really are just beginning with Web Development, all Web Browsers cache (make a local copy of) the resources downloaded by your page. That's things like images, scripts and css, although the precise details vary between different browsers and browser settings. Doing this means that subsequent views of the page are faster and use less network/internet bandwidth

The next question of course is why that makes a difference. Again a little deduction, what's the practical difference between resources being pulled from the cache vs downloaded from the Internet ? The former is much faster than the latter (which is the main reason it's done, of course). So, the issue would appear to be one of TIMING.

So, why would a page or a component thereon behave differently if resources loaded slowly as opposed to quickly ? To realise that, you start to need to know a bit more about how JScript actually works. An important part of the very power of JavaScript is that it is very Asynchronous. If your experience of programming thus far involves lines of code that are executed one after the other in a predictable order, JS is not like that. It's event-driven, meaning that different sequences of code are triggered by events (for example, an image finishes loading, or the user clicks an element in the page) and run (at least apparently) at the same time in parallel. The order in which they run, or complete, cannot reliably be predicted in advance. What that means is that in order to have a robust system, those individual sequences of code need to be independent of each other - function a can run successfully before, after or while function b is running, and vice versa.

The other classic giveaway of a timing/synchronisation issue was that in fact the precise nature of the mis-alignment could actually vary from test to test.

The way that Isotope works is that you lay out your HTML with the necessary elements - basically a container containing sub-containers for the Isotope toolbar (the selectable list of categories) and each individual tile item, then set up your JScript code to "isotope" them, meaning to size and lay everything out nicely. You arrange for the code to redo this each time the filter selection changes. My problem was arising because of when in the sequence of events that initial layout was happening, which was basically before all the images had finished loading. Because the size and layout of course depends on what is inside each tile, if the final content is not actually there yet then obviously it cannot do a proper job of this. I needed to look again at my code to ensure this happened AFTER I was sure that all the content had loaded. Initially, it was just happening as the last operation of the "setup" code that was run when the page loaded. When the images were already in the cache, they loaded quickly enough for this to have completed by the time the layout function ran. When they were not, the layout code was running before some or all of them had loaded.

There is an event fired when the window has finished loading, meaning that every image, JS file, CSS file and so on has completed loading, which sounds like just what I wanted. However, the Metafizzy guys strongly advise that the window load event should only be used as a last resort and that actually you should use a more tightly-scoped event. Effectively, one which is triggered when only the components that actually matter have all loaded, rather than all components. They suggest using another Desandro JavaScript library called imagesloaded, which as the name suggests will detect when all images within a specified container have loaded. In the case of this specific page, it doesn't make much difference because the only things really on the page are the Isotope tiles. However, we should always try to build our code to be flexible in anticipation of future changes or re-use, so I did just this. The final code for doing the initial layout, and changing it when a filter is selected, looks like this:

To explain the above, this code runs at the time that the page is loaded. For each container with the class "isotope-wrapper" (most likely there will only be one, but you could have more than one on a page), it gets references to ($isotope) the container inside it of class "isotope box" and ($filterCheckboxes) the set of "input" controls with type "radio". These are used within the function "applyFilter" which is then defined. This function assigns a variable with either "*" meaning all or "[data-type="..."]" depending on which radiobutton is currently checked, then makes a call to the isotope function via the reference to the "isotope-box" container, thus updating which items are displayed. The function "applyFilter" is then attached to the change event of the current "isotope-wrapper", meaning the function gets called when the user clicks a radiobutton. The remaining piece of code that is executed within this "for each isotop-wrapper" is a call to imagesLoaded, passing a callback function, which will get executed when imagesLoaded determines that all images have been loaded. The callback function makes a call to the isotope function causing it to lay out the boxes. Because at this point we know all images have been loaded, we can be sure that the layout will be done correctly.