react-responsive-modal icon indicating copy to clipboard operation
react-responsive-modal copied to clipboard

Potential iOS scroll bug

Open O4epegb opened this issue 3 years ago • 12 comments

Hi, everyone! First of all, thanks for nice library, great job!

Bug report

Describe the bug

I've encountered strange problem with scroll, not quite sure if this is because of some weird bug or maybe I am doing something wrong, or it just Safari css behaviour.

Anyway, so if you make full screen modal with css flexbox then it won't let you scroll content inside of it on iOS. It works fine on Desktop (Mac, Window), have not tested on Android.

I saw an issue about scroll and I waited for the fix, but it did not fixed it actually. https://github.com/pradel/react-responsive-modal/issues/462

And if you disable scroll block (blockScroll={false}) then it actually works, but not quite good because, well, body scroll is not disabled and it is wonky.

To Reproduce

Open modal on iOS, try to scroll

Expected behavior

You can scroll to the end

Screenshots

Codesandbox: https://xop8k.csb.app/

System information

  • Version of react-responsive-modal: [e.g. 6.0.1]
  • Version of react: [e.g. 17.0.1]

O4epegb avatar Jan 08 '21 17:01 O4epegb

Similar issue. Trying to scroll on a video element which has a div element stretched over it (pos absolute), horizontally within a mobile viewport. The peculiar thing is that sometimes I can scroll as I would expect, but as soon as the movement/slow ease out of the scroll animation stops, scrolling is not possible anymore for a while. Then, when quickly trying to move the element, sometimes it "grabs" the element again and scrolls as desired.

The same approach works fine on desktop browsers, so I think it's an iOS webkit specific issue - or touch event related.

genox avatar Mar 03 '21 08:03 genox

As I don't have an IOS device I can't debug and fix this issue. If anyone wants to give it a try I would gladly accept a pr to fix this :)

pradel avatar Mar 04 '21 08:03 pradel

Thanks for your reply. I know this is a tricky one and honestly, I don't even know where to start looking for the root cause. And with these odds, for now I'll just stick to a workaround. What I was able to figure out is basically the same as OP: If the body scroll is locked on iOS Safari, scroll within the modal is (randomly, for some reason) locked, too.

From past experiences with issues on scroll locking, I know that iOS Safari is very anal about various techniques and - but don't pin me down on that - it might have something to do with touch event propagation, in which case we can't do anything, anyways. I ended up targeting iOS Safari with a dirty hack and set blockScroll={!isIosSafari()}. This basically solves the issue for me.

If you would like access to an iOS testing environment, I can share some time on BrowserStack with you. Let me know. But for now I'd file it under "things that define my hate love with iOS". ;-)

genox avatar Mar 04 '21 08:03 genox

To block the scroll we use the body-scroll-lock npm package and by just taking a look at the IOS issues I can see that IOS seems to be a constant source of pain :D https://github.com/willmcpo/body-scroll-lock/issues?q=is%3Aissue+is%3Aopen+sort%3Aupdated-desc So the bug might actually be on this lib, making it even harder to debug.

pradel avatar Mar 04 '21 08:03 pradel

Oh.. oh.. now all the compartmentalised and suppressed memories come back.. exactly. I was playing around with this lib a few years ago and ended up in the exact same rabbit hole. Oh well. Maybe just document the iOS issue potential and suggest browser or feature detection as a workaround then. Don't sink your time into this.

genox avatar Mar 04 '21 08:03 genox

I fork the example at https://2wyki.csb.app/ with height: 100% in styles.css commented out, and it works on my iOS 14.4.2 Safari.

It seems that body-scroll-lock requires setting disableBodyScroll on correct scroll container. For the original example https://xop8k.csb.app/, actually, v6.0.0 will work, since it calls disableBodyScroll on modal rather than modal container.

Working version uses v6.0.0: https://qedbk.csb.app/ (only change the react-responsive-modal version)

  • v6.0.0: disableBodyScroll on modal
  • v6.0.1: disableBodyScroll on modal container

In conclusion, the scroll lock behavior highly depends on how you write your CSS, hence there are a lot of issues reported around body-scroll-lock package.

pc035860 avatar May 20 '21 11:05 pc035860

A potential fix has been merged at https://github.com/willmcpo/body-scroll-lock/pull/207; perhaps it's worth releasing a beta with it?

bhj avatar Jun 25 '21 19:06 bhj

I've downgraded back to 5.1.1 for now as that doesn't seem to have to the issue.

Link2Twenty avatar Jul 12 '21 11:07 Link2Twenty

There is another PR at https://github.com/willmcpo/body-scroll-lock/pull/229 that fixes some additional issues; just posting here for reference. Hopefully when that is merged and released it can be incorporated into this package.

bhj avatar Dec 21 '21 19:12 bhj

Any updates on this?

Feijo avatar Jul 24 '23 02:07 Feijo

Any updates on this?

https://github.com/willmcpo/body-scroll-lock/pull/229#issuecomment-1040498731

Kepro avatar Jul 24 '23 07:07 Kepro

Given this lib is kinda dead, if you need a simple replacement, here it goes

import { css } from '@emotion/react';
import { MouseEvent, useEffect, useRef } from 'react';
import { GrClose } from 'react-icons/gr';

const styles = {
  root: css`
    width: 400px;
    border-radius: 8px;
    border: 1px solid #888;

    ::backdrop {
      background: rgba(0, 0, 0, 0.3);
    }
  `,
  close: css`
    position: absolute;
    right: 15px;
  `,
};

const isClickInsideRectangle = (e: MouseEvent, element: HTMLElement) => {
  const r = element.getBoundingClientRect();

  return e.clientX > r.left && e.clientX < r.right && e.clientY > r.top && e.clientY < r.bottom;
};

type Props = {
  title?: string;
  open: boolean;
  showCloseIcon?: boolean;
  onClose: () => void;
  children: React.ReactNode;
};

const Modal = ({ title, open, showCloseIcon, onClose, children }: Props) => {
  const ref = useRef<HTMLDialogElement>(null);

  useEffect(() => {
    if (open) {
      ref.current?.showModal();
      document.body.classList.add('modal-open'); // Add this to your main styles file: body.modal-open { overflow: hidden; }
    } else {
      ref.current?.close();
      document.body.classList.remove('modal-open');
    }
  }, [open]);

  return (
    <dialog
      ref={ref}
      css={styles.root}
      onCancel={onClose}
      onClick={(e) => ref.current && !isClickInsideRectangle(e, ref.current) && onClose()}
    >
      {showCloseIcon !== false && (
        <div css={styles.close} onClick={onClose}>
          <GrClose />
        </div>
      )}

      <h3>{title}</h3>

      {children}
    </dialog>
  );
};

export default Modal;

Feijo avatar Jul 25 '23 05:07 Feijo