Skip to content

Instantly share code, notes, and snippets.

@ryanditjia
Last active September 27, 2021 12:36
Show Gist options
  • Save ryanditjia/f66dd1d0e7dfd678a18dc4a15de8531d to your computer and use it in GitHub Desktop.
Save ryanditjia/f66dd1d0e7dfd678a18dc4a15de8531d to your computer and use it in GitHub Desktop.
Gatsby smooth anchor scroll + history “bug” fix
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
@ryanditjia
Copy link
Author

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!

@cgpro
Copy link

cgpro commented Jun 26, 2018

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)

@cgpro
Copy link

cgpro commented Jun 26, 2018

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 ;-)

@ryanditjia
Copy link
Author

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.

@cgpro
Copy link

cgpro commented Jun 26, 2018

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 :)

@ryanditjia
Copy link
Author

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.

@eivindflobak
Copy link

eivindflobak commented Oct 7, 2018

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