Show element when in viewport on scroll

10,016

In pure javascript, you could do something like this, which uses a lot less resources than a full on jQuery approach:

function inViewport( element ){
  
  // Get the elements position relative to the viewport

  var bb = element.getBoundingClientRect();
  
  // Check if the element is outside the viewport
  // Then invert the returned value because you want to know the opposite

  return !(bb.top > innerHeight || bb.bottom < 0);
  
}

var myElement = document.querySelector( 'div' );

// Listen for the scroll event

document.addEventListener( 'scroll', event => {
 
  // Check the viewport status

  if( inViewport( myElement ) ){
    
    myElement.style.background = 'red';
    
  } else {
    
    myElement.style.background = '';
    
  }
  
})
body {
  
  height: 400vh;
  
}

div {
  
  width: 50vw;
  height: 50vh;
  position: absolute;
  top: 125vh;
  left: 25vw;
  transition: background 4s;
  border: 1px solid red;
  
}
<p>Scroll Down</p>
<div></div>

Here is a snippet with the opacity change:

function inViewport( element ){
  
  // Get the elements position relative to the viewport

  var bb = element.getBoundingClientRect();
  
  // Check if the element is outside the viewport
  // Then invert the returned value because you want to know the opposite

  return !(bb.top > innerHeight || bb.bottom < 0);
  
}

var myElement = document.querySelector( 'div' );

// Listen for the scroll event

document.addEventListener( 'scroll', event => {
 
  // Check the viewport status

  if( inViewport( myElement ) ){
    
    myElement.style.opacity = 1;
    
  } else {
    
    myElement.style.opacity = '';
    
  }
  
})
body {
  
  height: 400vh;
  
}

div {
  
  width: 50vw;
  height: 50vh;
  position: absolute;
  top: 125vh;
  left: 25vw;
  transition: opacity 1s;
  opacity: .2;
  background: blue;
  
}
<p>Scroll Down</p>
<div></div>

And here is a snippet showing you how to define where in the viewport it triggers, I just changed the innerHeight and 0 values to an object where you define the amount of pixels from the top it should be and the amount of pixels from the bottom. Don't forget to also add an event listener for resize, as these pixel based values will change if your viewport changes, so your myViewport object would need to be updated accordingly:

function inViewport( element, viewport = { top: 0, bottom: innerHeight } ){
  
  // Get the elements position relative to the viewport

  var bb = element.getBoundingClientRect();
  
  // Check if the element is outside the viewport
  // Then invert the returned value because you want to know the opposite

  return !(bb.top > viewport.bottom || bb.bottom < viewport.top);
  
}

var myViewport = { top: innerHeight * .4, bottom: innerHeight * .6 };
var myElement = document.querySelector( 'div' );

// Listen for the scroll event

document.addEventListener( 'scroll', event => {
 
  // Check the viewport status

  if( inViewport( myElement, myViewport ) ){
    
    myElement.style.opacity = 1;
    
  } else {
    
    myElement.style.opacity = '';
    
  }
  
})

window.addEventListener( 'resize', event => {
 
  // Update your viewport values

  myViewport.top = innerHeight * .4;
  myViewport.bottom = innerHeight * .6;
  
})
body {
  
  height: 400vh;
  
}

div {
  
  width: 50vw;
  height: 50vh;
  position: absolute;
  top: 125vh;
  left: 25vw;
  transition: opacity 1s;
  opacity: .2;
  background: blue;
  
}
<p>Scroll Down</p>
<div></div>

Share:
10,016
Marco
Author by

Marco

Updated on June 04, 2022

Comments

  • Marco
    Marco almost 2 years

    I've been trying to show an element on scroll when it's in viewport and when no, hide it again. But no matter what I try, I can't make it work.

    This is what I have so far, but the function is running just once, when the page is loaded and not when it's scrolled, so it doesn't update the value.

    $(window).scroll(function() {
        var top_of_element = $("#cont_quote blockquote").offset().top;
        var bottom_of_element = $("#cont_quote blockquote").offset().top + $("#cont_quote blockquote").outerHeight();
        var bottom_of_screen = $(window).scrollTop() + window.innerHeight;
        var top_of_screen = $(window).scrollTop();
    
        if((bottom_of_screen > top_of_element) && (top_of_screen < bottom_of_element)){
           $('#cont_quote blockquote').animate({'opacity':'1'},1000);
        }
        else {
           $('#cont_quote blockquote').animate({'opacity':'0'},1000);
        }
    });
    
    
    
    <section id="cont_quote">
        <article class="cont_q">
            <blockquote>Lorem ipsum</blockquote>
        </article>
    </section>
    
  • somethinghere
    somethinghere about 6 years
    @marco My bad, I changed the variable name to make it clearer but didnt change it in the variable in the scroll code. Amended it and also changed the visualisation, the background will now fill in when in viewport with a transition son the result is clear.
  • somethinghere
    somethinghere about 6 years
    @marco yeah that would work, I just chose for background since I can also display the border of where the element appears. Just make sure that original opacity of the element is 0 or you won't see the change (and if you set the element opacity to 0 after leaving the scrollable area, you will only see the change the next time it gets into view)
  • Robert Bisschop
    Robert Bisschop about 6 years
    @Marco The problem could also be in your css. Something like html/body height set to 100% or an overflow setting. Otherwise you could also try using $('#cont_quote').on('scroll', function(){
  • somethinghere
    somethinghere about 6 years
    @marco I have added a snippet with an opacity change from .2 to 1 (just so you don't miss the element while scrolling, but feel free to change the original opacity to a 0 in the CSS)
  • Marco
    Marco about 6 years
    Thank you! It works really well. There's one more thing I am trying to do. Instead of showing the element when it appears in viewport, show it when it's in the middle of the viewport, like only when it's in the 40% of the viewport's height. I've tried return !((bb.top + 300) > innerHeight || (bb.bottom - 100) < 0); but I prefer to have a percentage for responsiveness
  • somethinghere
    somethinghere about 6 years
    @marco I added another snippet with that functionality. Just define the top and bottom of your viewport in pixels (I calculate them based on the height of the viewport) and check for those values.
  • Marco
    Marco about 6 years
    That's exactly what I needed. I don't know how to thank you!
  • somethinghere
    somethinghere about 6 years
    @marco I prefer cash, but in lieu of that the checkmark on this post will do ;) - Also, I just made a small change that will make sure this function is more versatile by falling back to the standard viewport but allowing you to pass in your own viewport values where needed.
  • m.eslampnah
    m.eslampnah over 3 years
    it is good but not when scroll down and then scrollup