main/ScrollHandler.js

import throttle from 'lodash/throttle';
import customEvent from '../utils/customEvent';
import supportsPassiveEvent from '../utils/supportsPassiveEvent';

/**
 * ScrollHandler
 *
 * Monitor scroll event and apply custom css to indicate
 * if the user is scrolling up or down a page.
 *
 * @module ScrollHandler
 */

const ScrollHandler = {
	selectors: {
		scrollListener: '[data-scrollListener]',
		container: '.l-container',
	},

	states: {
		up: 'is-scrolling-up',
		down: 'is-scrolling-down',
	},

	thresholds: {
		top: 0,
		scroll: 100,
	},

	currentScrollY: 0,

	currentDirection: '',

	$container: false,

	init() {
		if ( document.querySelectorAll( this.selectors.scrollListener ).length > 0 ) {
			this.$container = document.querySelector( this.selectors.container );

			// If browser supports passive event, add passive option.
			// This is done so handling of the scroll event does not block the scrolling itself
			window.addEventListener( 'scroll', throttle( () => {
				this.handleScroll();
			}, 200 ), supportsPassiveEvent ? { passive: true } : false );
		}
	},

	/**
	 * Handles page scrolling
	 *
	 * @method handleScroll
	 */
	handleScroll() {
		// Determine scroll direction.
		let direction = '';
		let distance = window.pageYOffset - this.currentScrollY;

		if ( distance > 0 ) {
			// scrolling down
			direction = 'down';
		} else if ( distance < 0 ) {
			// scrolling up
			direction = 'up';
		}

		distance = Math.abs( distance );

		// When the scroll position does not exceed the top threshold
		// consider user has scrolled to the top.
		if ( window.pageYOffset <= this.thresholds.top ) {
			// Remove scroll direction css classes.
			this.$container.classList.remove( this.states.up );
			this.$container.classList.remove( this.states.down );

			// Reset the current scroll direction.
			this.currentDirection = '';

			// Dispatch event to flag scroll to top.
			customEvent.fire( window, customEvent.SCROLLED_TO_TOP, {});

			return;
		}

		// Remember scroll position.
		this.currentScrollY = window.pageYOffset;

		// Fire event whenever the scroll distance is beyond the threshold.
		if ( distance >= this.thresholds.scroll ) {
			customEvent.fire( window, customEvent.SCROLLED, {
				direction,
				distance,
			});
		}

		// Scroll direction changed.
		if ( direction !== this.currentDirection ) {
			// Add / remove css class based on scroll direction.
			this.$container.classList.remove( this.states[this.currentDirection]);
			this.$container.classList.add( this.states[direction]);

			// Remember the current scroll direction.
			this.currentDirection = direction;

			// Dispatch event to flag change of scroll direction.
			customEvent.fire( window, customEvent.SCROLL_DIRECTION_CHANGED, {
				direction,
				distance,
			});
		}
	},
};

export default ScrollHandler;