-
-
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 |
Hi, glad this has helped you. And thanks for letting me know step 2 that I missed, gist updated!
history
is a react-router thing, which controls the browser history. If you have React Devtools installed, you can check this prop. Every page in /src/pages/
as well as those programmatically generated (template) has this prop.
You should pass this history
prop from your page to this AnchorLink component, else the browser history will produce unintended behavior.
src/pages/index.js
import React from "react"
import AnchorLink from "../components/AnchorLink"
const Homepage = props => (
<div>
<h1>Hi, welcome to homepage</h1>
<AnchorLink history={props.history} />
</div>
)
Let me know if this works!
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 inAnchorLink
, but its value isundefined
.
I could remove isRequired, but I think you required it not just for fun hehe (Line 70)
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 ;-)
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?
Works like a charm with
export default AnchorLink
at the end ;-)I use it with Gatsby.
Step 1:
npm install --save prop-types
Step 2: Add
'export default AnchorLink'
at the end of the componentStep 3: Source it with
import Anchorlink from '../components/anchorlink'
Step 4: Use it with
<Anchorlink href="#the-id">Linktext</Anchorlink>
Do you have a hint regarding the history? I get the following error in the console:
Sorry, I'm quite new to react and Gatsby :-)