I\'m not able to prevent the main body content from scrolling while a fixed position overlay is showing. Similar questions have been asked many times, but all of the techniq
For those using React, I've had success putting @bohdan-didukh's solution in the componentDidMount method in a component. Something like this (link viewable via mobile browsers):
class Hello extends React.Component {
componentDidMount = () => {
var _overlay = document.getElementById('overlay');
var _clientY = null; // remember Y position on touch start
function isOverlayTotallyScrolled() {
// https://developer.mozilla.org/en-US/docs/Web/API/Element/scrollHeight#Problems_and_solutions
return _overlay.scrollHeight - _overlay.scrollTop <= _overlay.clientHeight;
}
function disableRubberBand(event) {
var clientY = event.targetTouches[0].clientY - _clientY;
if (_overlay.scrollTop === 0 && clientY > 0) {
// element is at the top of its scroll
event.preventDefault();
}
if (isOverlayTotallyScrolled() && clientY < 0) {
//element is at the top of its scroll
event.preventDefault();
}
}
_overlay.addEventListener('touchstart', function (event) {
if (event.targetTouches.length === 1) {
// detect single touch
_clientY = event.targetTouches[0].clientY;
}
}, false);
_overlay.addEventListener('touchmove', function (event) {
if (event.targetTouches.length === 1) {
// detect single touch
disableRubberBand(event);
}
}, false);
}
render() {
// border and padding just to illustrate outer scrolling disabled
// when scrolling in overlay, and enabled when scrolling in outer
// area
return <div style={{ border: "1px solid red", padding: "48px" }}>
<div id='overlay' style={{ border: "1px solid black", overflowScrolling: 'touch', WebkitOverflowScrolling: 'touch' }}>
{[...Array(10).keys()].map(x => <p>Text</p>)}
</div>
</div>;
}
}
ReactDOM.render(
<Hello name="World" />,
document.getElementById('container')
);
Viewable via mobile: https://jsbin.com/wiholabuka
Editable link: https://jsbin.com/wiholabuka/edit?html,js,output
In some cases where the body content is hidden behind your overlay, you can store the current scroll position using const scrollPos = window.scrollY, then apply position: fixed; to the body. When the model closes remove the fixed position from the body and run window.scrollTo(0, scrollPos) to restore the previous position.
This was the easiest solution for me with the least amount of code.
I found the code on github. It work on Safari in iOS 10,11,12
/* ScrollClass */
class ScrollClass {
constructor () {
this.$body = $('body');
this.styles = {
disabled: {
'height': '100%',
'overflow': 'hidden',
},
enabled: {
'height': '',
'overflow': '',
}
};
}
disable ($element = $(window)) {
let disabled = false;
let scrollTop = window.pageYOffset;
$element
.on('scroll.disablescroll', (event) => {
event.preventDefault();
this.$body.css(this.styles.disabled);
window.scrollTo(0, scrollTop);
return false;
})
.on('touchstart.disablescroll', () => {
disabled = true;
})
.on('touchmove.disablescroll', (event) => {
if (disabled) {
event.preventDefault();
}
})
.on('touchend.disablescroll', () => {
disabled = false;
});
}
enable ($element = $(window)) {
$element.off('.disablescroll');
this.$body.css(this.styles.enabled);
}
}
use:
Scroll = new ScrollClass();
Scroll.disable();// disable scroll for $(window)
Scroll.disable($('element'));// disable scroll for $('element')
Scroll.enable();// enable scroll for $(window)
Scroll.enable($('element'));// enable scroll for $('element')
I hope it helps you.
I was trying to find a clean solution to this for a long time, and what seems to have worked best for me is setting pointer-events: none; on the body, and then pointer-events: auto; explicitly on the item I want to allow scrolling in.