Skip to content

Instantly share code, notes, and snippets.

@theStrangeAdventurer
Last active July 17, 2019 10:00
Show Gist options
  • Save theStrangeAdventurer/401d56ade2499870d247d0810fb4bb3e to your computer and use it in GitHub Desktop.
Save theStrangeAdventurer/401d56ade2499870d247d0810fb4bb3e to your computer and use it in GitHub Desktop.
Back to top vanilla JS button (Конпка "Наверх", Ванильный js)
class Upstair {
constructor(options={}) {
this.duration = options.duration || 350
this.callback = options.callback || ((f) => f)
this.scrollStartHeight = options.scrollStartHeight || 100
this.btnColor = options.btnColor || '#444'
this.btnStrokeWidth = options.btnStrokeWidth || 2
this.stickySelector = options.stickySelector || null
this.html = options.html || this.getDefaultBtnHtml()
this.defaultStyle = {
position: 'fixed',
right: '4vw',
bottom: '4vh',
cursor: 'pointer',
opacity: '0',
visibility: 'hidden',
transition: 'opacity .3s linear, visibility .3s linear',
MozTransition: 'opacity .3s linear, visibility .3s linear',
WebkitTransition: 'opacity .3s linear, visibility .3s linear',
}
if (!options.style || typeof ( options.style !== 'object' ))
options.style = {};
this.style = Object.assign({}, this.defaultStyle, options.style)
this.scroll = this.scroll.bind(this)
this.isScrolledIntoView = this.isScrolledIntoView.bind(this)
this.setElemBottom = this.setElemBottom.bind(this)
this.createElements = this.createElements.bind(this)
this.getWindowHeight = this.getWindowHeight.bind(this)
this.getScrollY = this.getScrollY.bind(this)
}
getDefaultBtnHtml() {
return (
`<svg xmlns="http://www.w3.org/2000/svg"
width="40"
height="16"
viewBox="0 0 40 16"
fill="none"
>
<path d="M2 14L19.1151 2.37468C19.4491 2.1478 19.8868 2.14406 20.2246 2.36519L38 14"
stroke="${this.btnColor}"
stroke-width="${this.btnStrokeWidth}"
stroke-linecap="round"
/>
</svg>`
)
}
scroll(destination) {
const start = window.pageYOffset;
const startTime = 'now' in window.performance ? performance.now() : new Date().getTime();
const documentHeight = Math.max(document.body.scrollHeight, document.body.offsetHeight, document.documentElement.clientHeight, document.documentElement.scrollHeight, document.documentElement.offsetHeight);
const windowHeight = this.getWindowHeight();
const destinationOffset = (typeof destination === 'number') ?
destination :
destination.offsetTop;
const destinationOffsetToScroll = Math.round(documentHeight - destinationOffset < windowHeight ? documentHeight - windowHeight : destinationOffset);
if ('requestAnimationFrame' in window === false) {
window.scroll(0, destinationOffsetToScroll);
return this.callback();
}
const __scroll = () => {
const now = 'now' in window.performance ?
performance.now() :
new Date().getTime();
const time = Math.min(1, ((now - startTime) / this.duration));
const timeFunction = time; // linear
window.scroll(0, Math.ceil((timeFunction * (destinationOffsetToScroll - start)) + start));
if (window.pageYOffset === destinationOffsetToScroll) {
return this.callback();
}
requestAnimationFrame(__scroll);
}
__scroll();
}
createElements() {
this.stickyContainer = document.createElement("DIV")
Object.assign(this.stickyContainer.style, this.style)
this.stickyContainer.innerHTML = this.html
this.stickySelector = document.querySelector(this.stickySelector)
this.stickyContainer.addEventListener( 'click', () => this.scroll(0) )
document.body.appendChild(this.stickyContainer)
}
isScrolledIntoView(elem) {
const scrollY = window.scrollY
const docViewBottom = scrollY + this.getWindowHeight();
const elemTop = elem.offsetTop;
return ((elemTop <= docViewBottom) && (elemTop >= scrollY));
}
setElemBottom(elem, target, margin) {
const docViewBottom = this.getScrollY() + this.getWindowHeight();
const elemTop = target.offsetTop;
elem.style.bottom = `calc(${margin} + ${docViewBottom - elemTop}px)`
}
getScrollY() {
return window.scrollY
}
getWindowHeight() {
return (
window.innerHeight ||
document.documentElement.clientHeight ||
document.getElementsByTagName('body')[0].clientHeight
);
}
init() {
console.log("|",this.stickySelector)
let stateCheck = setInterval(() => {
if (document.readyState === 'complete') {
clearInterval(stateCheck);
this.createElements()
const raf = () => {
if (window.scrollY > this.scrollStartHeight) {
this.stickyContainer.style.opacity = '1'
this.stickyContainer.style.visibility = 'visible'
} else {
this.stickyContainer.style.opacity = '0'
this.stickyContainer.style.visibility = 'hidden'
}
if (this.stickySelector) {
if ( this.isScrolledIntoView(this.stickySelector) )
this.setElemBottom(this.stickyContainer, this.stickySelector, this.style.bottom)
else
this.stickyContainer.style.bottom = this.style.bottom
}
requestAnimationFrame(raf);
}
raf();
}
}, 100);
}
};
@theStrangeAdventurer
Copy link
Author

theStrangeAdventurer commented Apr 8, 2019

The button is very easy to set up:

  1. Connect the file with the above code
    <script src="path/to/file-with-code"></script>
  2. Transfer necessary parameters
new Upstair({
    btnColor: '#282828', // button color, not work with custom html
    html: '<div>Back to top!</div>', // Custom btn html (default svg button) 
    duration: 250, 
    stickySelector: '#footer', // Selector for footer, if specified, the button will stick to the footer during scrolling (default NULL )
    callback: () => console.log('button clicked and scrolled to top)
}).init();

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