DEV Community

Cover image for How To Know If An Element Is Visible In Viewport.
Lucius Emmanuel Emmaccen
Lucius Emmanuel Emmaccen

Posted on • Edited on

How To Know If An Element Is Visible In Viewport.

In this article, we're going to learn how to know/detect if an element is visible in the browser's viewport.

Before we start, I'd like to clarify what a viewport is, relative to our browser engine.

A Viewport is the area (ordinarily rectangular) of our computer graphics (Screen) that is currently being viewed. In other words, it's the area of your computer screen that is currently visible to you (physically).

Check the MDN docs for an in-depth explanation, if mine is not clear enough.

Now, why would this be useful to you? Why would you want to know if an element is currently visible in the browser's viewport?

You might find it useful in situations like :

  • You want to show an overlay asking your customers to subscribe to your newsletter when they scroll to the footer of your website (e.g If its a blog site, this might indicate they just finished reading and you want them to subscribe)
  • You want to increase counter values that read when an element becomes visible to users
    counter image by emmaccen

  • Trigger animations when e.g "section A" on your webpage comes to view, fadeIn animations, etc

  • As a progress bar at the top of the screen that tells you how much content is left to view on a page (You might have seen it used on blog sites or any site that involves reading through long text content).

  • Do some Javascript magic, like play a video, show some short popup ads, toggle a help "BOT" 🤖 etc.

Am sure by now, you're seeing useful things that can be done with this in your mind too and at the end of this article, you'll get even more insight and ideas. So... let's get to it.

Walkthrough

We can achieve this by using the getBoundingClientRect() function on an element which returns a DOMRect object providing information about the size of an element and its position relative to the viewport.
so we have something like yourElement.getBoundingClientRect() or elementInfo = yourElement.getBoundingClientRect()

A DOMRect describes the size and position of a rectangle.

The DOMRect Object returned from getBoundingClientRect() are key-values (in pixels) that can be used to compute our goal and is the smallest rectangle which contains the entire element, including its padding and border-width.
The Object returned looks something like this:



 {
      x: 20,
      y: 5.5,
      width: 882,
      height: 198.890625,
      top: 5.5,
      right: 902,
      bottom: 204.390625,
      left: 20,
    };


Enter fullscreen mode Exit fullscreen mode

Let's go through the explanation in more detail. I've separated the visual presentation in order to avoid confusion.

DOMRect Key-Values (in pixels)

  • X and Left

Represents the distance from the left between the viewport (browser screen) and the top-left area of the DOMRect (yourElement).

getBoundingClient-X and Left

  • Y and Top

Represents the distance from the top of the viewport (browser screen) and the top of the DOMRect (yourElement).

getBoundingClient-Y and Top

  • Width

Represents the width of the DOMRect

  • Height

Represents the height of the DOMRect

The width and height properties of the DOMRect object returned by the method include the padding and border-width, not only the content width/height. In the standard box model, this would be equal to the width or height property of the element + padding + border-width. But if box-sizing: border-box is set for the element this would be directly equal to its width or height.

You can check the MDN Docs on box-sizing.

  • Bottom

Represents the distance from the top of the viewport (browser screen) and the bottom of the DOMRect (yourElement).

getBoundingClient-bottom

  • Right

Represents the distance from the left of the viewport (browser screen) and the right (bottom-right) of the DOMRect (yourElement). It has the same value as x + width, or x if width is negative.

getBoundingClient-right

Full Diagram

getBoundingClient value/angles

If you're wondering where I got all these diagrams from, I designed them in Figma

Some Useful Tips

  • Calculating Partial Visibility

Let's say we want to know if an element is partially visible in the viewport, and we've assigned an evenListner that triggers each time we scroll through the page e.g something like :



window.addEventListener("scroll", () => {
        //Some javascript magic here...
      });


Enter fullscreen mode Exit fullscreen mode

we can achieve this by simply subtracting the top/y value from the viewport height (screen) and also doing a check to make sure the bottom value is greater than 0.
The viewport height can be gotten using window.innerHeight or document.documentElement.clientHeight but usually, it's safer to combine them due to the browser compatibility of innerHeight and documentElement
So you might use something like :



const height = 
window.innerHeight || document.documentElement.clientHeight;


Enter fullscreen mode Exit fullscreen mode

So partial visibility would pass for the condition :
viewportHeight - top is greater than 0 and bottom is also greater than 0



const viewportHeight =
            window.innerHeight || document.documentElement.clientHeight;
// condition 
(viewportHeight - top > 0 && bottom > 0)


Enter fullscreen mode Exit fullscreen mode

If this is getting a little rough, it might be helpful to use the diagram and a pen and paper (I definitely did).

  • Calculating Full Visibility

Now, this part is almost as easy. The conditions required for full visibility are:
bottom is greater than 0 and bottom is less than or equal to viewportHeight and top is greater than or equal to 0
So it looks something like this:



bottom > 0 && bottom <= viewportHeight && top >= 0


Enter fullscreen mode Exit fullscreen mode

At this point, I think it would be nice to have us view a live website that computes the values of getBoundingClientRect() in real-time.

It'll also help you understand how all the conditions/checks we did earlier pass the visibility test. Just scroll through the page and watch the magic.
It's a super simple webpage with nicely cooked and understandable code 😎.
Feel free to clone/fork the gitHub repo if you want to get familiar with the code.

Now, it's evident that everything we've done so far accounts for the vertically scrollable element (scroll-top-bottom & scroll-bottom-top), but what about horizontally scrollable elements (scroll-left-right & scroll-right-left)?

We'll have to pair the condition with the browser width using :



(window.innerWidth || document.documentElement.clientWidth) 


Enter fullscreen mode Exit fullscreen mode

So we'll have something that looks like this:



(right > 0 && right <= width)

Enter fullscreen mode Exit fullscreen mode




Browser Compatibility

Browser Compatibility of getBoundingClientRect()

That's it and we've come to the end of this tutorial. I hope you found it useful. If you'd like to revisit/keep this post for reference, please, do bookmark it and leave a like/unicorn 🙂. Let me know what you think in the comment/discussion section below (improvements, your thoughts, etc). Cheers 🥂.

Update

While getBoundingClientRect() can help you achieve a lot of things, it's important to know the cost of using it as it fires very rapidly if used in an event listener like 'scroll listener'.

I'd recommend pairing it with a debounce() function, however, in most cases, the IntersectionObserver API does this quite efficiently. It observes changes in the visibility of an element relative to its parent or the top-level document's viewport. It can be used to trigger an action when an element becomes visible or hidden.

So, while getBoundingClientRect() can be used for positioning and sizing, IntersectionObserver is used for observing the visibility of an element. They serve different purposes and cannot be compared in terms of what one can do that the other can't. Read more about IntersectionObserver on MDN

Top comments (11)

Collapse
 
orkhanjafarovr profile image
Orkhan Jafarov • Edited

Nice! It’s awesome that IntersectionObserver was added in JS and does it easily .

Collapse
 
emmaccen profile image
Lucius Emmanuel Emmaccen

Thanks, Orkhan. The IntersectionObserver really is a considerable option.

Thanks again for bringing that up 🥂.

Collapse
 
maciekgrzybek profile image
Maciek Grzybek

Intersection Observer is a total game-changer for me :) I wrote a tutorial on how you can use it in real life -> dev.to/maciekgrzybek/create-sectio...

BTW, nice article Lucius :)

Thread Thread
 
emmaccen profile image
Lucius Emmanuel Emmaccen

Thanks, Maciek.
I just looked it up and bookmarked. Nice article. 💯%

Thread Thread
 
maciekgrzybek profile image
Maciek Grzybek

Thanks :)

Collapse
 
andrewbridge profile image
Andrew Bridge

I've found getBoundingClientRect can become an expensive call if it's used a lot. The scroll event is fired very rapidly in some browsers too, so as with most things involving this event, it's usually worthwhile debouncing calls to your handler.

Of course if you're able to use the InteractionObserver as others have suggested then great! But your method provides a much wider browser compatibility!

Collapse
 
emmaccen profile image
Lucius Emmanuel Emmaccen • Edited

Good feedback, Andrew.
By the looks of it, I should probably update the article. Everyone needs to know the cost of using getBoundingClientRect and how InteractionObserver might be a better fit in heavier projects.
By teaching, I've learnt a lot myself, thanks for pointing this out.

Collapse
 
devdahcoder profile image
Adigun Olamide

Well explained..

Collapse
 
emmaccen profile image
Lucius Emmanuel Emmaccen

Thanks.

Collapse
 
emmaccen profile image
Lucius Emmanuel Emmaccen

Thanks for sharing, Daniel.

Collapse
 
karimimahdi profile image
karimimahdi

What if the elements height is greater than viewport height???????
what is the algorithm????