Created
June 2, 2017 16:33
-
-
Save ebinion/823146456affae1da49c7ac1add188ed to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* Toggles a className on an element based on it's visiblity on screen | |
* @type {Class} | |
* @param {HTMLElement} element Element you'd like to toggle | |
* @param {Object} [options={}] Optional settings | |
*/ | |
class ScrollToggle { | |
constructor(element, options={}) { | |
// Setup default options | |
const defaultOptions = { | |
className: 'is--visible', | |
padVisibility: 100 | |
} | |
// Assign properties | |
this.element = element | |
this.options = Object.assign({}, defaultOptions, options) | |
// Use data attribute if available | |
if(this.element.getAttribute('data-scroll-toggle-class') !== '') { | |
this.options.className = this.element.getAttribute('data-scroll-toggle-class') | |
} | |
// This binding | |
this.addClass = this.addClass.bind(this) | |
this.getElementPosY = this.getElementPosY.bind(this) | |
this.getWindowPosY = this.getWindowPosY.bind(this) | |
this.isOnScreen = this.isOnScreen.bind(this) | |
// Eventlisteners | |
window.addEventListener('scroll', () => { | |
window.requestAnimationFrame(this.addClass) | |
}) | |
// Initial call | |
this.addClass() | |
} | |
/** | |
* Adds this.options.className to element when visible on screen | |
*/ | |
addClass() { | |
if(this.isOnScreen()) { | |
this.element.classList.add(this.options.className) | |
} else { | |
this.element.classList.remove(this.options.className) | |
} | |
} | |
/** | |
* Determines whether element is on screen based on scroll position | |
* @return {Boolean} True if element is visible on screen | |
*/ | |
isOnScreen() { | |
const windowY = this.getWindowPosY() | |
const elementY = this.getElementPosY() | |
if(windowY.max >= elementY.min + this.options.padVisibility && windowY.min <= elementY.max - this.options.padVisibility) { | |
return true | |
} else { | |
return false | |
} | |
} | |
/** | |
* Gets the offsetTop of the window and end of visible area | |
* @return {Object} Object.min and Object.max properties define the visible coordinates | |
*/ | |
getWindowPosY() { | |
return { | |
min: window.scrollY, | |
max: window.scrollY + window.innerHeight | |
} | |
} | |
/** | |
* Gets the offsetTop of this.element and end of its visible area | |
* @return {Object} Object.min and Object.max properties define the visible coordinates | |
*/ | |
getElementPosY() { | |
const body = document.querySelector('body') | |
let offsetY = this.element.offsetTop | |
// Let's loop through offsetParents until we reach the body | |
let element = this.element | |
while (element.offsetParent !== body) { | |
offsetY += element.offsetParent.offsetTop | |
element = element.offsetParent | |
} | |
return { | |
min: offsetY, | |
max: offsetY + this.element.offsetHeight | |
} | |
} | |
} | |
export default ScrollToggle |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment