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 |
Thank you for updating the gist! I've added a vanilla-smooth-scroll function... dont know why, scrollTo accepts only a number for x and y, but the behavior 'smooth' is totaly ignored... but it works for now in my case :)
I have modified the AnchorLink component to be closer to what I have on my site. If this confuses you, you can click Revisions to check the previous version! Hope this helps.
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?
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
This was a mistake on my part,
hash
should be nested insidelocation
, gist updated!So this doesn’t work in IE11 and Edge? I have only tried Safari, Chrome, Firefox 😄.
Anyway, I have another component inside of Layout that lets the smooth scrolling work when clicking the browser back and forward buttons. I name it HashChangeHandler and I’ve pasted the code in this gist.