Skip to content

Instantly share code, notes, and snippets.

@ryanditjia
Created April 11, 2018 05:58
Show Gist options
  • Save ryanditjia/a1b8c12aa82efc286e865e56570ec63e to your computer and use it in GitHub Desktop.
Save ryanditjia/a1b8c12aa82efc286e865e56570ec63e to your computer and use it in GitHub Desktop.
Gatsby smooth scroll
/*
* Use this AnchorLink component instead of your normal <a> tag for page anchors
*
* RouteContext.Consumer here is basically passing history object
* which is available as props with Gatsy’s layout and all page components
* you don’t have to use context if you don’t want to
*
* callback is optional
*/
import React from 'react'
import { string, node, func } from 'prop-types'
import { scrollToHref } from '../../utils/helpers/scroll'
import RouteContext from '../../contexts/route'
const handleClick = ({
href, callback, history, event,
}) => {
/* prevent normal link behavior */
event.preventDefault()
/* temporarily hijack the window event handler */
const tempFuncHolder = window.onhashchange
window.onhashchange = undefined
/* invoke the scroll function (always, regardless of browser history push) */
scrollToHref(href)
/*
* push to browser history, but only if it’s a new anchor
* this fixes a bug in Gatsby where you can keep on clicking
* the same anchor over and over again, tainting history
*/
if (history && history.location.hash !== href) {
history.push(href)
}
/* perform callback (example: closing modal sidebar) */
if (callback) {
callback()
}
/* return what you hijacked */
window.onhashchange = tempFuncHolder
}
export default function AnchorLink({
href, callback, children, ...restProps
}) {
return (
<RouteContext.Consumer>
{({ history }) => (
<a
href={href}
onClick={event =>
handleClick({
href,
callback,
history,
event,
})
}
{...restProps}
>
{children}
</a>
)}
</RouteContext.Consumer>
)
}
AnchorLink.propTypes = {
href: string.isRequired,
callback: func,
children: node.isRequired,
}
AnchorLink.defaultProps = {
callback: undefined,
}
/*
* import to your layout/index.js
*/
import { PureComponent } from 'react'
import { performScroll, scrollToHref } from '../../utils/helpers/scroll'
const handleHashChange = () => {
if (window.location.hash) {
scrollToHref(window.location.hash)
} else {
/* hash doesn’t exist, meaning it just got removed. scroll to the very top */
performScroll(0)
}
}
export default class HashChangeHandler extends PureComponent {
componentDidMount = () => {
window.onhashchange = handleHashChange
}
render() {
return null
}
}
export const performScroll = (top) => {
/* invoke scroll, with behavior smooth (not supported in Safari as of writing) */
window.scrollTo({
behavior: 'smooth',
top,
})
}
export const scrollToHref = (href) => {
/* destination element to scroll to */
const destinationElement = document.querySelector(href)
performScroll(destinationElement.offsetTop)
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment