-
-
Notifications
You must be signed in to change notification settings - Fork 32.5k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[Modal] Disable background scrolling in iOS #5750
Comments
And with Popover - when it's open you can scroll screen behind to negative position on iOS - very annoying... Anybody did research or some related links? |
@oliviertassinari I'm being bitten by this bug, too. I can try to fix it, though at the moment I'm not even sure where to start. Do you have any idea what can be causing this? |
@oliviertassinari Same problem on |
As raised by someone on the bootstrap thread, that seems to be a Safari browser bug. We can't do much about it here. I'm closing the issue. It's unfortunate. |
i had the issue with the popover component, so I added a custom BackdropComponent that cancels the touchmove event import * as React from 'react';
import Backdrop, { BackdropProps } from 'material-ui/Modal/Backdrop';
/**
* Prevents scrolling of content behind the backdrop.
*/
export class BackDropIOSWorkaround extends React.PureComponent<BackdropProps> {
protected onTouchMove(event: React.TouchEvent<HTMLDivElement>): void {
event.preventDefault();
}
public render(): JSX.Element {
return (
<Backdrop {...this.props} onTouchMove={this.onTouchMove}/>
);
}
} <Popover
BackdropInvisible={false}
BackdropComponent={BackDropIOSWorkaround}
anchorEl={this.clickElement}
onRequestClose={this.unexpandChoices}
anchorOrigin={{vertical: 'top', horizontal: 'left'}}
transformOrigin={{vertical: 'top', horizontal: 'left'}}
>
<List disablePadding={true}>
{this.choices()}
</List>
</Popover> |
@daniel-rabe 's solution looks good but would be for Material UI v1, not previous versions |
@oliviertassinari I see that the This would save a lot of people time in having to manually add |
@jpmoyn No, you would reset the scroll position to the top of the page by doing so. Users will no longer be at the right scroll position once the dialog is closed. |
Would it be possible to implement the onTouchMove workaround by default? I'm not sure it's possible to specify a custom BackdropComponent everywhere it's needed. This bug affects Dialog, Select, SwipeableDrawer, etc. |
you can override the default-props of material-ui's BackDrop before creating your App: import BackDrop from 'material-ui/Modal/Backdrop';
BackDrop.defaultProps = {...BackDrop.defaultProps, onTouchMove: preventBackdropScroll};
export function preventBackdropScroll(event: React.TouchEvent<HTMLElement>): void {
let target: HTMLElement | null = (event.target as HTMLDivElement);
while (target != null && target !== document.body) {
const scrollHeight: number = target.scrollHeight;
const clientHeight: number = target.clientHeight;
if (scrollHeight > clientHeight) {
return;
}
target = target.parentElement;
}
event.preventDefault();
} |
@daniel-rabe Sneaky! I like it. Unfortunately it didn't fix the issue for me. |
@jacobweber Are you saying #5750 (comment) workaround doesn't work? If it comes with no side effect, we could add it to the core of the library. |
It didn't work for me (although I could see the function being invoked). Although maybe I'm doing something differently; I didn't get a chance to investigate this too deeply. I also noticed that using |
the workaround does not work for current iOS, i dont know since when version exactly |
this solution works for me atm import Fade from 'material-ui/transitions/Fade';
function fadeOnEnter(node: HTMLElement, isAppearing: boolean): void {
let clientY: number | null = null; // remember Y position on touch start
const touchStart: (event: Event) => void = (event: Event) => {
if ((event as TouchEvent).targetTouches.length === 1) {
clientY = (event as TouchEvent).targetTouches[0].clientY;
}
};
const touchMove: (event: Event) => void = (event: Event) => {
if ((event as TouchEvent).targetTouches.length === 1) {
disableRubberBand(event as TouchEvent);
}
};
const disableRubberBand: (event: TouchEvent) => void = (event: TouchEvent) => {
const tmpClientY: number = event.targetTouches[0].clientY - (clientY || 0);
if (node.scrollTop === 0 && tmpClientY > 0) {
// element is at the top of its scroll
event.preventDefault();
}
if (isOverlayTotallyScrolled() && tmpClientY < 0) {
// element is at the top of its scroll
event.preventDefault();
}
};
const isOverlayTotallyScrolled: () => boolean = () => {
// https://developer.mozilla.org/en-US/docs/Web/API/Element/scrollHeight#Problems_and_solutions
return node.scrollHeight - node.scrollTop <= node.clientHeight;
};
node.addEventListener('touchstart', touchStart, false);
node.addEventListener('touchmove', touchMove, false);
}
Fade.defaultProps = {...Fade.defaultProps, onEnter: fadeOnEnter}; |
How about setting In the meantime here is what Ive done:
|
This PR adds the workaround that fixes the Safari bug 🙏 |
I stand corrected. this fixes the problem but losses scroll position. im trying another work around. |
There one more fix which can help for the majority of cases - inert which is coming to a full support (only Firefox is one little behind) |
ive implemented this hook, and if you place this in your index.js it should fix the safari bug...the basic idea is to set overflow hidden on the HTML tag, and reset the scroll position back when the modal closes.
|
I propose that in ModalManage's handleContainer function, we can also set scroll container's touch action style to "none" when open, and restore it when close. Although this will only work on IOS version 13 and later, I think this is OK as a temporary workaround until the iOS safari bug is fixed. You can try it here on your iOS device. @oliviertassinari If you think this workaround is OK, I will create a pull request, thanks! |
The issue definitely should be fixed on MUI level or on Safari level, but as temporary solution you can use just styling. // Media for Safari only |
I was banging my head for this one, so I might share a solution that worked like a charm using Dialog Mui. Check out this npm package: https://www.npmjs.com/package/inobounce You can add it without a dependency also. Just call it in your index.js just after your .render: iNoBounce.disable() And when opening your Dialog do iNoBounce.enable() and onClose iNoBounce.disable(). Works like a charm with Mui on IoS! Make sure your body has -webkit-overflow-scrolling: touch; |
Fix to resolve this problem. Working on my production web page with dropdowns, popovers, dialogs...
|
Woah, CSS is saving the world once again. touch-action sounds exactly like the solution |
The above solution (CSS: #5750 (comment) or JS: #5750 (comment)) will work until we have any input field in modal. If we have input field in modal and it's focused then we see scrolling of background again :( |
Along with |
You can Simply use this lines of code in the Select Component. onOpen={() => (document.body.style.touchAction = 'none')} |
Unfortunately if you do that and use the MuiDrawer, you lose your initial scroll.. So I have an infinite scroll, when I scroll down quite a bit, open my drawer, to check my filters and close it again, I will start at the top of the page. Which is really annoying for the user |
Not only that. If you add position fixed on the body, all sorts of weird UI changes will happen since it will mess with any other styling on the page. Even if you apply this in full screen dialogs you will have time to see the changes once you close the modal. The biggest issue is when you have to use some input field inside the modal which makes this even harder to solve. I'm honestly a bit shocked that this doesn't seem to be a bigger issue and that it has not been resolved yet. I mean, this issue has been opened since 2016 and we're still trying to make some strange hack to maybe get it to work. Modals are pretty important in websites and web apps. It's no secret that Apple does not like any sort of site/web app that could compare with a native app in any way so i'm not really surprised they don't want to solve this, but I still feel like there should be a better solution. |
@nipkai @martin-linden Have you guys found a solution? I've been stucked on this issue for days, I don't want to use a library to address it. Input fields in modal make the background scrolled when the input is focused. It seems like nobody has a clear solution to address that one. |
@emmanuelangola Unfortunately, I've tried various solutions (including libraries), but none have resolved the issue. If this continues to be a significant problem for users of our web app, I will recommend they use an alternative browser like Chrome, where this issue does not occur. It's definitely not a perfect solution, but since this seems to be a Safari bug, there is not much we can do. Please do write if you find a solution that works, even if it is a library. |
@martin-linden, I haven't found a perfect solution neither. Similarly as you, I looked for a robust solution for days, however I couldn't find anything perfect. I'm currently using a work-around that works as intended, however it makes the page scroll to the top of the page each time the user opens the modal. I don't think that's what you're looking for. I can suggest you some javascript libraries. Personally, I'm not a fan of libraries. |
Hey @martin-linden, |
@tao-qian try using the backdrop component and then using it as the custom backdrop, add the property
Usage:
|
I think i found a fix , at least for using it with nextjs : Then you add a height: 100svh to your mounting point in your css file and it should work. my usecase is a fullscreen modal, and i needed to use 100svh as the bottom navbar on Safari was coming in and 100vh didnt cut it somehow, but you can play around with that. hope this helps someone |
I think I worked this out using an implementation from react-spectrum . Use this is your component: usePreventScroll.ts
|
@SteliosKornelakis Please just use |
Tested dialogs in http://www.material-ui.com/#/components/dialog.
On desktop Chrome, background scrolling is disabled when dialogs are shown.
However, it is not disabled in iOS Safari or Chrome.
The text was updated successfully, but these errors were encountered: