webentwicklung-frage-antwort-db.com.de

iOS 10 Safari: Verhindern Sie einen Bildlauf hinter einer festen Überlagerung und behalten Sie die Bildlaufposition bei

Ich kann nicht verhindern, dass der Inhalt des Hauptteils gescrollt wird, während eine Überlagerung mit fester Position angezeigt wird. Ähnliche Fragen wurden schon oft gestellt, aber alle Techniken, die zuvor funktionierten, scheinen unter iOS 10 auf Safari nicht zu funktionieren. Dies scheint ein aktuelles Problem zu sein.

Einige Notizen:

  • Ich kann das Scrollen deaktivieren, wenn ich sowohl html als auch body auf overflow: hidden eingestellt habe. Dies führt jedoch dazu, dass der Hauptinhalt nach oben scrollen wird.
  • Wenn der Inhalt in der Überlagerung lang genug ist, um einen Bildlauf durchführen zu können, ist der Bildlauf für den Inhalt der Hauptseite ordnungsgemäß deaktiviert. Wenn der Inhalt in der Überlagerung nicht lang genug ist, um einen Bildlauf durchzuführen, können Sie den Inhalt der Hauptseite blättern.
  • Ich habe eine Javascript-Funktion von http://blog.christoffer.me/six-things-i-learnt-about-ios-safaris-rubber-band-scrolling/ eingefügt, die touchmove deaktiviert, während das Overlay angezeigt wird. Das hat früher funktioniert, funktioniert aber nicht mehr.

Hier ist der vollständige HTML-Quellcode:

<!doctype html>
<html>
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <script src="https://ajax.googleapis.com/ajax/libs/jquery/2.2.4/jquery.min.js"></script>
    <style type="text/css">
        html, body {
            width: 100%;
            height: 100%;
            margin: 0;
            padding: 0;
        }
        body {
            font-family: arial;
        }
        #overlay {
            display: none;
            position: fixed;
            z-index: 9999;
            left: 0;
            right: 0;
            top: 0;
            bottom: 0;
            overflow: scroll;
            color: #fff;
            background: rgba(0, 0, 0, 0.5);
        }
        #overlay span {
            position: absolute;
            display: block;
            right: 10px;
            top: 10px;
            font-weight: bold;
            font-size: 44px;
            cursor: pointer;
        }
        #overlay p {
            display: block;
            padding: 100px;
            font-size: 36px;
        }
        #page {
            width: 100%;
            height: 100%;
        }
        a {
            font-weight: bold;
            color: blue;
        }
    </style>
    <script>
        $(function() {
            $('a').click(function(e) {
                e.preventDefault();
                $('body').css('overflow', 'hidden');
                $('#page').addClass('disable-scrolling'); // for touchmove technique below

                $('#overlay').fadeIn();
            });
            $('#overlay span').click(function() {
                $('body').css('overflow', 'auto');
                $('#page').removeClass('disable-scrolling'); // for touchmove technique below

                $('#overlay').fadeOut();
            });
        });

        /* Technique from http://blog.christoffer.me/six-things-i-learnt-about-ios-safaris-rubber-band-scrolling/ */
        document.ontouchmove = function ( event ) {
            var isTouchMoveAllowed = true, target = event.target;
            while ( target !== null ) {
                if ( target.classList && target.classList.contains( 'disable-scrolling' ) ) {
                    isTouchMoveAllowed = false;
                    break;
                }
                target = target.parentNode;
            }
            if ( !isTouchMoveAllowed ) {
                event.preventDefault();
            }
        };
    </script>
</head>

<body>
    <div id="overlay">
        <span>&times;</span>
        <p>fixed popover</p>
    </div>

    <div id="page">
        <strong>this is the top</strong><br>
        lots of scrollable content<br>
        asdfasdf<br>
        lots of scrollable content<br>
        asdfasdf<br>
        lots of scrollable content<br>
        asdfasdf<br>
        lots of scrollable content<br>
        asdfasdf<br>
        lots of scrollable content<br>
        asdfasdf<br>
        lots of scrollable content<br>
        asdfasdf<br>
        lots of scrollable content<br>
        asdfasdf<br>
        lots of scrollable content<br>
        asdfasdf<br>
        lots of scrollable content<br>
        asdfasdf<br>
        lots of scrollable content<br>
        asdfasdf<br>
        lots of scrollable content<br>
        asdfasdf<br>
        lots of scrollable content<br>
        asdfasdf<br>
        lots of scrollable content<br>
        asdfasdf<br>
        lots of scrollable content<br>
        asdfasdf<br>
        lots of scrollable content<br>
        asdfasdf<br>
        lots of scrollable content<br>
        asdfasdf<br>
        lots of scrollable content<br>
        asdfasdf<br>
        lots of scrollable content<br>
        asdfasdf<br>
        lots of scrollable content<br>
        asdfasdf<br>
        lots of scrollable content<br>
        asdfasdf<br>
        <br>
        <div><a href="#">Show Popover</a></div>
        <br>
        <br>

    </div>

</body>

</html>
25
Gavin


Bitte fügen Sie -webkit-overflow-scrolling: touch; zum #overlay-Element hinzu.

Und fügen Sie bitte diesen Javascript-Code am Ende des Body-Tags hinzu:

(function () {
    var _overlay = document.getElementById('overlay');
    var _clientY = null; // remember Y position on touch start

    _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);

    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();
        }
    }

    function isOverlayTotallyScrolled() {
        // https://developer.mozilla.org/en-US/docs/Web/API/Element/scrollHeight#Problems_and_solutions
        return _overlay.scrollHeight - _overlay.scrollTop <= _overlay.clientHeight;
    }
}())

Ich hoffe es hilft dir.

44
Bohdan Didukh

Kombinierte Bohdan Didukh's Ansatz mit meinem vorherigen Ansatz zur Erstellung eines einfach zu verwendenden npm-Pakets zum Deaktivieren/Aktivieren von Body Scroll.

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

Weitere Informationen zur Funktionsweise der Lösung finden Sie unter https://medium.com/jsdownunder/locking-body-scroll-for-all-devices-22def9615177

4
Will Po

Ich habe lange Zeit nach einer sauberen Lösung gesucht. Was für mich am besten funktioniert hat, ist, pointer-events: none; auf den Körper einzustellen und dann pointer-events: auto; explizit auf das Element zu setzen, in dem ich das Scrollen zulassen möchte.

2
Firas Dib

Es hat bei mir einfach funktioniert, das Überlauf-Scroll-Verhalten für body zu ändern:

body {
    -webkit-overflow-scrolling: touch;
}
1
Zach Saucier

Bohdan 's Lösung oben ist großartig. fängt den Impuls jedoch nicht ein/blockiert ihn nicht - dh der Fall, wenn der Benutzer nicht genau oben auf der Seite, sondern nahe oben ist der Seite (sagen wir, scrollTop ist 5px) und plötzlich macht der Benutzer einen plötzlichen massiven Pulldown! Bohands Lösung fängt die Ereignisse touchmove ab, aber da -webkit-overflow-scrollingist momentumbasiert , kann der Impuls selbst zusätzliches Scrollen verursachen, was in meinem Fall den Header versteckte und wirklich war nervig.

Warum passiert es?

Tatsächlich ist -webkit-overflow-scrolling: touch Eine Eigenschaft mit doppeltem Zweck.

  1. Der gute Zweck ist, dass es den Gummiband-Smooth-Scrolling-Effekt gibt, der in benutzerdefinierten overflow:scrolling - Containerelementen auf iOS-Geräten fast notwendig ist.
  2. Der unerwünschte Zweck ist jedoch dieses "Überrollen". Was irgendwie Sinn macht, wenn es darum geht, glatt zu sein und nicht plötzlich anzuhalten! :)

Momentum-Blocking-Lösung

Die Lösung, die ich mir ausgedacht habe, wurde von Bohdans Lösung übernommen, aber anstatt touchmove Ereignisse zu blockieren, ändere ich das oben erwähnte CSS-Attribut.

Übergeben Sie einfach das Element, das overflow: scroll (Und -webkit-overflow-scrolling: touch) Hat, zum Zeitpunkt des Einbindens/Renderns an diese Funktion.

Der Rückgabewert dieser Funktion sollte zum Zeitpunkt destroy/beforeDestroy aufgerufen werden.

const disableOverscroll = function(el: HTMLElement) {
    function _onScroll() {
        const isOverscroll = (el.scrollTop < 0) || (el.scrollTop > el.scrollHeight - el.clientHeight);
        el.style.webkitOverflowScrolling = (isOverscroll) ? "auto" : "touch";
        //or we could have: el.style.overflow = (isOverscroll) ? "hidden" : "auto";
    }

    function _listen() {
        el.addEventListener("scroll", _onScroll, true);
    }

    function _unlisten() {
        el.removeEventListener("scroll", _onScroll);
    }

    _listen();
    return _unlisten();
}

Schnelle, kurze Lösung

Oder, wenn Sie sich nicht für unlistening interessieren (was nicht empfohlen wird), ist eine kürzere Antwort:

el = document.getElementById("overlay");
el.addEventListener("scroll", function {
    const isOverscroll = (el.scrollTop < 0) || (el.scrollTop > el.scrollHeight - el.clientHeight);
    el.style.webkitOverflowScrolling = (isOverscroll) ? "auto" : "touch";
}, true);
1
Aidin

In einigen Fällen, in denen der Körperinhalt hinter Ihrer Überlagerung verborgen ist, können Sie die aktuelle Bildlaufposition mit const scrollPos = window.scrollY Speichern und dann position: fixed; Auf den Körper anwenden. Wenn das Modell geschlossen wird, entfernen Sie die feste Position vom Körper und führen Sie window.scrollTo(0, scrollPos) aus, um die vorherige Position wiederherzustellen.

Dies war die einfachste Lösung für mich mit der geringsten Menge an Code.

0
Angus Bremner

Wenn Ihr Overlay geöffnet ist, können Sie eine Klasse wie prevent-scroll zu body hinzufügen, um das Scrollen von Elementen hinter Ihrem Overlay zu verhindern:

body.prevent-scroll {
  position: fixed;
  overflow: hidden;
  width: 100%;
  height: 100%;
}

https://codepen.io/claudiojs/pen/ZKeLvq

0
Claudio

Für diejenigen, die mit React arbeiten, war es mir gelungen, die Lösung von @ bohdan-didukh in die componentDidMount-Methode in einer Komponente einzufügen. So etwas (Link über mobile Browser abrufbar):

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')
);

Über Handy abrufbar: https://jsbin.com/wiholabuka

Bearbeitbarer Link: https://jsbin.com/wiholabuka/edit?html,js,output

0
Ivan Gozali

Ich habe den Code auf github gefunden. Es funktioniert auf 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);
}
}

benutzen:

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')

Ich hoffe es hilft dir.

0
Hưng Trịnh