Journey to Creating a Long List

OK it’s not really a journey but these were the steps and iterations that I have taken to get to the final product. It’s a project I have worked on recently at Article that shows a list of images pulled from social medias that includes our products. This gives people an inspiration of what the community is doing with their furnitures in their homes. People need inspiration, they need visuals of what they can do with our products and these beautiful pictures can do just that and potentially drive our conversion rates. That’s the story of this task and from technology side: this module needs to have good performance, page load time cannot increase too much, and have good user experience that adapts to Article’s style. I love challenges 😉.

Browsing these User Generated Contents Browsing these User Generated Contents

The first part of task is to create a list of these images that I acquire from the AJAX response. To avoid long wait time and heavy pay load, I paginate these data and obtain just 25 images per response. Taking the screen size in consideration, this is the sweet spot for me to show just enough images before it has to fetch the next batch. As user continues to scroll through these images and almost reaches the end, it will fetch next page of data, append those to the end, and continue and so on. This is working fine when we have small dataset but some products have more than 400 images, the whole UI starts to feel sluggish which is very notable on low-end devices. The main reason is the need for reflow. Whenever we update the content of the DOM, insert new one, or remove one, a reflow will occur as browser needs to recalculate the position and dimension of these DOM elements. Although it’s not too obvious on high-end devices, we front-end developers still have the responsibility to keep it performant on low-end to keep it performing blazingly fast plus not dropping frames. Here’s a great article on reflow and repaint.

The solution that is most picked up is by something called Virtual Scroll. It’s just a general name that describes what’s being done and some people call it Virtual List. Idea is that if there are, for example, 100 images needed to be displayed in that list, instead of adding all of those images to the DOM immediately, we create a smaller number of DOMs that hold enough images to show on the screen. So I created 25 divs that hold these data and while user scrolls through, these images will start rotating so the 1st div that was holding 1st image will now hold 3rd image. At any given point, there will be a maximum of 25 DOMs present in the list.

Inspecting elements when browsing through images Inspecting elements when browsing through images

You can see from the image above that it’s not adding new images to the list but modifying the existing ones. If you use any modern JavaScript frameworks, nearly all of them use some variations of Virtual DOM in which they make comparison, make update, and apply update to the real DOM. Since these changes only include content updates and not size changes because all of these images / blocks have the same width, height, paddings, and margins, the list performs very well and you probably don’t notice any of these underlying implementations. Fun fact, Instagram is also doing this.

Expand the gallery and continue browsing them Expand the gallery and continue browsing them

One other optimization I make is to lazy load these images. Frankly it’s a lot of images that browser needs to download. However, this User Generated Content module is not above the fold, meaning it’s not immediately visible to the user when first lands on the page. It should not fight for the network bandwidth to download these images so I choose to lazy load them. What it means is that these images don’t load immediately but they load when they are in the viewport. You could use third party library like lazysizes or write your own. At the time of writing, I chose to write my own that adopts Intersection Observer. It allows the developer to set a an array of percentage that defines what you would like to do when certain amount of DOM is visible in the viewport. I set it to 1% and it would update the img src attribute to begin downloading the image. Not only this, I also load these images when load event fires on Window because it means all of the important stuffs have been loaded and I can start downloading these images for smoother experience.

These optimizations sound pretty minor but they prove to have huge impact, or no impact at all. After this User Generated Content module is online, by average it adds 30ms to overall load time and still keep it under 3 sec when on 3G. This has inspired many people in what they can do with our fine furnitures without sacrificing any performance. This is one of the fun and important projects I have participated in and I am very proud of the final outcome. So the takeaways here are that try to evaluate the necessity of a virtual list for your potentially long list and lazy load images when they are not immediately accessible to the users.

If you like what you read, give it a clap and follow me on Medium and Twitter. Leave a comment below to let me know what you like or dislike about long list.