From 4721b1855fd3a628071070f1118d9e2388432d89 Mon Sep 17 00:00:00 2001 From: Rafael Klynger Date: Wed, 22 Jan 2020 11:53:57 -0300 Subject: [PATCH] Pass to lock the scroll in the body element instead of the html element --- CHANGELOG.md | 2 ++ react/modules/useLockScroll.tsx | 61 +++++++++++++++++++-------------- react/styles.css | 6 ++++ 3 files changed, 44 insertions(+), 25 deletions(-) create mode 100644 react/styles.css diff --git a/CHANGELOG.md b/CHANGELOG.md index d10b04e..3c30f01 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -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 diff --git a/react/modules/useLockScroll.tsx b/react/modules/useLockScroll.tsx index f1101c6..34d0635 100644 --- a/react/modules/useLockScroll.tsx +++ b/react/modules/useLockScroll.tsx @@ -1,4 +1,5 @@ import { useRef, useState, useEffect } from 'react' +import styles from '../styles.css' // https://stackoverflow.com/a/3464890/5313009 const getScrollPosition = () => { @@ -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) @@ -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. @@ -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 diff --git a/react/styles.css b/react/styles.css new file mode 100644 index 0000000..9468486 --- /dev/null +++ b/react/styles.css @@ -0,0 +1,6 @@ +.hiddenBody { + overflow: hidden; + position: fixed; + bottom: 0; + left: 0; +}