iOS 10 Safari: Prevent scrolling behind a fixed overlay and maintain scroll position

前端 未结 10 2160
我寻月下人不归
我寻月下人不归 2020-12-12 13:51

I\'m not able to prevent the main body content from scrolling while a fixed position overlay is showing. Similar questions have been asked many times, but all of the techniq

相关标签:
10条回答
  • 2020-12-12 14:31

    For those using React, I've had success putting @bohdan-didukh's solution in the componentDidMount method in a component. Something like this (link viewable via mobile browsers):

    class Hello extends React.Component {
      componentDidMount = () => {
        var _overlay = document.getElementById('overlay');
        var _clientY = null; // remember Y position on touch start
    
        function isOverlayTotallyScrolled() {
            // https://developer.mozilla.org/en-US/docs/Web/API/Element/scrollHeight#Problems_and_solutions
            return _overlay.scrollHeight - _overlay.scrollTop <= _overlay.clientHeight;
        }
    
        function disableRubberBand(event) {
            var clientY = event.targetTouches[0].clientY - _clientY;
    
            if (_overlay.scrollTop === 0 && clientY > 0) {
                // element is at the top of its scroll
                event.preventDefault();
            }
    
            if (isOverlayTotallyScrolled() && clientY < 0) {
                //element is at the top of its scroll
                event.preventDefault();
            }
        }
    
        _overlay.addEventListener('touchstart', function (event) {
            if (event.targetTouches.length === 1) {
                // detect single touch
                _clientY = event.targetTouches[0].clientY;
            }
        }, false);
    
        _overlay.addEventListener('touchmove', function (event) {
            if (event.targetTouches.length === 1) {
                // detect single touch
                disableRubberBand(event);
            }
        }, false);
      }
    
      render() {
        // border and padding just to illustrate outer scrolling disabled 
        // when scrolling in overlay, and enabled when scrolling in outer
        // area
        return <div style={{ border: "1px solid red", padding: "48px" }}>
          <div id='overlay' style={{ border: "1px solid black", overflowScrolling: 'touch', WebkitOverflowScrolling: 'touch' }}>
            {[...Array(10).keys()].map(x => <p>Text</p>)}
          </div>
        </div>;
      }
    }
    
    ReactDOM.render(
      <Hello name="World" />,
      document.getElementById('container')
    );
    

    Viewable via mobile: https://jsbin.com/wiholabuka

    Editable link: https://jsbin.com/wiholabuka/edit?html,js,output

    0 讨论(0)
  • 2020-12-12 14:38

    In some cases where the body content is hidden behind your overlay, you can store the current scroll position using const scrollPos = window.scrollY, then apply position: fixed; to the body. When the model closes remove the fixed position from the body and run window.scrollTo(0, scrollPos) to restore the previous position.

    This was the easiest solution for me with the least amount of code.

    0 讨论(0)
  • 2020-12-12 14:41

    I found the code on github. It work on Safari in iOS 10,11,12

    /* ScrollClass */
    class ScrollClass {
    constructor () {
        this.$body = $('body');
    
        this.styles = {
            disabled: {
                'height': '100%',
                'overflow': 'hidden',
            },
    
            enabled: {
                'height': '',
                'overflow': '',
            }
        };
    }
    
    disable ($element = $(window)) {
        let disabled = false;
        let scrollTop = window.pageYOffset;
    
        $element
            .on('scroll.disablescroll', (event) => {
                event.preventDefault();
    
                this.$body.css(this.styles.disabled);
    
                window.scrollTo(0, scrollTop);
                return false;
            })
            .on('touchstart.disablescroll', () => {
                disabled = true;
            })
            .on('touchmove.disablescroll', (event) => {
                if (disabled) {
                    event.preventDefault();
                }
            })
            .on('touchend.disablescroll', () => {
                disabled = false;
            });
    }
    
    enable ($element = $(window)) {
        $element.off('.disablescroll');
    
        this.$body.css(this.styles.enabled);
    }
    }
    

    use:

    Scroll = new ScrollClass();
    
    Scroll.disable();// disable scroll for $(window)
    
    Scroll.disable($('element'));// disable scroll for $('element')
    
    Scroll.enable();// enable scroll for $(window)
    
    Scroll.enable($('element'));// enable scroll for $('element')
    

    I hope it helps you.

    0 讨论(0)
  • 2020-12-12 14:44

    I was trying to find a clean solution to this for a long time, and what seems to have worked best for me is setting pointer-events: none; on the body, and then pointer-events: auto; explicitly on the item I want to allow scrolling in.

    0 讨论(0)
提交回复
热议问题