-
-
Save ryanditjia/f66dd1d0e7dfd678a18dc4a15de8531d to your computer and use it in GitHub Desktop.
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 |
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 | |
} | |
} |
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) | |
} |
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 your feedback! props.history is working, but hash is required:
Warning: Failed prop type: The prop history.hash is marked as required in AnchorLink, but its value is undefined.
I could remove isRequired, but I think you required it not just for fun hehe (Line 70)
This was a mistake on my part, hash
should be nested inside location
, gist updated!
And is it possible to open an other page and scroll to a specific anchor? For example in a slider on the homepage with a link to a subpage. (from / to /subpage/#anchor). I tried it in such a setup and get an error in line #6.
SyntaxError: Failed to execute 'querySelector' on 'Document': '/think-tank/#post-quantum-crypto' is not a valid selector.
Currently I try to fix it in IE11 & Edge. Chrome and Firefox is working fine... as usual ;-)
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.
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?
And is it possible to open an other page and scroll to a specific anchor? For example in a slider on the homepage with a link to a subpage. (from / to /subpage/#anchor). I tried it in such a setup and get an error in line #6.
Currently I try to fix it in IE11 & Edge. Chrome and Firefox is working fine... as usual ;-)