Last active
September 27, 2021 12:36
-
-
Save ryanditjia/f66dd1d0e7dfd678a18dc4a15de8531d to your computer and use it in GitHub Desktop.
Gatsby smooth anchor scroll + history “bug” fix
This file contains hidden or 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
import React from 'react' | |
import { string, node, func, shape } from 'prop-types' | |
import { scrollToHref } from './helpers' | |
const handleClick = ({ href, callback, history, event }) => { | |
/* prevent normal link behavior */ | |
event.preventDefault() | |
/* call the function */ | |
scrollToHref(href) | |
/* | |
* only push to browser history if it’s a new anchor | |
* | |
* this fixes the “bug” in Gatsby (react-router ?) | |
* where the same hash can be pushed to history | |
* | |
* if you want to stick to default Gatsby behavior, remove the if condition block | |
* and uncomment history.push(href) following it | |
*/ | |
if (history.location.hash !== href) { | |
history.push(href) | |
} | |
/* Uncomment below line if you want default Gatsby behavior */ | |
// history.push(href) | |
/* perform callback (example: closing modal sidebar) */ | |
if (callback) { | |
callback() | |
} | |
} | |
const AnchorLink = ({ href, history, callback, children, ...restProps }) => ( | |
<a | |
href={href} | |
onClick={event => | |
handleClick({ | |
href, | |
callback, // optional callback, I use it to close modal sidebar | |
history, // history object props that layout and pages have | |
event, | |
}) | |
} | |
{...restProps} | |
> | |
{children} | |
</a> | |
) | |
AnchorLink.propTypes = { | |
href: string.isRequired, | |
callback: func, | |
children: node.isRequired, | |
history: shape({ | |
push: func.isRequired, | |
location: shape({ | |
hash: string.isRequired, | |
}).isRequired, | |
}).isRequired, | |
} | |
AnchorLink.defaultProps = { | |
callback: undefined, | |
} | |
export default AnchorLink |
This file contains hidden or 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
import { PureComponent } from 'react' | |
// npm install smoothscroll-polyfill | |
// this is a polyfill for Safari | |
import smoothscroll from 'smoothscroll-polyfill' | |
import { performScroll, scrollToHref } from './helpers' | |
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 = () => { | |
smoothscroll.polyfill() | |
window.onhashchange = handleHashChange | |
} | |
render() { | |
return null | |
} | |
} |
This file contains hidden or 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
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) | |
} |
This file contains hidden or 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
import React from 'react' | |
import HashChangeHandler from './HashChangeHandler' | |
const Layout = ({ children }) => ( | |
<div> | |
<header>Header</header> | |
<nav>Nav</nav> | |
{children} | |
<footer></footer> | |
<HashChangeHandler /> | |
</div> | |
) | |
// if you’re using Gatsby v1, change children to be a function, as such: children() | |
export default Layout |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
This worked very well for me, thank you.
Now I have adjusted my site to fetch all its content from Markdown-files with GraphQL, and it's not working anymore. I put the anchor link destination in "h2" tags, like h2 id="1">Section Title< /h2>. Do you know why this could happen, and any workaround for this problem?