-
-
Save reecelucas/2f510e6b8504008deaaa52732202d2da to your computer and use it in GitHub Desktop.
import { useRef } from 'react'; | |
const safeDocument = typeof document !== 'undefined' ? document : {}; | |
/** | |
* Usage: | |
* const [blockScroll, allowScroll] = useScrollBlock(); | |
*/ | |
export default () => { | |
const scrollBlocked = useRef(); | |
const html = safeDocument.documentElement; | |
const { body } = safeDocument; | |
const blockScroll = () => { | |
if (!body || !body.style || scrollBlocked.current) return; | |
const scrollBarWidth = window.innerWidth - html.clientWidth; | |
const bodyPaddingRight = | |
parseInt(window.getComputedStyle(body).getPropertyValue("padding-right")) || 0; | |
/** | |
* 1. Fixes a bug in iOS and desktop Safari whereby setting | |
* `overflow: hidden` on the html/body does not prevent scrolling. | |
* 2. Fixes a bug in desktop Safari where `overflowY` does not prevent | |
* scroll if an `overflow-x` style is also applied to the body. | |
*/ | |
html.style.position = 'relative'; /* [1] */ | |
html.style.overflow = 'hidden'; /* [2] */ | |
body.style.position = 'relative'; /* [1] */ | |
body.style.overflow = 'hidden'; /* [2] */ | |
body.style.paddingRight = `${bodyPaddingRight + scrollBarWidth}px`; | |
scrollBlocked.current = true; | |
}; | |
const allowScroll = () => { | |
if (!body || !body.style || !scrollBlocked.current) return; | |
html.style.position = ''; | |
html.style.overflow = ''; | |
body.style.position = ''; | |
body.style.overflow = ''; | |
body.style.paddingRight = ''; | |
scrollBlocked.current = false; | |
}; | |
return [blockScroll, allowScroll]; | |
}; |
In this case you call blockScroll()
to prevent scrolling, and allowScroll()
to enable it.
dig it
Nice thank you !
Here is the typescript version if that can help. 😄 !
Awesome, thanks.
Typescript:
import { useRef } from "react"
const useScrollBlock = () => {
const scroll = useRef(false)
const blockScroll = () => {
if (typeof document === "undefined") return
const html = document.documentElement
const { body } = document
if (!body || !body.style || scroll.current) return
const scrollBarWidth = window.innerWidth - html.clientWidth
const bodyPaddingRight = parseInt(window.getComputedStyle(body).getPropertyValue("padding-right")) || 0
/**
* 1. Fixes a bug in iOS and desktop Safari whereby setting
* `overflow: hidden` on the html/body does not prevent scrolling.
* 2. Fixes a bug in desktop Safari where `overflowY` does not prevent
* scroll if an `overflow-x` style is also applied to the body.
*/
html.style.position = "relative" /* [1] */
body.style.position = "relative" /* [1] */
html.style.overflow = "hidden" /* [2] */
body.style.overflow = "hidden" /* [2] */
body.style.paddingRight = `${bodyPaddingRight + scrollBarWidth}px`
scroll.current = true
}
const allowScroll = () => {
if (typeof document === "undefined") return
const html = document.documentElement
const { body } = document
if (!body || !body.style || !scroll.current) return
html.style.position = ""
html.style.overflow = ""
body.style.position = ""
body.style.overflow = ""
body.style.paddingRight = ""
scroll.current = false
}
return [blockScroll, allowScroll]
}
export { useScrollBlock }
Awesome! This is exactly what I was looking for. Thanks!
Thanks for that.
Personally, I have to set overflow as 'auto' to make allowScroll working in:
html.style.overflow = "auto'";
body.style.overflow = "auto";
(Working on Mac + vscode + Chrome)
When I call disableBlock()
, my absolutely positioned sidebar is moved up for some reason.
@reecelucas any ideas?
PS: Fixed. I was using position:sticky
. For some reason @reecelucas doesn't work with sticky
. After I changed it to position:fixed
it works fine.
Amazing hook thank you 👍👍
one small suggestion, try replacing parseInt
with Number
to avoid the radix parameter issue.
Number(window.getComputedStyle(body).getPropertyValue('padding-right')) || 0;
Awesome Hook & Appreciate 🙏🏿 . Added hook to my tool kit!!
Is very useful, thanks a lot.
+1 well done
Hi... I had to explore it differently for my specific scenario... I hope that it can be helpful to someone.
const blockScroll = () => {
if (!body || !body.style || scrollBlocked.current) return
const scrollBarWidth = window.innerWidth - html.clientWidth
const bodyPaddingRight =
parseInt(window.getComputedStyle(body).getPropertyValue("padding-right")) || 0
/**
* 1. Fixes a bug in iOS and desktop Safari whereby setting
* `overflow: hidden` on the html/body does not prevent scrolling.
* 2. Fixes a bug in desktop Safari where `overflowY` does not prevent
* scroll if an `overflow-x` style is also applied to the body.
*/
html.style.position = "relative" /* [1] */
html.style.overflow = "hidden" /* [2] */
html.style.height = "100%"
body.style.position = "relative" /* [1] */
body.style.overflow = "hidden" /* [2] */
body.style.height = "100%"
body.style.paddingRight = `${bodyPaddingRight + scrollBarWidth}px`
scrollBlocked.current = true
}
const allowScroll = () => {
if (!body || !body.style || !scrollBlocked.current) return
html.style.position = ""
html.style.overflow = ""
html.style.height = ""
body.style.position = ""
body.style.overflow = ""
body.style.paddingRight = ""
body.style.maxHeight = ""
scrollBlocked.current = false
}
I had to add this height property to body and html, otherwise, I was seeing some bottom screen black background.
Thanks :)
Thanks, was very useful to me!
This is exatcly what I was looking for, and with such a simple solution, thank you very much man
this is truly incredible, I've already implemented in my project and gave you credit in code. will give you credit on readme when it's done!
I logged in all my accounts just to give a star in each of them. Thank you, is was very useful!
This doesn't work for me
The block scroll worked, but allowScroll doesn't work and am using nextjs
Thank you so much
Thank you!! This works perfectly :)
phenomenal !
++ thanks!
Thanks!
Thanks a lot 👍
Works! Thank you!!!!!!!!!
improved hook for TypeScript:
import { useRef, useEffect } from 'react';
const safeDocument = typeof document !== 'undefined' ? document : {};
/**
* Usage:
* const [blockScroll, allowScroll] = useScrollBlock();
*/
export default function useScrollBlock(): [() => void, () => void] {
const scrollBlocked = useRef<boolean>(false);
const { body } = safeDocument as Document;
const html = (safeDocument as Document).documentElement;
const blockScroll = () => {
if (!body || !body.style || scrollBlocked.current) return;
const scrollBarWidth = window.innerWidth - (html?.clientWidth || 0);
const bodyPaddingRight =
parseInt(
window.getComputedStyle(body).getPropertyValue('padding-right'),
) || 0;
/**
* 1. Fixes a bug in iOS and desktop Safari whereby setting
* `overflow: hidden` on the html/body does not prevent scrolling.
* 2. Fixes a bug in desktop Safari where `overflowY` does not prevent
* scroll if an `overflow-x` style is also applied to the body.
*/
if (html) {
html.style.position = 'relative'; /* [1] */
html.style.overflow = 'hidden'; /* [2] */
}
body.style.position = 'relative'; /* [1] */
body.style.overflow = 'hidden'; /* [2] */
body.style.paddingRight = `${bodyPaddingRight + scrollBarWidth}px`;
scrollBlocked.current = true;
};
const allowScroll = () => {
if (!body || !body.style || !scrollBlocked.current) return;
if (html) {
html.style.position = '';
html.style.overflow = '';
}
body.style.position = '';
body.style.overflow = '';
body.style.paddingRight = '';
scrollBlocked.current = false;
};
useEffect(() => {
return () => {
if (scrollBlocked.current) {
allowScroll();
}
};
}, []);
return [blockScroll, allowScroll];
}
Thanks Lucas :)
thanks @litehacker for the TS version
How to use it?
const [blockScroll, allowScroll] = useScrollBlock();
After I need to write "allowScroll(false)" when I want to block scroll?