angular / material

Material design for AngularJS
https://material.angularjs.org/
MIT License
16.55k stars 3.39k forks source link

dialog: cuts off/hides body when window is scrolled #4124

Closed rgthree closed 3 years ago

rgthree commented 9 years ago

Fiddle: http://jsfiddle.net/rgthree/9m7z8cqy/

If body has any overflow applied by the app, angular overrides its style to hidden when the dialog is open. This clashes with the other styles added to body, particularly, position: fixed; top: -XXpx; and max-height: 100% from the md-dialog-is-showing class which ends up cutting off the body content or completely hiding it when the window has scrolled more than 100% the height.

Note, we use body {overflow-x:scroll;} to force a scrollbar and reduce horizontal jitter when page content height changes due to navigation.

jelbourn commented 9 years ago

@rschmukler

leonn commented 9 years ago

+1

ngasst commented 9 years ago

+1

EladBezalel commented 9 years ago

@rgthree can you please check if it's still happening on HEAD?

ngasst commented 9 years ago

@EladBezalel I don't know about @rgthree, but I just checked against HEAD and the issue is still very much there for me. Happy to provide as many details as I can/you require.

EladBezalel commented 9 years ago

Can you please post a codepen(not jsfiddle)?

ngasst commented 9 years ago

Couldn't reproduce this with a codepen (including it anyway, in case it might prove useful). I'll keep trying to reproduce the issue, but in the meantime, here are a couple screenshots of the problem taken from the app I'm working on. The pictures include the position of the scrollbar when it works (only at the very top) and when it doesn't (anywhere when scrolled down).

This could be an isolated issue. Maybe someone else can shout out if they've experienced the same problem so you don't waste time tracking something that might be due to my individual config?

CP: http://codepen.io/ngasst/pen/XmEoaK ngmatissue

EladBezalel commented 9 years ago

@ngasst all you had to do is adding disableParentScroll: false to reproduce it http://codepen.io/EladBezalel/pen/OyvdBN

the question is, why you wouldn't like to disable parent scroll

ngasst commented 9 years ago

Very strange. The behavior is indeed reproduced by setting the disableParentScroll to false in the codenpen. But I didn't set it in my actual code, and my understanding is it defaults to true when not set. Here's the options definition from my ModalService. It's in TypeScript, but essentially not much changes:

export class ModalService implements IModalService {
        static $inject: Array<string> = ['$mdDialog'];
        options: ng.material.MDDialogOptions;
        constructor(
            private $mdDialog: ng.material.MDDialogService
        ) {
            //
        }

        open(template: string, controller:string, event?: MouseEvent, data?:any, animcb?:any, ctlAs?:string) {
            this.options = {
                templateUrl: template,
                targetEvent: event,
                controller: controller,
                locals: {
                    data: data  
                },
                bindToController: true,
                controllerAs: ctlAs ? ctlAs : 'vm',
                onComplete: animcb
            }; 

            this.$mdDialog.show(this.options)
                .then(() => {}, () => {});
        }
ngasst commented 8 years ago

A little bit more information about this: For whatever reason, when the dialog is triggered after a scroll, the body receives a top positioning equivalent in pixels to the scrolled distance (if that makes sense -- i.e.: I scroll 889 pixels down, the body receives {[...]; top: -889px}). Now, I see that this comes from the scrollTop function on the $mdUtil service. I have enough to manage a dirty fix, but not the right contextual experience with the implementation to offer a permanent solution. And I still don't know why I seem to be the only one affected by this. Will dig some more and report back if I have something worth sharing.

bampakoa commented 8 years ago

Hi,

I have the same problem. After a scroll, the top property of body is set to a negative value when the modal is shown, thus hiding some content of the body. @ngasst can you share your temp workaround?

ngasst commented 8 years ago

@bampakoa,

Please find a sample of the code below. Maybe it'll be useful in finding a more universal fix? I'll walk through what the code does because, like I said above, it's TypeScript, and I know that not everyone uses that, so...

My fix implies having a wrapper service that takes responsibility for opening all modals in your app. If that service has an open function, you use it to get the value of the scroll position and store it (you actually need to store it, because it'll change as soon as the modal is invoked). To get that value, you can use the useful but undocumented $mdUtil service. Then you select the body and set its style to {position:fixed; top: 0; height:100%; width:100%;}. I use D3 because I'm already using it for other stuff in the app and it's already loaded, but you can use JQuery or even plain JS for this.

That corrects the issue and lets the modal be positioned correctly. But you need to cleanup once you close the modal. That's where the onRemoving event comes in. You give it a callback and use whatever method you want to set the body style to {height:100%;} and just to be fancy, you can get the scroll position, which you stored earlier, and use $window.scrollBy() to go back to whereverfrom you triggered the modal, otherwise it'll default to the top of your app -- which may or may not be what you're going for.

Here's the code.

export class ModalService implements IModalService {
        static $inject: Array<string> = ['$mdDialog', '$mdUtil', '$document', '$window'];
        options: ng.material.MDDialogOptions;
        scrollAmount: number;
        constructor(
            private $mdDialog: ng.material.MDDialogService,
            private $mdUtil: any,
            private $document: ng.IDocumentService,
            private $window: ng.IWindowService
        ) {
            //
        }

        open(template: string, controller:string, event?: MouseEvent, data?:any, animcb?:any, ctlAs?:string) {
            this.scrollAmount = this.$mdUtil.scrollTop(this.$document[0].body);
            d3.select('body').style({
                position: 'fixed',
                height: '100%',
                width: '100%',
                top: '0'
            });

            this.options = {
                templateUrl: template,
                targetEvent: event,
                controller: controller,
                locals: {
                    data: data  
                },
                bindToController: true,
                controllerAs: ctlAs ? ctlAs : 'vm',
                onComplete: animcb,
                onRemoving: () => {
                    d3.select('body').attr({style: 'height: 100%;'});
                    console.log(this.scrollAmount);
                    this.$window.scrollBy(0, this.scrollAmount);
                }   
            }; 

            this.$mdDialog.show(this.options)
                .then((scAmount) => {}, () => {});
        }

        close() {
            this.$mdDialog.hide();
        }
    }
bampakoa commented 8 years ago

@ngasst thanks for your tip!

EladBezalel commented 8 years ago

I don't see it much of an issue, disableParentScroll default value is true and the developer should be aware of disabling this.

@robertmesserle @topherfangio can you guys say your thoughts about it?

bampakoa commented 8 years ago

Just to share a workaround on this, through css:

.md-dialog-is-showing {
    max-height: none;
}

I do not know if it can be applied globally, but it seems to work in my occasion.

abouillon commented 8 years ago

I am experiencing this and looking at the demo, it is exactly what I get. When opening a dialog after scrolling the view a white bar shows up at the bottom of the view. The size of the bar is equivalent to how much the view was scrolled. This is especially troublesome because, just like the demo, I do not have disableParentScroll set to false. However, setting the overflow to initial for the app body did indeed fix this issue.

akashjarad commented 8 years ago

@rgthree where you able to solve the issue.

zilions commented 8 years ago

Also having this problem within my entire project

animesh1987 commented 8 years ago

Also having this issue, able to solve it by overriding css positioning which seems like a hack . Will be good to get a permanent fix. I am using v1.0.6. Thanks.

zilions commented 8 years ago

@animesh1987 - Are you able to provide some code that solves this issue?

lexyfeito commented 8 years ago

Has anyone been able to solve this. I am having the same issue

lexyfeito commented 8 years ago

@zilions were u able to find a fix for this issue?

zuspan commented 8 years ago

not sure if this is a true fix, but this seemed to do the trick for me: body { overflow-x: visible; }

zilions commented 8 years ago

@lexyfeito - No fix from me yet. Tried everything in this thread and nothing seems to work well.

lexyfeito commented 8 years ago

@zilions same here this is getting really annoying

lexyfeito commented 8 years ago

@zilions hey so after reading @abouillon comment i set the body overflow: initial; and it seems to fixed the problem.

abouillon commented 8 years ago

@lexyfeito That's what I was going to suggest, but I was also trying to see if there was a workaround if someone was using the parent option and setting the parent container of the dialog to something other than the document body. So far I haven't found a quick workaround if a user sets the parent to something other than body. Luckily, in my use case, we just let material set the parent container to the document body, but it would be nice if there was a workaround for those who need the parent to be something else.

zilions commented 8 years ago

I managed to solve my issue today, after hours of digging into code and work arounds. Thought I'd share for anyone else that is struggling with the same problem.

.md-dialog-container { height: 100vh !important; top: 0 !important; position: fixed !important; }

lexyfeito commented 8 years ago

@zilions did setting body overflow to initial didnt do the work for you? This seems like a lot of more work

zilions commented 8 years ago

@lexyfeito - It did not work in my case unfortunately. Most of my content is absolute positions, so it's probably a different layout to most other sites. The code I posted above is the only way I could get it to work for me.

lexyfeito commented 8 years ago

@zilions oh well i am glad you found a fix/workaround at least :)

rgthree commented 8 years ago

@akashjarad, @abouillon, @zilions: FWIW, you can isolate the workaround by using .md-dialog-is-showing which is appended to the body when a dialog is showing. This seems to fix it for me while still allowing me to use overflow-y: scroll when a dialog is not shown.

body.md-dialog-is-showing {
    overflow: visible !important;
}

You should keep in mind that this and the other fixes mentioned, while fixes our problems right now, could introduce issues going forward. Clearly, Angular is expecting body to be overflow hidden.

lexyfeito commented 8 years ago

@rgthree you are right and it does work it my case thank you so much for your suggestion.

lexyfeito commented 8 years ago

@rgthree hey man this fixed/workaround doesn't seem to work on IE. Does it happen to you to?

qmphan commented 8 years ago

I'm having this bug too. Thank you @rgthree for the fix.

wengkhing commented 8 years ago

@rgthree Sir, you are such a big help! Thanks for the fix!! I have been digging the internet for hours to get this fix.

messense commented 8 years ago

Just ran into this bug with codes on master branch. It's really wired.

bahaaldine commented 8 years ago

thanks @rgthree fixed my issue !

ChrisSoto commented 7 years ago

This was closed but still seems to be an issue on master. Using the suggestions above @rgthree & @zilions helped fix it for me. I'd just like to know if there is going to be an official solution for this? Thanks.

palfaro91 commented 7 years ago

@rgthree Your solution helped me so much! Can't thank you enough!

caradong commented 7 years ago

@rgthree the solution is very helpful. Thank you. On top of this solution, in my case, I have some navigation bars that are not scrollable and the rest of the page has {overflow-y: scroll}. To completely make it work, I added {top: 0px !important;} to those navigation bars.

generated commented 4 years ago

thanks @caradong this bit was missing in my case. apply explicit top value to all fixed elements

Splaktar commented 3 years ago

I don't see any specific version that this was reported against here in the issue report. I also don't see any mention of this being browser-specific.

I guess it was seen in a version around v1.0.0. I tried http://codepen.io/EladBezalel/pen/OyvdBN with master (1.2.1+), 1.0.0, and 0.11.0 on macOS with Chrome 87 and I wasn't able to see any strange behavior or the dialog appearing partially off screen.

I'm going to close this. If you are still seeing this issue in AngularJS Material 1.2.1, please open a new issue with an updated StackBlitz or CodePen reproduction.