Layout thrashing happens when we force the browser to measure the DOM multiple times during a synchronous process. This can have major performance consequences. In this lesson we will see the case in which measuring a dynamic value from the DOM can cause layout thrashing - and it can be solved by debouncing the measuring function.
Instructor: [0:01] In this app we can add blue boxes on screen. We can add a single box, or a box of thousand boxes. We now have to add a feature to the app to submit an event on every change in the boxes wrapper's height.
[0:16] We have a function that submit new height. It submits an event new wrapper height with the currently measured value. I'll add an event listener so that we'll log the height change. It's a native event listener on the wrapper itself with a call back that logs the event's detail which is the height.
[0:39] I'll hit the submit new event function after we add the box. I refresh the page and trigger the box's edition. We can see it log the heights as expected.
[0:52] I'll refresh again and start recording, add the boxes. When it's done, I stopped the recording. We now see the red line that hints in a performance issue. As well as a lot of purple in the timeline inside the yellow part, which is a big hint for the layout reflow. The whole process took more than two seconds.
[1:16] At the bottom of the flame chart, we can see the layout thrashing. A lot of purple marked with red underneath yellow JavaScript. A way to solve this would be to wrap the measuring function with requestAnimationFrame. Refresh the page, start recording, add the 1000 boxes again.
[1:43] Looking at the results, we see that we've eliminated the layout thrashing. There is no purple layout calculation inside our yellow JavaScript. In order to see the problem in this case, let's look at the log in the console.
[1:58] The same result repeated 1000 times and this was not what we intended. Although, we reduced the time it took our code to render from two seconds to less than 100 milliseconds, logically, there is no sense in repeating the same event with the same data over and over again.
[2:16] One solution for this is the debounce technique. A debounce function is a function that is called with a delay. If during the delay period the function is called again, the timer resets and another delay count starts. The process continues until the delay timer runs out. Our function runs only once even though it was called many times.
[2:39] In this code, I'm using lodash debounce. I send it to the function and the delay in milliseconds and it returns the debounce function. I'll replace their requestAnimationFrame with the debounce function refresh, start recording and add the boxes again.
[2:58] In the results, we can see not a lot has happened. The whole process took less than 40 milliseconds. The result was logged only once.
[3:11] To summarize, we started with adding an event emitter that send the current measured heights synchronously. We saw it creates layout thrashing, so we try to wrap our function with requestAnimationFrame.
[3:26] In that case, we saw that even though we removed the layout thrashing, we are emitting the same event 1000 times. We move on to debounce our function with the lodash debounce method.
[3:41] We saw that not only we removed the layout thrashing with debounce, we also reduced the unnecessary events and emitted the result only once.