import ScrollingNav from '../main/ScrollingNav';
import customEvent from '../utils/customEvent';
/**
* Displays 'Back to Top' button when user scrolls up quickly
*
* @module BackToTop
* @prop {Integer} oldScrollValue - initial vertical position on the page
* @prop {Integer} tabletBreakpoint - Tablet-portrait breakpoint, same as in CSS
* @prop {String} tagMetaSelector - DOM reference to tag meta header
* @prop {String} buttonDivSelector - Class reference for Back to Top button container
* @prop {String} buttonDivModifier - CSS modifier accounting for larger tag meta header offset
* @prop {String} buttonSelector - Class reference for Back to Top button
* @prop {String} fadeInClass - Fade in animation class
* @prop {String} fadeOutClass - Fade out animation class
* @prop {String} resetClass - 3d transform animation class
* @prop {Object} $buttonDiv - DOM reference to Back to Top button
*/
const BackToTop = {
oldScrollValue: 0,
tabletBreakpoint: 768, // Same as css breakpoint tablet-portrait
tagMetaSelector: '#custom-navbar',
buttonDivSelector: '.c-backToTop',
buttonDivModifier: 'c-backToTop--tagMeta',
buttonSelector: '.c-backToTop__button',
fadeInClass: 'is-slid-and-faded-in',
fadeOutClass: 'is-slid-and-faded-out',
resetClass: 'is-reset',
hiddenClass: 'is-hidden',
$buttonDiv: null,
/**
* Initializes 'Back To Top' button on article pages
*
* @method init
*/
init() {
// Make sure there is a button container on the page
if ( ! document.querySelector( this.buttonDivSelector ) ) {
return;
}
this.$buttonDiv = document.querySelector( this.buttonDivSelector );
// Don't run on screens larger than tablet-portrait
if ( window.innerWidth >= this.tabletBreakpoint ) {
return;
}
// Detect if this article has a tag meta header
if ( document.querySelector( this.tagMetaSelector ) ) {
this.$buttonDiv.classList.add( this.buttonDivModifier );
}
// Bind click event to the button
const $button = document.querySelector( this.buttonSelector );
if ( $button ) {
// markup has is-hidden applied to avoid big gap rendering on page load.
$button.classList.remove( this.hiddenClass );
$button.addEventListener( 'click', () => {
this.handleBackToTopClick();
});
}
window.addEventListener( customEvent.SCROLLED_TO_TOP, () => this.handleScrollToTop() );
window.addEventListener( customEvent.SCROLLED, evt => this.handleUpwardScroll( evt ) );
window.addEventListener( customEvent.SCROLL_DIRECTION_CHANGED,
evt => this.handleDownwardScroll( evt ) );
},
/**
* Handles upward page scroll.
*
* @method handleScroll
*/
handleUpwardScroll( event ) {
const { direction } = event.detail;
// When user scrolls up by 100px or more, show button if it's currently not visible
if ( 'up' === direction
&& ! BackToTop.$buttonDiv.classList.contains( BackToTop.resetClass ) ) {
BackToTop.showButton();
}
},
/**
* Handles downward page scroll.
*
* @method handleScroll
*/
handleDownwardScroll( event ) {
const { direction } = event.detail;
// When user scrolls down, hide button if it's currently visible
if ( 'down' === direction ) {
BackToTop.hideButton();
}
},
/**
* Handles page scroll back to the top.
*
* @method handleScroll
*/
handleScrollToTop() {
// When page scrolls all the way to the top, hide button
BackToTop.hideButton();
ScrollingNav.resetTransitions();
},
/**
* Handles click on 'Back To Top' button
*
* @method handleBackToTopClick
*/
handleBackToTopClick() {
window.scroll({ top: 0, behavior: 'smooth' });
},
/**
* Brings 'Back To Top' button out of view
*
* @method hideButton
*/
hideButton() {
this.$buttonDiv.classList.remove( this.resetClass );
this.$buttonDiv.classList.remove( this.fadeInClass );
this.$buttonDiv.classList.add( this.fadeOutClass );
},
/**
* Brings 'Back To Top' button into view
*
* @method showButton
*/
showButton() {
this.$buttonDiv.classList.add( this.resetClass );
this.$buttonDiv.classList.add( this.fadeInClass );
this.$buttonDiv.classList.remove( this.fadeOutClass );
},
};
export default BackToTop;