A simple and fast API to monitor elements as you scroll

Overview

scrollMonitor

The scroll monitor allows you to receive events when elements enter or exit a viewport. It does this using watcher objects, which watch an element and trigger events. Watcher objects also contain information about the element they watch, including the element's visibility and location relative to the viewport. If your scroll container is an element other than the body you can create a container that creates watchers.

The scroll monitor was designed to be very fast. On each scroll event the DOM is only touched twice, once to find the document height and again to find the viewport top. No variables are declared, nor are any objects, arrays, or strings created. Watchers are very cheap. Create them liberally.

The code is vanilla javascript and has no external dependencies, however the script cannot be put in the head.

Also see the React hooks, React component and the parallax library.

Basic Usage

When the body scrolls

var scrollMonitor = require("scrollmonitor"); // if you're old school you can use the scrollMonitor global.
var myElement = document.getElementById("itemToWatch");

var elementWatcher = scrollMonitor.create( myElement );

elementWatcher.enterViewport(function() {
    console.log( 'I have entered the viewport' );
});
elementWatcher.exitViewport(function() {
    console.log( 'I have left the viewport' );
});

For a scroll container

var containerElement = document.getElementById("container");

var containerMonitor = scrollMonitor.createContainer(containerElement);
// this containerMonitor is an instance of the scroll monitor
// that listens to scroll events on your container.

var childElement = document.getElementById("child-of-container");
var elementWatcher = containerMonitor.create(childElement);

elementWatcher.enterViewport(function() {
    console.log( 'I have entered the viewport' );
});
elementWatcher.exitViewport(function() {
    console.log( 'I have left the viewport' );
});

Note: an element is said to be in the viewport if it is scrolled into its parent, it does not matter if the parent is in the viewport.

Demos

Watcher Objects

Create watcher objects with scrollMonitor.create( watchItem ). An optional second argument lets you receive events before or after this element enters the viewport. See "Offsets".

watchItem can be one of the following:

  • DOM Element - the watcher will watch the area contained by the DOM element.
  • Object - obj.top and obj.bottom will be used for watcher.top and watcher.bottom.
  • Number - the watcher will watch a 1px area this many pixels from the top. Negative numbers will watch from the bottom.
  • jQuery object - it will use the first DOM element.
  • NodeList or Array - it will use the first DOM element.
  • string - it will use the string as a CSS selector and watch the first match.

Watchers are automatically recalculated on the first scroll event after the height of the document changes.

Events

Element watchers trigger six events:

  • visibilityChange - when the element enters or exits the viewport.
  • stateChange - similar to visibilityChange but is also called if the element goes from below the viewport to above it in one scroll event or when the element goes from partially to fully visible or vice versa.
  • enterViewport - when the element enters the viewport.
  • fullyEnterViewport - when the element is completely in the viewport [1].
  • exitViewport - when the element completely leaves the viewport.
  • partiallyExitViewport - when the element goes from being fully in the viewport to only partially [2].
  1. If the element is larger than the viewport fullyEnterViewport will be triggered when the element spans the entire viewport.
  2. If the element is larger than the viewport partiallyExitViewport will be triggered when the element no longer spans the entire viewport.

Properties

  • elementWatcher.isInViewport - true if any part of the element is visible, false if not.
  • elementWatcher.isFullyInViewport - true if the entire element is visible [1].
  • elementWatcher.isAboveViewport - true if any part of the element is above the viewport.
  • elementWatcher.isBelowViewport - true if any part of the element is below the viewport.
  • elementWatcher.top - distance from the top of the document to the top of this watcher.
  • elementWatcher.bottom - distance from the top of the document to the bottom of this watcher.
  • elementWatcher.height - top - bottom.
  • elementWatcher.watchItem - the element, number, or object that this watcher is watching.
  • elementWatcher.offsets - an object that determines the offsets of this watcher. See "Offsets".
  1. If the element is larger than the viewport isFullyInViewport is true when the element spans the entire viewport.

Methods

  • elementWatcher.on/off/one - the standard event functions.
  • elementWatcher.recalculateLocation - recalculates the location of the element in relation to the document.
  • elementWatcher.destroy - removes this watcher and clears out its event listeners.
  • elementWatcher.lock - locks this watcher at its current location. See "Locking".
  • elementWatcher.unlock - unlocks this watcher.

These methods are automatically called by the scrollMonitor, you should never need them:

  • elementWatcher.update - updates the boolean properties in relation to the viewport. Does not trigger events.
  • elementWatcher.triggerCallbacks - triggers any callbacks that need to be called.

Locking

Sometimes you want to change the element you're watching, but want to continue watching the original area. One common use case is setting position: fixed on an element when it exits the viewport, then removing positioning when it when it reenters.

var watcher = scrollMonitor.create( $element );
watcher.lock(); // ensure that we're always watching the place the element originally was

watcher.exitViewport(function() {
    $element.addClass('fixed');
});
watcher.enterViewport(function() {
    $element.removeClass('fixed');
});

Because the watcher was locked on the second line, the scroll monitor will never recalculate its location.

Offsets

If you want to trigger an event when the edge of an element is near the edge of the viewport, you can use offsets. The offset is the second argument to scrollMonitor.create.

This will trigger events when an element gets within 200px of the viewport:

scrollMonitor.create( element, 200 )

This will trigger when the element is 200px inside the viewport:

scrollMonitor.create( element, -200 )

If you only want it to affect the top and bottom differently you can send an object in.

scrollMonitor.create( element, {top: 200, bottom: 50})

If you only want it to affect the top and not the bottom you can use only one property in.

scrollMonitor.create( element, {top: 200})

scrollMonitor Module

Methods

  • scrollMonitor.createContainer( containerEl ) - returns a new ScrollMonitorContainer that can be used just like the scrollMonitor module.
  • scrollMonitor.create( watchItem, offsets ) - Returns a new watcher. watchItem is a DOM element, jQuery object, NodeList, CSS selector, object with .top and .bottom, or a number.
  • scrollMonitor.update() - update and trigger all watchers.
  • scrollMonitor.recalculateLocations() - recalculate the location of all unlocked watchers and trigger if needed.

Properties

  • scrollMonitor.viewportTop - distance from the top of the document to the top of the viewport.
  • scrollMonitor.viewportBottom - distance from the top of the document to the bottom of the viewport.
  • scrollMonitor.viewportHeight - height of the viewport.
  • scrollMonitor.documentHeight - height of the document.

Contributing

There is a set of unit tests written with Mocha + Chai that run in testem. Getting them running is simple:

npm install
npm test

then open http://localhost:7357

Comments
  • Use another wrapper than window/document

    Use another wrapper than window/document

    I'm working with this GREAT plugin. I'm really love it (some are WIP): http://codepen.io/myconcretelab/pen/wkduG http://codepen.io/myconcretelab/pen/ctuoa http://codepen.io/myconcretelab/pen/aHkih

    I'm asking if is it possible to easily pass another wrapper into the constructor. it seems that if elements are in a div, somewhere in the page i will nee to set all these properties : scrollMonitor.viewportTop, scrollMonitor.viewportBottom, scrollMonitor.viewportHeight and scrollMonitor.documentHeight ? Is it possible to add this feature ?

    opened by myconcretelab 15
  • Regular event for scrolling on tablets (iOS)

    Regular event for scrolling on tablets (iOS)

    I've tested the script on the iPad (no other tablet systems). The downside here is that it only fires the event, after the scrolling ended – so 1-2 seconds delay because of the deceleration phase. Is there a way to send an event during the active scrolling phase on iOS? Thanks so far for your good work!

    opened by Jugibur 10
  • Make scrollMonitor instance based and allow for multiple containers

    Make scrollMonitor instance based and allow for multiple containers

    Opening this PR per discussion from issue #10.

    This PR contains two changes:

    Make scrollMonitor instance based I've revised the internal architecture of scrollMonitor to be instance based. This was necessary for the second part of this PR.

    Allow for multiple containers With this PR you can now create additional scrollMonitor instances on different DOM elements. This is useful when you have a

    with overflow: scroll set on it and you want to use scrollMonitor.

    I've extended the current API to allow for this additional functionality. I've added an additional argument to scrollMonitor.create/update/recalculateLocations. They now accept a jQuery object in which a new scrollMonitor instance is attached.

    I realized I didn't update the docs for scrollMonitor to reflect these changes. I'm going to add a commit which does so, and hopefully that will better explain the changes I did.

    opened by hswolff 10
  • Sticky element rely on viewport/contentHeight, even if it's out of viewport

    Sticky element rely on viewport/contentHeight, even if it's out of viewport

    Really appreciate Your efforts and a nice approach to manage scroll listener. 👍

    Ref - https://github.com/stutrek/scrollMonitor/blob/master/src/container.js#L168 As it rely on viewport/contentHeight, it cause an issue when we have multiple containers with different heights, So sticky element does not "recalculateLocation" even it's out of viewport.

    I tried to explain the problem with a JSFiddle Demo

    This Problem fix, if i remove remove a condition but that maybe a wrong fix.

    So is this a bug or i am missing something?

    Kindly ask if You need more detail. Thanks

    opened by mahboob-awan 9
  • Removing jQuery dependency

    Removing jQuery dependency

    Hi,

    ScrollMonitor is great. I just needed to use it in a jQuery-free environment. This pull request removes jQuery from dependencies and replaces its methods with either standard DOM Javascript methods or utility methods.

    I have tested it with the recent Chrome, Firefox and Safari (both desktop and mobile). Even though the replacement methods are standard, it still would be great if somebody could test it on Android and IE.

    opened by theorm 8
  • Scroll does not recalculate watcher locations

    Scroll does not recalculate watcher locations

    Upon scroll, the calculateViewport is called. However, since the document height did not change, the watchers positions will not be updated. As a result the values of isInViewport does not get updated. I have verified all this through the debugger, and I am not certain how this is working for other people.

    I am using Chrome (v 51.0.2704.103 64-bit) on Mac if that makes any difference.

    opened by adeopura4 7
  • add scrollMonitor.destroyAll() method

    add scrollMonitor.destroyAll() method

    Hi, We're using scrollMonitor in a single-page app which replaces a large number of elements during search or navigation. This destroyAll() function has been useful to cleanup between page transitions.

    I tried to npm install to run your tests, but the dependencies are so old, the karma launcher will no longer compile.

    hth. j

    opened by jldec 7
  • Only one element triggers events

    Only one element triggers events

    I'm currently looking into implementing scrollMonitor. The promised functionality is exactly what I need. However, I cannot get any event fired more than once. Even worse, Only the element that is in view on page load, triggers an event.

    My HTML looks like this:

    <body>
        <div class="section-container">
            <div class="section" id="section1">...</div>
            <div class="section" id="section2">...</div>
            <div class="section" id="section3">...</div>
            ...
        </div>
    </body>
    

    Each .section element has the following styling:

    .section {
        width: 100vw;
        height: 100vh;
        box-sizing : border-box;
        position   : relative;
    }
    

    The javascript:

    var sectionElements = $('.section');
    
    sectionElements.each(function (index, element) {
        var sectionWatcher = scrollMonitor.create(element);
        var elementId = element.getAttribute('id');
    
        sectionWatcher.fullyEnterViewport(function() {
            console.log(elementId + ' fullyEnterViewport')
        });
    
        sectionWatcher.enterViewport(function () {
            console.log(elementId + ' has entered the viewport');
        });
    
        sectionWatcher.stateChange(function () {
    
            console.log(elementId + ' state has changed');
        });
    });
    

    Now, when I load the page, my console prints this:

    section1 fullyEnterViewport
    section1 has entered the viewport
    

    Which makes sense. But scrolling doesn't yield any other console messages. At all. Frustrating.

    There must be something I'm overlooking or forgot, but I can't see it. Anyone has an idea?

    opened by janjaap 6
  • Always create global scrollMonitor reference

    Always create global scrollMonitor reference

    Other libraries that depend on scrollMonitor shouldn't have to be AMD-aware. However, currently, if you have a non-AMD-aware library that depends on scrollMonitor (using window.scrollMonitor), it can't be shimmed because scrollMonitor will never add itself to the window object.

    opened by matthewwithanm 6
  • fix(container.ts): Check for window undefined error in SSR apps

    fix(container.ts): Check for window undefined error in SSR apps

    On SSR apps specially, those which do not use webpack,

    window is undefined, REFERENCE ERROR would be thrown

    to avoid reference errors in SSR apps this check will be helpful

    Closes #99

    opened by alokreddyk 5
  • bower registration was hijacked?!

    bower registration was hijacked?!

    Running bower info scrollMonitor started to return this in the last few hours:

    image

    It points to this repo: https://github.com/sakabako/scrollMonitor Which contains some minified code which I suspect is malicious, but didn't verify yet

    opened by shahata 5
  • document.body is null

    document.body is null

    Hi, i made a simple call of scroll monitor like this

    import 'scrollmonitor'

    but as soon as the module is load (webpack compile), i've got an error

    document.body is null

    My function is call with document.addEventListener("DOMContentLoaded", function () {}

    i don't see why.

    Any suggestion ?

    Thank you

    opened by Colir 0
  • Getting some warning and that inturn breaking the functionality (v1.2.8)

    Getting some warning and that inturn breaking the functionality (v1.2.8)

    Hi All,

    Getting some warnings with latest version (1.2.8) like below (not pasted the entire error)

    Is this related to any configurational problems? How to solve this?

    WARNING in ./node_modules/scrollmonitor/dist/index.js 24:20-27 Critical dependency: require function is used in a way in which dependencies cannot be statically extracted @ ... WARNING in ./node_modules/scrollmonitor/dist/src/constants.js 5:20-27 Critical dependency: require function is used in a way in which dependencies cannot be statically extracted @ ...

    WARNING in ./node_modules/scrollmonitor/dist/src/watcher.js 5:20-27
    Critical dependency: require function is used in a way in which dependencies cannot be statically extracted
     @ ...
    
    WARNING in ./node_modules/scrollmonitor/dist/src/container.js 5:20-27
    Critical dependency: require function is used in a way in which dependencies cannot be statically extracted
     @ ....
    
    WARNING in ./node_modules/scrollmonitor/dist/src/types.js 5:20-27
    Critical dependency: require function is used in a way in which dependencies cannot be statically extracted
     @ ./node_modules/scrollmonitor/dist/index.js
    

    Thanks, Johns

    opened by johns-abraham-borngroup 3
  • `1.2.7` broke CJS

    `1.2.7` broke CJS

    Summary

    Hey!

    Seems the recent changes in version 1.2.7 broke regular non-ESM consumers:

    https://github.com/stutrek/scrollmonitor/commit/e7dbd0f3e9f48e95b9a9815a0790a5eb7c81cf34#diff-3d2b59189eeedc2d428ddd632e97658fe310f587f7cb63b01f9b98ffc11c0197R1

    Current workaround is to pin 1.2.6.

    opened by amitdahan 1
  • IsFullyEntered is triggered on DomContentLoaded for all items regardless of their state

    IsFullyEntered is triggered on DomContentLoaded for all items regardless of their state

    Initialising a monitor on DomContentLoaded the state is correctly identified in stateChange. However, adding a callback for fullyEnterViewport is also triggered for each item. I would have expected that to fire only for items actually moving into the viewport. Illustrated here, with console output: https://jsfiddle.net/ExternalUse/17wL9pxv/9/

    In entryChangeState, this produces correct results: `else if (this.isFullyInViewport):

    guid1is correctly considered fully entered here guid2is correctly considered fully entered here for the fullyEnteredCallback function entryFullyEnterViewport() { console.log(this.watchItem.id + " may to be incorrectly considered fully entered, this.isFullyInViewport is " + this.isFullyInViewport); } this is produced: guid1 may to be incorrectly considered fully entered, this.isFullyInViewport is true guid2 may to be incorrectly considered fully entered, this.isFullyInViewport is false guid3 may to be incorrectly considered fully entered, this.isFullyInViewport is false guid4 may to be incorrectly considered fully entered, this.isFullyInViewport is false ` ...

    It's also slightly weird that in stateChange, guid1 and 2 are caught by fullyEntered; in the fullyEntered callback only guid1 has isFullyInViewport == true.

    Also, for the first element (guid1) the log message is produced twice.

    I'm probably missing a trick, could you possibly help, please? Thank you very much.

    opened by Externaluse 0
  • maybe u don't want to just delete the last one.

    maybe u don't want to just delete the last one.

    thx, this is a great repo.

    i think, there may be should have a condition: this.container.watchers.splice(index, 1); otherwise, it's just delete the last wather.

    opened by cbbfcd 0
  • on scroll event

    on scroll event

    Hi. I would like to know if there a method to use the 'on scroll' function from scrollMonitor. The idea is to use it for create a custom parallax effect rather than coding another scrolling event handler. thank you, and especially thank for this lib !

    opened by Colir 1
Releases(v1.2.5)
  • v1.2.5(Nov 11, 2021)

  • v1.2.0(Dec 1, 2016)

    We're proud to announce the release of everyone's biggest request, using any arbitrary element as a scroll container!

    Simply create a container and use it just like you would in the old days.

    var scrollContainer = scrollMonitor.createContainer(element);
    var watcher = scrollContainer.create(childElement);
    

    Additionally, the tests are much easier to run.

    Enjoy!

    Source code(tar.gz)
    Source code(zip)
Owner
Stu Kabakoff
Stu Kabakoff
Animate elements as they scroll into view.

Animate elements as they scroll into view. Introduction ScrollReveal is a JavaScript library for easily animating elements as they enter/leave the vie

Julian Lloyd 21.2k Jan 4, 2023
Simple & lightweight (<4kb gzipped) vanilla JavaScript library to create smooth & beautiful animations when you scroll.

lax.js Simple & lightweight (<4kb gzipped) vanilla JavaScript library to create smooth & beautiful animations when you scroll. >> DEMO << What's new w

Alex Fox 9.4k Dec 29, 2022
Create an Apple-like one page scroller website (iPhone 5S website) with One Page Scroll plugin

#One Page Scroll 1.3.1 by Pete R. Create an Apple-like one page scroll website (iPhone 5S website) with One Page Scroll plugin Created by Pete R., Fou

Pete R. 9.6k Dec 31, 2022
Smooth WebGL Shader Transformations on Scroll

Rock the Stage with a Smooth WebGL Shader Transformation on Scroll A handy setup to create a smooth scroll based GLSL animation using Three.js and GSA

Faboolea 97 Dec 25, 2022
Largetable - jQuery plugin to scroll in/maximize large tables

largetable jQuery plugin to scroll in/maximize large tables Usage Install with npm: $ npm install largetable Then include largetable files in the HTM

Edinum 1 Feb 3, 2021
🚀 Performance focused, lightweight scroll animation library 🚀

Sal Performance focused, lightweight (less than 2.8 kb) scroll animation library, written in vanilla JavaScript. No dependencies! Sal (Scroll Animatio

Mirek Ciastek 3.4k Dec 28, 2022
▲ NEXT.JS - Example project using Next.js with Locomotive Scroll. (experimental)

nextjs-with-locomotive-scroll-example ?? NEXT.JS - Example project using Next.js with Locomotive Scroll. (experimental) ?? Demo ?? Getting started The

Bruno Silva 27 Dec 21, 2022
Better scroll event management using requestAnimationFrame.

Scroll Frame Better scroll event management using requestAnimationFrame. Install npm install @jamestomasino/scroll-frame Use const { addScrollListener

James Tomasino 2 Feb 12, 2022
Scroll made easy!

Demo - Installation - Methods - Callbacks - Styling ◼️ Features: ?? Native Scroll Behavior ?? Standardized events / shortcuts / API ?? Fast & Lightwei

Bruno Vieira 307 Nov 20, 2022
🚀 Scroll Follow Tab is a lightweight javascript library without jQuery, no dependencies.

Scroll Follow Tab is a lightweight javascript library without jQuery, no dependencies. It is used to make scrollspy effect for your menu, table of contents, etc. Only 21.7Kb.

Hieu Truong 11 Jun 20, 2022
🛤 Detection of elements in viewport & smooth scrolling with parallax.

Locomotive Scroll Detection of elements in viewport & smooth scrolling with parallax effects. Installation ⚠️ Scroll-hijacking is a controversial prac

Locomotive 5.9k Dec 31, 2022
fullPage plugin by Alvaro Trigo. Create full screen pages fast and simple

fullPage.js English | Español | Français | Pусский | 中文 | 한국어 Available for Vue, React and Angular. | 7Kb gziped | Created by @imac2 Demo online | Cod

Álvaro 34.3k Jan 4, 2023
Simple and tiny JavaScript library that adds parallax animations on any images

simpleParallax.js simpleParallax.js is a very simple and tiny Vanilla JS library that adds parallax animations on any images. Where it may be laboriou

Geoffrey 1.5k Jan 3, 2023
Give your pages some headroom. Hide your header until you need it

Headroom.js Headroom.js is a lightweight, high-performance JS widget (with no dependencies) that allows you to react to the user's scroll. The header

Nick Williams 10.9k Jan 4, 2023
Stand-alone parallax scrolling library for mobile (Android + iOS) and desktop. No jQuery. Just plain JavaScript (and some love).

Please note: skrollr hasn't been under active development since about September 2014 (check out the contributions graphs on https://github.com/Prinzho

Alexander Prinzhorn 18.6k Jan 3, 2023
Customizable, Pluginable, and High-Performance JavaScript-Based Scrollbar Solution.

Smooth Scrollbar Customizable, Flexible, and High Performance Scrollbars! Installation ⚠️ DO NOT use custom scrollbars unless you know what you are do

Daofeng Wu 3k Jan 1, 2023
Create a deep copy of a set of matched elements with the dynamic state of all form elements copied to the cloned elements.

jq-deepest-copy FUNCTION: Create a deep copy of a set of matched elements while preserving the dynamic state of any matched form elements. Example Use

Michael Coughlin 5 Oct 28, 2022
Create a beautiful 3D tilted effect on scroll with jQuery Tilted Page Scroll plugin

#Tilted Page Scroll by Pete R. Create a beautilful 3D tilted scrolling effect for your website with jQuery Tilted Page Scroll. Created by Pete R., Fou

Pete R. 321 Sep 18, 2022
Scroll-stash - A JavaScript plugin to help preserve an element's scroll position.

scroll-stash A JavaScript plugin to help preserve an element's scroll position. CodePen Example Installation npm install scroll-stash JavaScript Impo

Sebastian Nitu 5 Sep 5, 2022
API Observability. Trace API calls and Monitor API performance, health and usage statistics in Node.js Microservices.

swagger-stats | API Observability https://swaggerstats.io | Guide Trace API calls and Monitor API performance, health and usage statistics in Node.js

slana.tech 773 Jan 4, 2023