Skip to content

Instantly share code, notes, and snippets.

@eltonmesquita
Last active November 1, 2020 09:32
Show Gist options
  • Star 23 You must be signed in to star a gist
  • Fork 8 You must be signed in to fork a gist
  • Save eltonmesquita/bd803a21f27b12a58df1 to your computer and use it in GitHub Desktop.
Save eltonmesquita/bd803a21f27b12a58df1 to your computer and use it in GitHub Desktop.
A simple jQuery function that adds a class when the target(s) is in the viewport
function onViewport(el, elClass, offset, callback) {
/*** Based on http://ejohn.org/blog/learning-from-twitter/ ***/
var didScroll = false;
var this_top;
var height;
var top;
if(!offset) { var offset = 0; }
$(window).scroll(function() {
didScroll = true;
});
setInterval(function() {
if (didScroll) {
didScroll = false;
top = $(this).scrollTop();
$(el).each(function(i){
this_top = $(this).offset().top - offset;
height = $(this).height();
// Scrolled within current section
if (top >= this_top && !$(this).hasClass(elClass)) {
$(this).addClass(elClass);
if (typeof callback == "function") callback(el);
}
});
}
}, 100);
}
@eltonmesquita
Copy link
Author

Use like this:

// Call the function with the following parameters:
onViewport(selector, classNameToBeAdded, offset, callback);

// Example:
onViewport(".about-section", "active", 300, function() {
  console.log("This is not a drill.")
});

The optional callback will be executed after the class is added.

@Nagenderpratap
Copy link

i am beginner in front end or i am facing same problem in my project but i am unable to understand your code....please help me in simple way..

@DRD161
Copy link

DRD161 commented Jan 13, 2018

@Nagenderpratap I know this post is kinda old but if you still need help understanding this code I'd be happy to help you out :)

@leialane
Copy link

@DRD161 I would love a run down.

@kirschkern
Copy link

Here is my understanding of the code.

function onViewport(el, elClass, offset, callback) {
 // initialize internal variables
  var didScroll = false;
  var this_top;
  var height;  // this is finally never used, so remove this line
  var top;
  
  // if no offset is given, set the offset to 0. This means, as soon as the top part 
  // of an becomes visible, it will get the extra class (elClass)
  // You may instead define 100 so the element will get the extra class only if at 
  // least 100 px of the element is visible.
  if(!offset) { var offset = 0; }
 
  // Register an event call back for scrolling. So every time the user scrolls,
  // the didScroll flag will be set to true.
  $(window).scroll(function() {
      didScroll = true;
  });
 
  // start a prediodic timer which will check 10 times in a second
  // if the didScroll flag is set due to a user scroll.
  setInterval(function() {
    // first, check, if the scroll flag is set. Only then something changed in the viewport and
    // we need to check, if the object might be visible now.
    if (didScroll) {
      // reset the flag, as we will handle a scroll change now and only want to act on a new scroll
      didScroll = false;
      // get the current scroll position of the window
      top = $(this).scrollTop();
 
      // Check all elements that are defined by the "el" selector argument 
      $(el).each(function(i){
        // get the elements position (and handle a given offset)
        // NOTE: Within this "each" loop, this refers to a single HTMLElement and not the window!
        this_top = $(this).offset().top - offset;
        // get the elements height. This is currently not used in the code, so you may delete this line.
        // It could be used, to check, if the element is fully visible.
        height   = $(this).height();
 
        // Now check if the scroll position of the window is larger or identical to the elements 
        // position. And check, if the class has been added allready. If not...
        if (top >= this_top && !$(this).hasClass(elClass)) {
          // ...add the elClass
          $(this).addClass(elClass);
 
          // In case you provided a function that should be invoked as soon as the element 
          // comes into the viewport, it is called now.
          if (typeof callback == "function") callback(el);
        }
      });
    }
  }, 100);  // 100 means each 100 milliseconds => 10 times in a second
}

@dasaki
Copy link

dasaki commented May 17, 2018

Just in case someone is interested, I adapted the code so that the elements recover their original look after they are hidden again by the scroll, so one can, for example replay animations each time the elements get into the viewport (see it working: http://jsfiddle.net/jp78rLef/ ). It doesn't work with latest jquery though ( tested with jquery-2.1.0.js )

/*** original found in https://gist.github.com/eltonmesquita/bd803a21f27b12a58df1 ***/
/*** Based on http://ejohn.org/blog/learning-from-twitter/ ***/
/*** class removal technique explained here: https://css-tricks.com/restart-css-animation/ ***/
function onViewport(el, elClass, offset, callback) {
 // initialize internal variables
  var didScroll = false;
  var this_top;
  var height;  // this is finally never used, so remove this line
  var scrollBottom;
  
  // if no offset is given, set the offset to 0. This means, as soon as the top part 
  // of an becomes visible, it will get the extra class (elClass)
  // You may instead define 100 so the element will get the extra class only if at 
  // least 100 px of the element is visible.
  if(!offset) { var offset = 0; }
 
  // Register an event call back for scrolling. So every time the user scrolls,
  // the didScroll flag will be set to true.
  $(window).scroll(function() {
      didScroll = true;
  });
 
  // start a prediodic timer which will check 10 times in a second
  // if the didScroll flag is set due to a user scroll.
  setInterval(function() {
    // first, check, if the scroll flag is set. Only then something changed in the viewport and
    // we need to check, if the object might be visible now.
    if (didScroll) {
      // reset the flag, as we will handle a scroll change now and only want to act on a new scroll
      didScroll = false;
      // get the current scroll position of the window
      scrollBottom = $(window).scrollTop() + $(window).height();
 
      // Check all elements that are defined by the "el" selector argument 
      $(el).each(function(i){
        // get the elements position (and handle a given offset)
        // NOTE: Within this "each" loop, this refers to a single HTMLElement and not the window!
        this_top = $(this).offset().top - offset;
        // get the elements height. This is currently not used in the code, so you may delete this line.
        // It could be used, to check, if the element is fully visible.
        height   = $(this).height();
 
        // Now check if the scroll position of the window is larger or identical to the elements 
        // position. And check, if the class has been added allready. If not...
        if (scrollBottom >= this_top && !$(this).hasClass(elClass)) {
          // ...add the elClass
          $(this).addClass(elClass);
 
          // In case you provided a function that should be invoked as soon as the element 
          // comes into the viewport, it is called now.
          if (typeof callback == "function") callback(el);
        }
        // if the class has been added allready and the element is not visible anymore
        else if (scrollBottom <= this_top && $(this).hasClass(elClass)) {
           // select last added class
           var el     = $(this), newone = el.clone(true);
            el.before(newone);
            // remove the class so the element has the original look
            $("." + el.attr("class") + ":last").remove();
        }
      });
    }
  }, 100);  // 100 means each 100 milliseconds => 10 times in a second
}

@BilalHalayqa
Copy link

BilalHalayqa commented Jul 7, 2018

@dasaki the code replicates the element each time.

@AdrianGen
Copy link

AdrianGen commented Jul 23, 2018

@eltonmesquita

Hi, I'm trying to implement your solution with my code but I am not sure how to combine the two.

I have a simple jQuery function to animate some CSS progress bars for me:

$(".progressBar > span").each(function() {
  $(this)
    .data("origWidth", $(this).width())
    .width(0)
    .animate({
      width: $(this).data("origWidth")
    }, 1200);
});

But I don't know how should I link yours with mine to make it work.

I would appreciate any advice, please don't kill me I am new to this.

Thank you

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment