iOS 10 Safari: empêcher de défiler derrière une superposition fixe et maintenir la position de défilement
Je ne suis pas en mesure d'empêcher le contenu du corps principal de défiler alors qu'un recouvrement de position fixe est affiché. Des questions similaires ont été posées à de nombreuses reprises, mais toutes les techniques qui ont précédemment fonctionné ne semblent pas fonctionner sur Safari dans iOS 10. Cela semble être un problème récent.
Quelques remarques:
- je peux désactiver le défilement si j'ai mis les deux
html
etbody
overflow: hidden
, cependant cela fait défiler le contenu du corps vers le haut. - Si le contenu dans la superposition est assez long de sorte qu'il peut être scrolled, scrolling est correctement désactivé pour le contenu de la page principale. Si le contenu de la superposition n'est pas assez long pour causer le défilement, vous pouvez faire défiler le contenu de la page principale.
- j'ai inclus une fonction javascript à partir de http://blog.christoffer.me/six-things-i-learnt-about-ios-safaris-rubber-band-scrolling/ qui désactive
touchmove
pendant que la superposition est visible. Cela a fonctionné auparavant, mais plus maintenant travail.
Voici le plein code source HTML:
<!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>
5 réponses
s'il vous plaît, ajouter -webkit-overflow-scrolling: touch;
#overlay
élément.
Et veuillez ajouter le code javascript à la fin de la balise body:
(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;
}
}())
j'espère que ça vous aidera.
J'ai combiné L'approche de Bohdan Didukh avec mon approche précédente pour créer un paquet npm facile à utiliser pour désactiver/activer body scroll.
https://github.com/willmcpo/body-scroll-lock
pour plus de détails sur le fonctionnement de la solution, lire https://medium.com/jsdownunder/locking-body-scroll-for-all-devices-22def9615177
pour ceux qui utilisent React, j'ai réussi à mettre la solution de @bohdan-didukh dans la méthode componentDidMount dans un component. Quelque chose comme ceci (lien visible via les navigateurs mobiles):
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')
);
visible via mobile:https://jsbin.com/wiholabuka
Modifiable lien: https://jsbin.com/wiholabuka/edit?html,js,sortie
Lorsque votre superposition est ouvert, vous pouvez ajouter une classe comme prevent-scroll
body
pour empêcher le défilement des éléments de votre superposition:
body.prevent-scroll {
position: fixed;
overflow: hidden;
width: 100%;
height: 100%;
}
Essayez d'ajouter de la masse corporelle-hauteur: 100vh;