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:
html
als auch body
auf overflow: hidden
eingestellt habe. Dies führt jedoch dazu, dass der Hauptinhalt nach oben scrollen wird.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>×</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>
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.
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
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.
Es hat bei mir einfach funktioniert, das Überlauf-Scroll-Verhalten für body
zu ändern:
body {
-webkit-overflow-scrolling: touch;
}
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-scrolling
ist momentumbasiert , kann der Impuls selbst zusätzliches Scrollen verursachen, was in meinem Fall den Header versteckte und wirklich war nervig.
Tatsächlich ist -webkit-overflow-scrolling: touch
Eine Eigenschaft mit doppeltem Zweck.
overflow:scrolling
- Containerelementen auf iOS-Geräten fast notwendig ist.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();
}
Oder, wenn Sie sich nicht für unlisten
ing 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);
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.
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%;
}
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
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.