Open chuanxie opened 7 years ago
@diasbruno I also encountered this problem.
My configuration:
<ReactModal
isOpen={isOpen}
contentLabel="Modal"
style={{
overlay: {
zIndex: 10,
backgroundColor: 'rgba(0, 0, 0, 0.5)',
},
content: {
padding: '0',
borderRadius: '8px',
border: 'none',
top: '20px',
left: '20px',
right: '20px',
bottom: 'initial',
overflow: 'auto',
WebkitOverflowScrolling: 'touch',
},
}}
>
I, too, am encountering this problem.
@chuanxie @Aaaaaashu @musicalshore Did you find a css trick to prevent this from happening?
Yes, add position: fixed
to the body element when the modal is open
I'd like to point out that this is a known issue with Webkit itself, and not something this library has a lot of control over. This also means it will affect any iOS browser using the Webkit engine, including Chrome, Safari, and others.
Strangely (at least for me), the issue is only apparent on native iOS devices—there is no issue in the dev tools simulator (tested on Chrome).
The position: fixed
trick resolves the issue of having the body content scrollable when the modal is open for mobile iOS, but it may introduce new bugs in the process. One that affected me was not having the body element's width set to 100%, so my content was "squished" left whenever a modal was opened, which looked pretty strange.
I am unsure if there are other bugs introduced with this workaround, and perhaps that was just shame-on-me for not having my body width defined, but I thought I'd throw it out there in case other people share the same issue.
(Is it just me who feels like we're repeating history with some of this? Haha.)
I'm having the same problem position: fixed
doesn't solve the problem when:
a user scrolls halfway down a long page
opens then modal
closes the modal
applying position: fixed
in this case will cause the user to scroll to the top of the page.
Our resolution is to track the scroll position on each modal that we use with
window.scrollY
and window.scrollTo(0, y)
class Modal extends Component {
state = { scrollPosition: 0 }
componentDidMount() {
this.setState({ scrollPosition: window.scrollY });
document.body.style.position = 'fixed';
document.body.style.overflow = 'hidden';
}
componentDidUnmount() {
document.body.style.position = 'initial';
document.body.style.overflow = 'initial';
window.scrollTo(0, this.state.scrollPosition);
}
render() {...}
}
perhaps there's a way to dry this up into a HoC/prop flag or something and incorporate it with the library so that more people can benefit from this in an opt-in fashion?
I've tested a lightweight workaround, while it's not inside react-modal package, may be it could help someone.
const preventIOSScroll = (e) => {
e.preventDefault();
}
<Modal
isOpen={showModal}
contentLabel="Modal"
onAfterOpen={() => {
document.documentElement.style.overflowY = 'hidden';
document.documentElement.addEventListener('touchmove', preventIOSScroll);
}}
onRequestClose={() => {
document.documentElement.style.overflowY = 'visible';
document.documentElement.removeEventListener('touchmove', preventIOSScroll);
}}
shouldCloseOnOverlayClick
style={({ overlay: { zIndex: 100, backgroundColor: 'rgba(0, 0, 0, 0.4)' } })}
>
<YourComponentsInsideModal />
</Modal>
I like to move this logic inside static methods, but you could place it as described before. Tested with almost all accessible iOS devices within SimulatorApp.
I've tested a lightweight workaround, while it's not inside react-modal package, may be it could help someone.
const preventIOSScroll = (e) => { e.preventDefault(); } <Modal isOpen={showModal} contentLabel="Modal" onAfterOpen={() => { document.documentElement.style.overflowY = 'hidden'; document.documentElement.addEventListener('touchmove', preventIOSScroll); }} onRequestClose={() => { document.documentElement.style.overflowY = 'visible'; document.documentElement.removeEventListener('touchmove', preventIOSScroll); }} shouldCloseOnOverlayClick style={({ overlay: { zIndex: 100, backgroundColor: 'rgba(0, 0, 0, 0.4)' } })} > <YourComponentsInsideModal /> </Modal>
I like to move this logic inside static methods, but you could place it as described before. Tested with almost all accessible iOS devices within SimulatorApp.
Didn't initially work for me. After some investigation I figured out that now you need to pass { passive: false }
as a third argument to addEventListener
:
document.documentElement.addEventListener('touchmove', function(e) {
e.preventDefault();
}, { passive: false });
Check here for details: https://stackoverflow.com/questions/49500339/cant-prevent-touchmove-from-scrolling-window-on-ios
Summary:
in ios safari, you can drag the background elements under overlay after open modal. also, if you have a background image on body, even used
position: fixed
oroverflow: hidden
, you still can drag the imageSteps to reproduce:
Expected behavior:
should work as desktop and stop scrolling