Skip to content

Commit

Permalink
Pass to lock the scroll in the body element instead of the html element
Browse files Browse the repository at this point in the history
  • Loading branch information
Rafael Klynger committed Jan 22, 2020
1 parent cc05485 commit 4721b18
Show file tree
Hide file tree
Showing 3 changed files with 44 additions and 25 deletions.
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/)
and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html).

## [Unreleased]
### Changed
- `useLockScroll` now blocks the scroll of the body element instead of the html.

## [0.6.0] - 2020-01-06
### Added
Expand Down
61 changes: 36 additions & 25 deletions react/modules/useLockScroll.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { useRef, useState, useEffect } from 'react'
import styles from '../styles.css'

// https://stackoverflow.com/a/3464890/5313009
const getScrollPosition = () => {
Expand All @@ -13,6 +14,13 @@ const getScrollPosition = () => {
)
}

const propertiesNames = ['top', 'width'] as const

interface PropertyStyle {
name: keyof CSSStyleDeclaration
value: string
}

const useLockScroll = () => {
const initialized = useRef(false)
const [isLocked, setLocked] = useState(false)
Expand All @@ -34,18 +42,9 @@ const useLockScroll = () => {
*/
const shouldLockScroll = isLocked

const documentElement =
window && window.document && window.document.documentElement

if (documentElement) {
if (window && window.document) {
const bodyBounds = document.body.getBoundingClientRect()

document.body.style.width = shouldLockScroll
? `${bodyBounds.width}px`
: 'auto'

documentElement.style.overflow = shouldLockScroll ? 'hidden' : 'auto'

/** iOS doesn't lock the scroll of the body by just setting overflow to hidden.
* It requires setting the position of the HTML element to fixed, which also
* resets the scroll position.
Expand All @@ -66,24 +65,36 @@ const useLockScroll = () => {
setLockedScrollPosition(null)
}

documentElement.style.position = shouldLockScroll ? 'fixed' : 'static'

documentElement.style.top = shouldLockScroll
? `-${scrollPosition}px`
: 'auto'

documentElement.style.bottom = shouldLockScroll ? '0' : 'auto'
documentElement.style.left = shouldLockScroll ? '0' : 'auto'
const properties: PropertyStyle[] = [
{
name: 'top',
value: `-${scrollPosition}px`,
},
{
name: 'width',
value: `${bodyBounds.width}px`,
},
]

if (shouldLockScroll) {
properties.forEach(prop => {
window.document.body.style[prop.name as any] = prop.value
})

window.document.body.classList.add(styles.hiddenBody)
} else {
properties.forEach(prop => {
window.document.body.style.removeProperty(prop.name as any)
})
window.document.body.classList.remove(styles.hiddenBody)
}
}

return () => {
documentElement.style.overflow = 'auto'
documentElement.style.position = 'static'

documentElement.style.top = 'auto'
documentElement.style.bottom = 'auto'
documentElement.style.left = 'auto'
document.body.style.width = 'auto'
propertiesNames.forEach(prop => {
window.document.body.style.removeProperty(prop)
})
window.document.body.classList.remove(styles.hiddenBody)
}
}, [isLocked]) // eslint-disable-line react-hooks/exhaustive-deps
// ☝️ no need to trigger this on lockedScrollPosition changes
Expand Down
6 changes: 6 additions & 0 deletions react/styles.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
.hiddenBody {
overflow: hidden;
position: fixed;
bottom: 0;
left: 0;
}

0 comments on commit 4721b18

Please sign in to comment.