Scroll toTop or Bottom
🔝 Have you tried Scroll to the top of the page in Gatsby with a link to top, I have tried modules and some simple hash link code but and all of the modules and my code used the .window function to go to top. Which does not work in Gatsby without a work around, As in the code Below.
import Scroll from 'react-scroll'
Then tell React that, window !== undefined
.
// eslint-disable-next-line valid-typeof
if (typeof window !== undefined) { require('react-scroll') }
Such as the errors I got from react-scroll, and the workaround I used, "Which Works", but I did not like the implementation
So I came up with my own digging through React Repos. With React we can use handleScroll and window.pageYOffset to go to top or bottom of the page or anything.
import React, { useState, useEffect } from 'react'
// and then use handleScroll and window.pageYOffset to go to top or bottom
const scroll = ({ mode, to }) =>
window[`scroll` + mode]({ top: to, behavior: `smooth` })
const handleScroll = () => {
if (window.pageYOffset > showBelow) {
if (!show) setShow(true)
} else {
if (show) setShow(false)
}
}
styled icon styledArrow
import React, { useState, useEffect } from 'react'import { Arrow } from './styles'
const Scroll = ({
direction = `up`, by,
to,
showBelow,
className,
size = `1.7em`,
}) => {
if (![`up`, `down`].includes(direction))
throw TypeError(
`Scroll component's direction prop must be either 'up' or 'down'`
)
if (to && (typeof to !== `number` || to <= 0))
throw TypeError(`Scroll component's to prop must be a positive number`)
if (by && typeof by !== `number`)
throw TypeError(`Scroll component's by prop must be a number`)
const [show, setShow] = useState(showBelow ? false : true)
const scroll = ({ mode, to }) =>
window[`scroll` + mode]({ top: to, behavior: `smooth` })
const handleScroll = () => {
if (window.pageYOffset > showBelow) {
if (!show) setShow(true)
} else {
if (show) setShow(false)
}
}
const handleClick = () => {
if (to) scroll({ mode: `To`, to: to * window.innerHeight })
else if (by) scroll({ mode: `By`, to: by * window.innerHeight })
else if (direction === `up`) scroll({ mode: `To`, to: 0 })
else scroll({ mode: `To`, to: document.body.scrollHeight })
}
useEffect(() => {
if (showBelow) {
window.addEventListener(`scroll`, handleScroll)
return () => window.removeEventListener(`scroll`, handleScroll)
}
})
const arrowProps = { show, direction, className, size }
return <Arrow onClick={handleClick} {...arrowProps} />
}
export default Scroll
styles.js Code
import styled from 'styled-components'
import { ArrowDownCircle as Down } from '@styled-icons/feather/ArrowDownCircle'
import { ArrowUpCircle as Up } from '@styled-icons/feather/ArrowUpCircle'
export const Arrow = styled(Down).attrs(({ direction, size }) => ({
as: direction === `up` && Up,
size,
}))`
${({ theme, show, size }) => `
z-index: 2;
background: ${theme.darkOrange};
color: ${theme.textColor};
border-radius: 50%;
transition: ${theme.shortTrans};
position: fixed;
bottom: 2.5em;
opacity: ${show ? 1 : 0};
visibility: ${show ? `visible` : `hidden`};
:hover {
transform: scale(1.15);
background: ${theme.orange};
}
right: calc(1vw - ${size} / 1);`}
`
Add Scroll
<Scroll
showBelow={1500}
css='position: fixed; right: 1.3em; bottom: 1.3em;'
/>
Scroll Down
import React, { useState, useEffect } from 'react'
import { Arrow } from './styles'
const ScrollDown = ({
direction = `down`,
by,
to,
showBelow,
className,
size = `1.7em`,
}) => {
if (![`up`, `down`].includes(direction))
throw TypeError(
`Scroll component's direction prop must be either 'up' or 'down'`
)
if (to && (typeof to !== `number` || to <= 0))
throw TypeError(`Scroll component's to prop must be a positive number`)
if (by && typeof by !== `number`)
throw TypeError(`Scroll component's by prop must be a number`)
const [show, setShow] = useState(showBelow ? false : true)
const scroll = ({ mode, to }) =>
window[`scroll` + mode]({ top: to, behavior: `smooth` })
const handleScroll = () => {
if (window.pageYOffset > showBelow) {
if (!show) setShow(true)
} else {
if (show) setShow(false)
}
}
const handleClick = () => {
if (to) scroll({ mode: `To`, to: to * window.innerHeight })
else if (by) scroll({ mode: `By`, to: by * window.innerHeight })
else if (direction === `up`) scroll({ mode: `To`, to: 0 })
else scroll({ mode: `To`, to: document.body.scrollHeight })
}
useEffect(() => {
if (showBelow) {
window.addEventListener(`scroll`, handleScroll)
return () => window.removeEventListener(`scroll`, handleScroll)
}
})
const arrowProps = { show, direction, className, size }
return <Arrow onClick={handleClick} {...arrowProps} />
}
export default ScrollDown
Scroll styles.js
export const Arrow = styled(Up).attrs(({ direction, size }) => ({
as: direction === `down` && Down,
size,
}))`
${({ theme, show, size }) => `
z-index: 2;
background: ${theme.darkOrange};
color: ${theme.textColor};
border-radius: 50%;
transition: ${theme.shortTrans};
position: fixed;
top: 4.5em;
opacity: ${show ? 1 : 0};
visibility: ${show ? `visible` : `hidden`};
:hover {
transform: scale(1.15);
background: ${theme.orange};
}
right: calc(1vw - ${size} / 1);`}
`
Post Template
Only my post's are long enough to need scroll down
<ScrollDown
direction='down' to={15}
showAbove={1500}
css='position: fixed; right: 1em; top: 4.5em;'
/>
Scroll down is visible at the top of the page and is fixed. I think its cool and somewhat original. Not static like the Netlify build page has scroll to bottom of the build log or back to top which is pretty Cool.