34
loading...
This website collects cookies to deliver better user experience
import React from 'react';
export const useScrollLock = () => {
const lockScroll = React.useCallback(() => {
/* ... */
}, [])
const unlockScroll = React.useCallback(() => {
/* ... */
}, []);
return {
lockScroll,
unlockScroll
};
}
const PLP = () => {
const [quickViewProductId, setQuickViewProductId] = React.useState(0);
const { lockScroll, unlockScroll } = useScrollLock();
const displayQuickView = (productId) => {
lockScroll();
setQuickViewProductId(productId);
}
const hideQuickView = () => {
unlockScroll();
setQuickViewProductId(0);
}
return (
/* Products list and conditionally rendered quickview modal */
);
};
lockScroll
and unlockScroll
functions:const lockScroll = React.useCallback(() => {
document.body.style.overflow = 'hidden';
}, [])
const unlockScroll = React.useCallback(() => {
document.body.style.overflow = '';
}, [])
lockScroll
function is called. Take a close look at the right side of the image below, and you’ll notice the scrollbar disappears. Nothing wrong with it disappearing, this is exactly what we want, as that tells the browser that the user can’t scroll.const lockScroll = React.useCallback(() => {
document.body.style.overflow = 'hidden';
document.body.style.paddingRight = '17px'
}, [])
const unlockScroll = React.useCallback(() => {
document.body.style.overflow = '';
document.body.style.paddingRight = ''
}, [])
const lockScroll = React.useCallback(
() => {
const scrollBarCompensation = window.innerWidth - document.body.offsetWidth;
document.body.style.overflow = 'hidden';
document.body.style.paddingRight = `${scrollBarCompensation}px`;
}, [])
.button--help {
position: fixed;
right: 10px;
top: 90vh;
/* ... */
}
export const useScrollLock = () => {
const lockScroll = React.useCallback(
() => {
// ...
document.body.style.paddingRight = 'var(--scrollbar-compensation)';
document.body.dataset.scrollLock = 'true';
}, [])
const unlockScroll = React.useCallback(
() => {
// ....
delete document.body.dataset.scrollLock;
}, []);
React.useLayoutEffect(() => {
const scrollBarCompensation = window.innerWidth - document.body.offsetWidth;
document.body.style.setProperty('--scrollbar-compensation', `${scrollBarCompensation}px`);
}, [])
// ...
}
useLayoutEffect
to our Hook that will set the CSS custom property on the body element, and seeing as though we now have that compensation value available, we’re making use of it when adding padding to the body, rather than calculating it again. We’re also adding a data property onto the body element that we can use as a trigger to conditionally use the --scrollbar-compensation
variable.--scrollbar-compensation
value to be set on the body element multiple times if there are multiple components being rendered that make use of the useScrollLock
Hook, but setting a CSS custom property on an element doesn’t appear to cause a browser repaint, so there should be minimal performance drawbacks.--scrollbar-compensation
available to any element that is a child of the body element (which is every element), we can use it when styling those elements![data-scroll-lock] .button--help {
margin-right: var(--scrollbar-compensation);
}
const lockScroll = React.useCallback(
() => {
document.body.dataset.scrollLock = 'true';
document.body.style.overflow = 'hidden';
document.body.style.paddingRight = 'var(--scrollbar-compensation)';
if (isiOS) {
scrollOffset.current = window.pageYOffset;
document.body.style.position = 'fixed';
document.body.style.top = `-${scrollOffset.current}px`;
document.body.style.width = '100%';
}
}, [])
const unlockScroll = React.useCallback(
() => {
document.body.style.overflow = '';
document.body.style.paddingRight = '';
if (isiOS) {
document.body.style.position = '';
document.body.style.top = ``;
document.body.style.width = '';
window.scrollTo(0, scrollOffset.current);
}
delete document.body.dataset.scrollLock;
}, []);
position
=
'fixed'
and then programmatically offset the body to match the current scroll distance, which will compensate for the browser wanting to display the top of the body content at the top of the viewport.