How to Avoid DOM Blocking in JavaScript

How to Avoid DOM Blocking in JavaScript

JavaScript programs run on a single thread in the browser and in runtimes such as Node.js. When code is executing in a browser tab, everything else stops: menu commands, downloads, rendering, DOM updates and even GIF animations.

JavaScript programs run on a single thread in the browser and in runtimes such as Node.js. When code is executing in a browser tab, everything else stops: menu commands, downloads, rendering, DOM updates and even GIF animations.

This is rarely evident to the user because processing occurs quickly in small chunks. For example: a button is clicked which raises an event that runs a function which makes a calculation and updates the DOM. Once complete, the browser is free to handle the next item on the processing queue.

JavaScript code can’t wait for something to occur; imagine the frustration if an app froze every time it made an Ajax request. JavaScript code therefore operates using events and callbacks: a browser or OS-level process is instructed to call a specific function when an operation has completed and the result is ready.

In the following example, a handler function is executed when a button click event occurs which animates an element by applying a CSS class. When that animation completes, an anonymous callback removes the class:

// raise an event when a button is clicked
document.getElementById('clickme').addEventListener('click', handleClick);

// handle button click event
function handleClick(e) {

  // get element to animate
  let sprite = document.getElementById('sprite');
  if (!sprite) return;

  // remove 'animate' class when animation ends
  sprite.addEventListener('animationend', () => {

  // add 'animate' class

ES2015 provided Promises and ES2017 introduced async/await to make coding easier, but callbacks are still used below the surface. For more information, refer to “Flow Control in Modern JS”.

Blocking Bandits

Unfortunately, some JavaScript operations will always be synchronous, including:

The following pen shows an invader which uses a combination of CSS animation to move and JavaScript to wave the limbs. The image on the right is a basic animated GIF. Hit the write button with the default 100,000 sessionStorage operations:

DOM updates are blocked during this operation. The invader will halt or stutter in most browsers. The animated GIF animation will pause in some. Slower devices may show a “script unresponsive” warning.

This is a convoluted example, but it demonstrates how front-end performance can be affected by basic operations.

Web Workers

One solution to long-running processes is web workers. These allow the main browser application to launch a background script and communicate using message events. For example:

// main.js
// are web workers supported?
if (!window.Worker) return;

// start web worker script
let myWorker = new Worker('myworker.js');

// message received from myWorker
myWorker.onmessage = e => {
  console.log('myworker sent:',;

// send message to myWorker

The web worker script:

// myworker.js
// start when a message is received
onmessage = e => {
  console.log('myworker received:',;
  // ... long-running process ...
  // post message back

A worker can even spawn other workers to emulate complex, thread-like operations. However, workers are intentionally limited and a worker cannot directly access the DOM or localStorage (doing so would effectively make JavaScript multi-threaded and break browser stability.) All messages are therefore sent as strings, which permits JSON-encoded objects to be passed but not DOM nodes.

Workers can access some window properties, web sockets, and IndexDB — but they wouldn’t improve the example shown above. In most cases, workers are used for long-running calculations — such as ray tracing, image processing, bitcoin mining and so on.

(Node.js offers *[child processes]( "*child processes")* which are similar to web workers but have options to run executables written in other languages.)*

Hardware-accelerated Animations

Most modern browsers don’t block hardware-accelerated CSS animations which run within their own layer.

By default, the example above moves the invader by changing the left-margin. This and similar properties such as left and width cause the browser to reflow and repaint the whole document at every animation step.

Animation is more efficient when using the transform and/or opacity properties. These effectively place the element into a separate compositing layer so it can be animated in isolation by the GPU.

Click the hardware acceleration checkbox and the animation will immediately become smoother. Now attempt another sessionStorage write; the invader will continue to move even if the animated GIF stops. Note that the limb movement will still pause because that’s controlled by JavaScript.

In-memory Storage

Updating an object in memory is considerably faster than using a storage mechanism which writes to disk. Select the object storage type in the pen above and hit write. Results will vary, but it should be around 10x faster than an equivalent sessionStorage operation.

Memory is volatile: closing the tab or navigating away causes all data to be lost. A good compromise is to use in-memory objects for improved performance, then store data permanently at convenient moments — such as when the page is unloaded:

// get previously-saved data
var store = JSON.parse(localStorage.getItem('store'));

// initialise an empty store
if (!store || !store.initialized) {
  store = {
    initialized: true,
    username: 'anonymous'
    score: 0,
    best: { score: 1000, username: 'Alice' }

// save to localStorage on page unload
window.addEventListener('unload', () => {
  localStorage.setItem('store', JSON.stringify(store));

Games or single-page applications may require more complex options. For example, data is saved when:

  • running calculations
  • updating the DOM
  • using localStorage or IndexedDB to store and retrieve data.

    Web Performance

Web performance is a hot topic. Developers are less constrained by browser limits and users expect fast, OS-like application performance.

Do as little processing as infrequently as possible and the DOM will never be noticeably blocked. Fortunately, there are options in situations where long-running tasks can’t be avoided.

Users and clients may never notice your speed optimizations, but they’ll always complain when the application becomes slower!

Thanks for reading ❤

Angular 9 Tutorial: Learn to Build a CRUD Angular App Quickly

What's new in Bootstrap 5 and when Bootstrap 5 release date?

Brave, Chrome, Firefox, Opera or Edge: Which is Better and Faster?

How to Build Progressive Web Apps (PWA) using Angular 9

What is new features in Javascript ES2020 ECMAScript 2020

JavaScript Tutorial: if-else Statement in JavaScript

This JavaScript tutorial is a step by step guide on JavaScript If Else Statements. Learn how to use If Else in javascript and also JavaScript If Else Statements. if-else Statement in JavaScript. JavaScript's conditional statements: if; if-else; nested-if; if-else-if. These statements allow you to control the flow of your program's execution based upon conditions known only during run time.

How to Retrieve full Profile of LinkedIn User using Javascript

I am trying to retrieve the full profile (especially job history and educational qualifications) of a linkedin user via the Javascript (Fetch LinkedIn Data Using JavaScript)

Java vs. JavaScript: Know The Difference

Java vs. JavaScript: Know the Difference, Java vs. JavaScript: What's the Difference? Java vs. JavaScript: Major Similarities and Differences. pros and cons of JavaScript and Java.