<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: toruskit</title>
    <description>The latest articles on DEV Community by toruskit (@toruskit).</description>
    <link>https://dev.to/toruskit</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F657562%2Fc4dbe790-63f0-45ad-8c4d-4aa39f634278.png</url>
      <title>DEV Community: toruskit</title>
      <link>https://dev.to/toruskit</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/toruskit"/>
    <language>en</language>
    <item>
      <title>How to get element bounds without forcing a reflow</title>
      <dc:creator>toruskit</dc:creator>
      <pubDate>Mon, 28 Jun 2021 09:55:13 +0000</pubDate>
      <link>https://dev.to/toruskit/how-to-get-element-bounds-without-forcing-a-reflow-65i</link>
      <guid>https://dev.to/toruskit/how-to-get-element-bounds-without-forcing-a-reflow-65i</guid>
      <description>&lt;p&gt;Getting the element bounds (size and position) seems like a trivial task. Just use &lt;a href="https://developer.mozilla.org/en-US/docs/Web/API/Element/getBoundingClientRect" rel="noopener noreferrer"&gt;getBoundingClientRect()&lt;/a&gt; in loop on bunch of elements and you're done. The truth is, that works pretty well, except the one thing - a performance. You're likely to force a browser &lt;a href="https://developers.google.com/speed/docs/insights/browser-reflow" rel="noopener noreferrer"&gt;reflow&lt;/a&gt;. And when you have a huge amount of elements, the performance hurt can be significant.&lt;/p&gt;

&lt;p&gt;In this post, I'm gonna show you a little bit unusual approach to getting element bounds with the use of &lt;a href="https://developer.mozilla.org/en-US/docs/Web/API/Intersection_Observer_API" rel="noopener noreferrer"&gt;IntersectionObserver&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  What is a browser reflow
&lt;/h2&gt;

&lt;p&gt;Long story short. There are a lot of &lt;a href="https://developers.google.com/speed/docs/insights/browser-reflow" rel="noopener noreferrer"&gt;resources&lt;/a&gt; about the &lt;a href="https://sites.google.com/site/getsnippet/javascript/dom/repaints-and-reflows-manipulating-the-dom-responsibly" rel="noopener noreferrer"&gt;reflows&lt;/a&gt;, so I will take it fast.&lt;/p&gt;

&lt;p&gt;The &lt;strong&gt;reflow&lt;/strong&gt; is a process when the browser needs to &lt;em&gt;re-calculate&lt;/em&gt; the position and dimensions of the elements on the page. The reflow always occurs when the page is loaded and the browser needs to traverse the DOM to get all elements. This is very expensive (in the meaning of performance) and can make longer rendering, &lt;a href="http://jankfree.org/" rel="noopener noreferrer"&gt;junky&lt;/a&gt; scrolling or sluggish animations.&lt;/p&gt;

&lt;p&gt;Forcing a browser reflow can be done just by changing the width of the element by as little as 1px. Yes, it's so small amount, but the browser needs to check the new position of the element and also how it affected other elements on the page. So it's better to use a &lt;a href="https://csstriggers.com/transform" rel="noopener noreferrer"&gt;&lt;code&gt;transform&lt;/code&gt;&lt;/a&gt; property for that. But this is out of scope of this article.&lt;/p&gt;

&lt;h2&gt;
  
  
  The old ways of getting the element dimensions
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Get element offsetTop/offsetLeft value
&lt;/h3&gt;

&lt;p&gt;This is the very old method of getting the element position using &lt;code&gt;offsetTop&lt;/code&gt; or &lt;code&gt;offsetLeft&lt;/code&gt;. Unfortunately, there is one (serious) detail to keep in mind - it returns the position relative to the &lt;em&gt;parent&lt;/em&gt; element and not the absolute position relative to the page. Even there is a solution using &lt;a href="https://gist.github.com/alexcambose/830e33a8f360e0b4a477e6e8f64fcd5a#file-offset-js" rel="noopener noreferrer"&gt;offset.js&lt;/a&gt; script, it still forces reflow.&lt;/p&gt;

&lt;h3&gt;
  
  
  Call getBoundingClientRect()
&lt;/h3&gt;

&lt;p&gt;This one is more precise and easier to use. It returns the element size and position relative to the viewport. You'll get &lt;code&gt;left&lt;/code&gt;, &lt;code&gt;top&lt;/code&gt;, &lt;code&gt;right&lt;/code&gt;, &lt;code&gt;bottom&lt;/code&gt;, &lt;code&gt;x&lt;/code&gt;, &lt;code&gt;y&lt;/code&gt;, &lt;code&gt;width&lt;/code&gt;, and &lt;code&gt;height&lt;/code&gt; values of selected element. It's relatively fast when you have a small number of elements. But it's getting to be slower and forcing a reflow when the number of elements starts to rise dramatically, or when calling multiple time.&lt;/p&gt;

&lt;h2&gt;
  
  
  Use IntersectionObserver to get element bounds
&lt;/h2&gt;

&lt;p&gt;This is the relatively unknown approach of getting the dimension and position of the element, because of the &lt;a href="https://developer.mozilla.org/en-US/docs/Web/API/Intersection_Observer_API" rel="noopener noreferrer"&gt;&lt;code&gt;IntersectionObserver&lt;/code&gt;&lt;/a&gt; is primarily used to calculate the visibility of the element in the viewport.&lt;/p&gt;

&lt;h3&gt;
  
  
  What is IntersectionObserver
&lt;/h3&gt;

&lt;p&gt;As it's mentioned in the MDN docs:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;The Intersection Observer API provides a way to asynchronously observe changes in the intersection of a target element with an ancestor element or with a top-level document's viewport.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The magic keyword - &lt;em&gt;asynchronously&lt;/em&gt; is why the performance will thank you. All the calculations are done "off the main thread" so the browser has much time to do the optimizations.&lt;/p&gt;

&lt;p&gt;But how to get element bounds with this, and what to do if the element is not even visible in the viewport?&lt;/p&gt;

&lt;p&gt;In fact, you don't need to care. &lt;code&gt;IntersectionObserver&lt;/code&gt; API has a &lt;code&gt;boundingClientRect&lt;/code&gt; property that calculates the element dimension independently on its visibility.&lt;/p&gt;

&lt;h3&gt;
  
  
  boundingClientRect to the rescue
&lt;/h3&gt;

&lt;p&gt;The &lt;code&gt;boundingClientRect&lt;/code&gt; is the &lt;code&gt;IntersectionObserver&lt;/code&gt; API interface that returns a read-only value of the rectangle describing the smallest rectangle that contains the entire target element. It's like the &lt;code&gt;getBoundingClientRect()&lt;/code&gt; but without forcing a reflow. You'll get &lt;code&gt;left&lt;/code&gt;, &lt;code&gt;top&lt;/code&gt;, &lt;code&gt;right&lt;/code&gt;, &lt;code&gt;bottom&lt;/code&gt;, &lt;code&gt;x&lt;/code&gt;, &lt;code&gt;y&lt;/code&gt;, &lt;code&gt;width&lt;/code&gt;, and &lt;code&gt;height&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;This property is accessible inside the &lt;code&gt;IntersectionObserver&lt;/code&gt; constructor via &lt;code&gt;entry.boundingClientRect&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  How to use it
&lt;/h3&gt;

&lt;p&gt;Finally, let's take a look at how to use this all to get the element dimensions without making the browser hate us.&lt;/p&gt;

&lt;p&gt;The full script looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// new `IntersectionObserver` constructor&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;observer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;IntersectionObserver&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;entries&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// Loop through all `entries` returned by the observer&lt;/span&gt;
  &lt;span class="k"&gt;for &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;entry&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="nx"&gt;entries&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// The `entry.boundingClientRect` is where all the dimensions are stored&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;bounds&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;entry&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;boundingClientRect&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="c1"&gt;// Log the `bounds` for every element&lt;/span&gt;
    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;bounds&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="c1"&gt;// Then do whatever with `bounds`&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="c1"&gt;// Disconnect the observer to stop from running in the background&lt;/span&gt;
  &lt;span class="nx"&gt;observer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;disconnect&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="c1"&gt;// Select all the `.element` elements&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;elements&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;querySelectorAll&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;.element&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// Loop through all elements&lt;/span&gt;
&lt;span class="k"&gt;for &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;element&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="nx"&gt;elements&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// Run the `observe` function of the `IntersectionObserver` on the element&lt;/span&gt;
  &lt;span class="nx"&gt;observer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;observe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;element&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;entry.boundingClientRect&lt;/code&gt; is where the magic happens. This property stores all the element dimensions and positions.&lt;/p&gt;

&lt;p&gt;Now let's take a closer look on each definition.&lt;/p&gt;

&lt;p&gt;The first step is to create a new &lt;code&gt;IntersectionObserver&lt;/code&gt; constructor that takes a list of elements as an argument and applies its calculations. Note to mention - you can pass custom options to the observer, but we're going to keep defaults one, as we don't need to track visibility.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;observer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;IntersectionObserver&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;entries&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;

&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Inside this &lt;code&gt;IntersectionObserver&lt;/code&gt;, we need to loop through all &lt;code&gt;entries&lt;/code&gt; that will be passed later in the loop. &lt;strong&gt;This is the place where you get elements bounds for further use&lt;/strong&gt;{.bg-green .bg-opacity-20}. We'll use &lt;code&gt;bounds&lt;/code&gt; constant to store the &lt;code&gt;entry.boundingClientRect&lt;/code&gt; values so when you need to get &lt;code&gt;x&lt;/code&gt; or &lt;code&gt;height&lt;/code&gt; value of the element, just use &lt;code&gt;bounds.x&lt;/code&gt; or &lt;code&gt;bounds.height&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;for &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;entry&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="nx"&gt;entries&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;bounds&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;entry&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;boundingClientRect&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="c1"&gt;// Use `bounds` like you need&lt;/span&gt;
  &lt;span class="c1"&gt;// Example: `bounds.height` will return the element `height` value in px&lt;/span&gt;

&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When the observing is done, it's good to disconnect the observer as we don't need it anymore.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;observer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;disconnect&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then we need to select all the elements on which we need to determine their bounds. They'll be stored in the &lt;code&gt;.elements&lt;/code&gt; constant.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;elements&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;querySelectorAll&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;.element&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And finally, loop through all of them and run the observer on them. This may look like a synchronous call, but in fact, the IntersectionObserver is not triggered immediately when the &lt;code&gt;observer.observe(element);&lt;/code&gt; is called. Instead, it waits and then takes a bunch of elements and runs the calculations asynchronously.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;for &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;element&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;querySelectorAll&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;.element&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;observer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;observe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;element&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Performance: getBoundingClientRect() vs IntersectionObserver
&lt;/h2&gt;

&lt;p&gt;To get an idea of how fast and performant the &lt;code&gt;IntersectionObserver&lt;/code&gt; is, I've made a quick comparison with the old &lt;code&gt;getBoundingClientRect()&lt;/code&gt; method.&lt;/p&gt;

&lt;p&gt;I've generated 5000 squared &lt;code&gt;&amp;lt;div&amp;gt;&lt;/code&gt; elements and give them a &lt;code&gt;.element&lt;/code&gt; class with basic stylings such as size and background color. There are no other elements that could affect performance.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Ftoruskit.com%2Fblog%2Fhow-to-get-element-bounds-without-reflow%2Fget-element-bounds-without-reflow-generated-elements.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Ftoruskit.com%2Fblog%2Fhow-to-get-element-bounds-without-reflow%2Fget-element-bounds-without-reflow-generated-elements.png" alt="Get element bounds without reflow - Rendered page with 5000 elements"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now let's compare the &lt;code&gt;getBoundingClientRect()&lt;/code&gt; vs &lt;code&gt;IntersectionObserver&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Simple test
&lt;/h3&gt;

&lt;p&gt;These are the scripts to evaluate the performance of the both methods:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;elements&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;querySelectorAll&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;.element&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// `getBoundingClientRect()`&lt;/span&gt;

&lt;span class="k"&gt;for &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;element&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="nx"&gt;elements&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;bounds&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;element&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getBoundingClientRect&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// `IntersectionObserver`&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;observer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;IntersectionObserver&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;entries&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;for &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;entry&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="nx"&gt;entries&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;bounds&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;entry&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;boundingClientRect&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="nx"&gt;observer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;disconnect&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="k"&gt;for &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;element&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="nx"&gt;elements&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;observer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;observe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;element&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When using &lt;code&gt;getBoundingClientRect()&lt;/code&gt; results without any further manipulation, everything runs pretty fast. Check the &lt;a href="//./get-bounding-client-rect.html"&gt;live demo&lt;/a&gt; to see how it performs in your browser.&lt;/p&gt;

&lt;p&gt;When using &lt;code&gt;IntersectionObserver&lt;/code&gt; in this &lt;a href="//./intersection-observer.html"&gt;live demo&lt;/a&gt; everything is fast, too. It seems there is no big difference until you check the Performance tab in Google Chrome tools. When running &lt;code&gt;getBoundingClientRect()&lt;/code&gt;, the browser is forced to do a &lt;strong&gt;reflow&lt;/strong&gt; and it takes longer to evaluate the script.&lt;/p&gt;

&lt;p&gt;On the other hand, using &lt;code&gt;IntersectionObserver&lt;/code&gt; makes no reflows, and the script runs as fast as possible. Take to count that the page has 5000 elements, so parsing and recalculating styles take more time in both cases.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Ftoruskit.com%2Fblog%2Fhow-to-get-element-bounds-without-reflow%2Fget-element-bounds-without-reflow-comparison.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Ftoruskit.com%2Fblog%2Fhow-to-get-element-bounds-without-reflow%2Fget-element-bounds-without-reflow-comparison.png" alt="getBoundingClientRect vs IntersectionObserver comparison"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Let's get complicated
&lt;/h3&gt;

&lt;p&gt;Even that the first method is not as fast as the second, the performance hit is not so obvious. But what if you need to display the element's dimensions somewhere.&lt;/p&gt;

&lt;p&gt;This example shows what happens when we want to display the bounds on each element as text content using CSS &lt;code&gt;::after&lt;/code&gt; pseudo-element.&lt;/p&gt;

&lt;p&gt;But first, let's edit the code a little bit and add a line that sets a &lt;code&gt;data-bounds&lt;/code&gt; attribute on the element.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;elements&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;querySelectorAll&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;.element&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// `getBoundingClientRect()`&lt;/span&gt;

&lt;span class="k"&gt;for &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;element&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="nx"&gt;elements&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;bounds&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;element&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getBoundingClientRect&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// `IntersectionObserver`&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;observer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;IntersectionObserver&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;entries&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;for &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;entry&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="nx"&gt;entries&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;bounds&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;entry&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;boundingClientRect&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="nx"&gt;observer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;disconnect&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="k"&gt;for &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;element&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="nx"&gt;elements&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;observer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;observe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;element&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The results are shocking. While the &lt;code&gt;IntersectionObserver&lt;/code&gt; method looks like there's no difference, the &lt;code&gt;getBoundingClientRect()&lt;/code&gt; method got mad. It takes 1.14s to evaluate the script and makes a huge amount of reflows.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Ftoruskit.com%2Fblog%2Fhow-to-get-element-bounds-without-reflow%2Fget-element-bounds-without-reflow-dataset-comparison.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Ftoruskit.com%2Fblog%2Fhow-to-get-element-bounds-without-reflow%2Fget-element-bounds-without-reflow-dataset-comparison.png" alt="getBoundingClientRect vs IntersectionObserver comparison with dataset"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;OK, someone can argue that this is because the &lt;code&gt;IntersectionObserver&lt;/code&gt; runs in asynchronous mode. It's true, so let's make the &lt;code&gt;getBoundingClientRect()&lt;/code&gt; asynchronous with this script:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;promises&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[];&lt;/span&gt;

&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;loop&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;for &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;element&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="nx"&gt;elements&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;bounds&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;element&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getBoundingClientRect&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="nx"&gt;promises&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;push&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;bounds&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="nb"&gt;Promise&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;all&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;promises&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;then&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;results&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;for &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;element&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="nb"&gt;Object&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;entries&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;elements&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;results&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nc"&gt;Number&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;)];&lt;/span&gt;
      &lt;span class="nx"&gt;element&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;dataset&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;bounds&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;`x: &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;x&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; y:&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;y&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; width: &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;width&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; height: &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;height&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nf"&gt;loop&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The results are much better compared with synchronous method. There are magically no reflows, but the script evaluation time is still longer then &lt;code&gt;IntersectionObserver&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Ftoruskit.com%2Fblog%2Fhow-to-get-element-bounds-without-reflow%2Fget-element-bounds-without-reflow-async-comparison.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Ftoruskit.com%2Fblog%2Fhow-to-get-element-bounds-without-reflow%2Fget-element-bounds-without-reflow-async-comparison.png" alt="getBoundingClientRect vs IntersectionObserver with asynchronous call"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Wrapping it up
&lt;/h2&gt;

&lt;p&gt;As you can see, the &lt;code&gt;IntersectionObserver&lt;/code&gt; can be used not just to check the element visibility, but also to calculate its dimensions and position. Compared to &lt;code&gt;getBoundingClientRect()&lt;/code&gt; it's faster and doesn't produce any reflows. Even when the &lt;code&gt;getBoundingClientRect()&lt;/code&gt; is used in asynchronous function, it's still slower.&lt;/p&gt;

&lt;p&gt;In the &lt;a href="https://toruskit.com" rel="noopener noreferrer"&gt;Torus Kit&lt;/a&gt;, we're using this approach to get element bounds as fast as possible without unnecessary reflows.&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>performance</category>
    </item>
  </channel>
</rss>
