reactjs / react-modal

Accessible modal dialog component for React
http://reactcommunity.org/react-modal
MIT License
7.37k stars 809 forks source link

Prevent Body Scrolling (iOS) #829

Closed Alfrex92 closed 2 years ago

Alfrex92 commented 4 years ago

There a lot of threads online about how to lock the body especially on iOS, but I haven't found a good answer.

body { overflow: hidden; } Doesn't work on iOS.

My question is: how can I prevent Body Scrolling on iOS with react-modal?

Note: I found this package that it might work, but the problem is that I can't find a way to make it work with react-modal and hooks. React Modal doesn't accept a ref using useRef.

https://github.com/willmcpo/body-scroll-lock

Any help?

jrmarqueshd commented 4 years ago

hello @Alfrex92!

You can try

body{ overflow: hidden; position: fixed; width: 100%; height: 100%; }

but, this will generate a side effect on the scroll, leading to the beginning of the page whenever the modal is open.

Alfrex92 commented 4 years ago

Thanks jrmarqueshd! any other idea?

trungtin commented 4 years ago

@Alfrex92 you could try this.

<ReactModal
  onAfterOpen={() => {
    document.body.style.top = `-${window.scrollY}px`
    document.body.style.position = 'fixed'
  }}
  onAfterClose={() => {
    const scrollY = document.body.style.top
    document.body.style.position = ''
    document.body.style.top = ''
    window.scrollTo(0, parseInt(scrollY || '0') * -1)
  }}
  ...other props
/>
mverissimo commented 4 years ago

I use this way:

import React, { forwardRef, useRef, useImperativeHandle } from 'react'
import ReactModal from 'react-modal'
import { disableBodyScroll, enableBodyScroll } from 'body-scroll-lock'

const Modal = forwardRef(({ children, ...props }, ref) => {
    const modalRef = useRef()

    useImperativeHandle(ref, () => modalRef.current, [modalRef])

     const modalProps = {
       onAfterOpen: () => disableBodyScroll(modalRef.current),
       onAfterClose: () => enableBodyScroll(modalRef.current),
       ref: modalRef,
       ...props,
     }

    return (
      <ReactModal {...modalProps}>
        {children}
       </ReactModal>
     )
})
ursnj commented 3 years ago

Simple solution would be 👍

onAfterOpen={() => document.body.style.overflow = 'hidden'}
onAfterClose={() => document.body.style.overflow = 'unset'}
Alfrex92 commented 3 years ago

@ursNj thanks, your solution works on iPhone and Android. But, there is a small issue on Android:

https://imgur.com/a/wS3903x

As you can see the body is not being scrolled but there is a small issue.

ursnj commented 3 years ago

@Alfrex92 I have tested this solution on android and web both the places it was working fine, i think your issue is not with scrollbar but chrome browser.

As per my understanding even your code should work fine in web, but not mobile because mobile chrome browser has URL bar, but when you scrollup that bar will go, due to this you are feeling still you have some issue.

You can see same on this app: https://www.watchpartys.com

Alfrex92 commented 3 years ago

@ursNj I tested on Firefox and it's the same issue 😢

These are my styles

const customStyles = {
  overlay: {
    background: 'rgba(0, 0, 0, 0.8)',
    zIndex: zindex.modal
  },
  content: {
    border: 'none',
    padding: 0,
    overflow: 'unset',
    backgroundColor: 'unset',
    width: '100%',
    height: '100%',
    inset: '0',
    display: 'flex',
    alignItems: 'center',
    justifyContent: 'center',
    top: 0,
    right: 0,
    left: 0,
    bottom: 0
  }
}

I found a code that doesn't have the issue but I don't know how to implement it to React Modal https://codepen.io/maladr0it/pen/LYpOJgV

thanks a lot for the help 😃

zeyf commented 3 years ago

Hey if you look at it carefully its due to the nav bar up top by the browser popuping up thats out of out control unless you set widths to be strictly based on pixel

tbfelder commented 2 years ago

Hi,

as a simple workaround, you could use the react window.innerHeight component to set the maxHeight property of your body component, something like

const noOverflowBox = () => { return ( <Box maxHeight={window.innerHeight * 0.75} width={'auto'} .../> }

so to make sure it's height doesn't exceed a particular proportion of the current window. This will prevent an overflow in any component if desired.

ShahriarKh commented 2 years ago

@Alfrex92 you could try this.

<ReactModal
  onAfterOpen={() => {
    document.body.style.top = `-${window.scrollY}px`
    document.body.style.position = 'fixed'
  }}
  onAfterClose={() => {
    const scrollY = document.body.style.top
    document.body.style.position = ''
    document.body.style.top = ''
    window.scrollTo(0, parseInt(scrollY || '0') * -1)
  }}
  ...other props
/>

This works great in Chrome, but in Firefox (Developer Edition v100), my position: sticky header disappears.

diasbruno commented 2 years ago

Controlling by hand is not a good idea...prefer using a package to deal with this. Unfortunately, each browser has its own behavior/or bug in this situation.

dewang002 commented 5 days ago

hello @Alfrex92!

You can try

body{ overflow: hidden; position: fixed; width: 100%; height: 100%; }

but, this will generate a side effect on the scroll, leading to the beginning of the page whenever the modal is open.

exacty that is what I am facing. how to stop that?