admin管理员组文章数量:1400628
I'm using next JS for my application. I have a sign in modal for my application, and I would like to fix the under lying page to not scroll while it is open. I could solve the issue with document.body.style.overflow = 'hidden'
when the modal is being open, but then the website jumps to occupy the space the scroll was present.
I would like to preserve the scroll bar yet disable the scroll. I'm calling the modal in a ponent, so CSS properties like overflow:hidden
only works on the respective ponent. Is there a way I could achieve whatever I'm trying to perform?
I'm using next JS for my application. I have a sign in modal for my application, and I would like to fix the under lying page to not scroll while it is open. I could solve the issue with document.body.style.overflow = 'hidden'
when the modal is being open, but then the website jumps to occupy the space the scroll was present.
I would like to preserve the scroll bar yet disable the scroll. I'm calling the modal in a ponent, so CSS properties like overflow:hidden
only works on the respective ponent. Is there a way I could achieve whatever I'm trying to perform?
2 Answers
Reset to default 4Phew. It took many hours of digging (bloggers and A.I. [NOT StackOverflow answers per se, especially not the accepted one here] seem weirdly content with the annoyingly-widespread solution of simply making the scrollbar temporarily disappear entirely, which I think is tacky/distracting), but I finally piled the 3 required ponents to disable the scrollbar while keeping it visible [only tested in Chromium(Edge) and the mobile emulator in Dev Tools]:
Block ScrollWheel Scroll
...
function App(){
const [isModalOpen, setIsModalOpen] = useState(false)
useEffect(() => {
const handleWindowWheel = (event: WheelEvent) => {
if (isModalOpen){
event.preventDefault();
}
};
window.addEventListener('wheel', handleWindowWheel, { passive: false });
return () => {
window.removeEventListener('wheel', handleWindowWheel);
};
}, [isModalOpen]);
return (
...
<button onClick={() => setIsModalOpen(true)}>Open Modal</button>
...
)
}
Block Click-and-Drag Scroll
...
function disableScroll() {
// Store the current X & Y scroll positions.
const scrollTop = window.pageYOffset || document.documentElement.scrollTop;
const scrollLeft = window.pageXOffset || document.documentElement.scrollLeft;
// If any scroll event is attempted on the window, change the window's
// onscroll function to always scroll back to the saved X & Y positions.
window.onscroll = function() {
window.scrollTo(scrollLeft, scrollTop);
};
}
function enableScroll() {
// Reset the window's onscroll function.
window.onscroll = function() {};
}
function App(){
const [isModalOpen, setIsModalOpen] = useState(false)
useEffect(() => {
if (isModalOpen) {
disableScroll();
} else {
enableScroll();
}
}, [isModalOpen]);
return (
...
<button onClick={() => setIsModalOpen(true)}>Open Modal</button>
...
)
}
Credit
The above snippet semi-blocks ScrollWheel scroll too, but it's ugly: It allows you to scroll the scrollbar with the wheel a whole ScrollWheel-click's distance, then visibly snaps the scrollbar back to where it originally was. (Which is why it's remended to additionally implement the Block ScrollWheel Scroll as well.)
Block Finger Scroll (mobile)
...
function App(){
const [isModalOpen, setIsModalOpen] = useState(false)
...
return (
{/* Sets the 'touch-action' CSS property to 'none' on
the outermost div when the modal is open. */}
<div style={{ touchAction: isModalOpen ? 'none' : 'auto' }}>
...
<button onClick={() => setIsModalOpen(true)}>Open Modal</button>
...
</div>
)
}
Combined (Functional Demo):
const {useState} = React;
const {useEffect} = React;
function disableScroll() {
const scrollTop = window.pageYOffset || document.documentElement.scrollTop;
const scrollLeft = window.pageXOffset || document.documentElement.scrollLeft;
window.onscroll = function() {
window.scrollTo(scrollLeft, scrollTop);
};
}
function enableScroll() {
window.onscroll = function() {};
}
const ExampleComponent = () => {
const [isModalOpen, setIsModalOpen] = useState(false);
useEffect(() => {
if (isModalOpen) {
disableScroll();
} else {
enableScroll();
}
const handleWindowWheel = (event: WheelEvent) => {
if (isModalOpen){
event.preventDefault();
}
};
window.addEventListener('wheel', handleWindowWheel, { passive: false });
return () => {
window.removeEventListener('wheel', handleWindowWheel);
};
}, [isModalOpen]);
return (
<div className={isModalOpen ? 'disable-touch-scroll' : ''}>
{isModalOpen &&
<div id="modal">
<span>You shouldn't be able to scroll now.</span>
<button
onClick={() => setIsModalOpen(false)}
>
Close Modal
</button>
</div>
}
<div>
{"hello ".repeat(1000)}
</div>
<button
id="modal-open-button"
onClick={() => setIsModalOpen(true)}
>
Open Modal
</button>
</div>
);
};
ReactDOM.createRoot(
document.getElementById("root")
).render(
<ExampleComponent/>
);
#modal {
width: 50%;
height: 50%;
position: fixed;
z-index: 999;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
text-align: center;
background: white;
border: 1px solid black;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
}
#modal-open-button {
position: fixed;
top: 50%;
color: white;
background-color: red;
}
.disable-touch-scroll {
touch-action: none;
}
<script src="https://cdnjs.cloudflare./ajax/libs/react/18.2.0/umd/react.development.js"></script>
<script src="https://cdnjs.cloudflare./ajax/libs/react-dom/18.2.0/umd/react-dom.development.js"></script>
<div id="root"></div>
You could just add an event to the onscroll, and then keep a track of the current scrollTop, scrollLeft (if you need to handle horizontal too), and then when you don't want scroll just reset the scroll to these stored values.
eg. If you run the snippet below when the large checkbox is checked, scrolling is disabled.
const div = document.querySelector('div');
const cb = document.querySelector('input');
let lastY = 0;
div.innerText = new Array(3000).fill().map(m => 'hello ').join(' ');
div.addEventListener('scroll', (e) => {
if (cb.checked) {
e.target.scrollTop = lastY;
} else lastY = e.target.scrollTop;
});
cb.addEventListener('click', () => {
console.log('click');
});
div {
height: 150px;
overflow: auto;
}
input {
position: fixed;
top: 50px;
left: 50px;
transform: scale(4);
}
<div>
</div>
<input type="checkbox"/>
本文标签: javascriptDisable scroll but without hiding the scroll bar for React JSStack Overflow
版权声明:本文标题:javascript - Disable scroll but without hiding the scroll bar for React JS? - Stack Overflow 内容由网友自发贡献,该文观点仅代表作者本人, 转载请联系作者并注明出处:http://www.betaflare.com/web/1744269177a2598096.html, 本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容,一经查实,本站将立刻删除。
发表评论