Empêcher le défilement du corps, mais permettre le défilement par recouvrement
j'ai cherché une solution de type" lightbox " qui permet cela mais je n'en ai pas encore trouvé (s'il vous plaît, suggérez si vous en connaissez une).
le comportement que j'essaie de recréer est exactement comme ce que vous verriez à Pinterest en cliquant sur une image. La superposition est scrollable ( comme dans l'ensemble de la superposition se déplace vers le haut comme une page en haut d'une page ) mais le corps derrière la superposition est fixe.
j'ai essayé de créer ceci avec juste CSS ( i.e. a div
overlay on top of the whole page and body with overflow: hidden
), mais cela n'empêche pas div
d'être scrollable.
comment empêcher le corps/la page de défiler tout en continuant à défiler à l'intérieur du conteneur plein écran?
17 réponses
Théorie
en regardant l'implémentation actuelle du site pinterest (il pourrait changer dans le futur), quand vous ouvrez la superposition une classe noscroll
est appliquée à l'élément body
et overflow: hidden
est défini, donc body
n'est plus contrôlable.
la superposition (créé à la volée ou déjà à l'intérieur de la page et rendu visible via display: block
, il ne fait aucune différence) a position : fixed
et overflow-y: scroll
, avec top
, left
, right
et bottom
propriétés définies à 0
: ce style fait la superposition remplir l'ensemble du viewport.
le div
à l'intérieur de la superposition est plutôt juste dans position: static
puis le scrollbar vertical que vous voyez est lié à cet élément. En conséquence, le contenu est scrollable mais la superposition reste fixe.
lorsque vous fermez le zoom vous cachez la superposition (via display: none
) et puis vous pouvez également supprimer entièrement via javascript (ou juste le contenu à l'intérieur, c'est à vous comment l'injecter).
comme dernière étape, vous devez également supprimer la classe noscroll
à la body
(de sorte que la propriété overflow retourne à sa valeur initiale)
Code
(il fonctionne en changeant la aria-hidden
attribut de la superposition afin d'afficher et de masquer et d'accroître son accessibilité).
Markup
(ouvrir le bouton)
<button type="button" class="open-overlay">OPEN LAYER</button>
(bouton "overlay and close")
<section class="overlay" aria-hidden="true">
<div>
<h2>Hello, I'm the overlayer</h2>
...
<button type="button" class="close-overlay">CLOSE LAYER</button>
</div>
</section>
CSS
.noscroll {
overflow: hidden;
}
.overlay {
position: fixed;
overflow-y: scroll;
top: 0; right: 0; bottom: 0; left: 0; }
[aria-hidden="true"] { display: none; }
[aria-hidden="false"] { display: block; }
Javascript (vanilla-JS)
var body = document.body,
overlay = document.querySelector('.overlay'),
overlayBtts = document.querySelectorAll('button[class$="overlay"]');
[].forEach.call(overlayBtts, function(btt) {
btt.addEventListener('click', function() {
/* Detect the button class name */
var overlayOpen = this.className === 'open-overlay';
/* Toggle the aria-hidden state on the overlay and the
no-scroll class on the body */
overlay.setAttribute('aria-hidden', !overlayOpen);
body.classList.toggle('noscroll', overlayOpen);
/* On some mobile browser when the overlay was previously
opened and scrolled, if you open it again it doesn't
reset its scrollTop property */
overlay.scrollTop = 0;
}, false);
});
enfin, voici un autre exemple dans lequel la superposition s'ouvre avec un fondu-en effet par un CSS transition
appliqué à la propriété opacity
. En outre ,un padding-right
est appliqué pour éviter un retour sur le texte sous-jacent lorsque le scrollbar disparaît.
CSS
.noscroll { overflow: hidden; }
@media (min-device-width: 1025px) {
/* not strictly necessary, just an experiment for
this specific example and couldn't be necessary
at all on some browser */
.noscroll {
padding-right: 15px;
}
}
.overlay {
position: fixed;
overflow-y: scroll;
top: 0; left: 0; right: 0; bottom: 0;
}
[aria-hidden="true"] {
transition: opacity 1s, z-index 0s 1s;
width: 100vw;
z-index: -1;
opacity: 0;
}
[aria-hidden="false"] {
transition: opacity 1s;
width: 100%;
z-index: 1;
opacity: 1;
}
si vous voulez éviter de trop défiler sur ios, vous pouvez ajouter la position fixée sur votre .classe noscroll
body.noscroll{
position:fixed;
overflow:hidden;
}
N'utilisez pas overflow: hidden;
sur body
. Il défile automatiquement tout en haut. Pas besoin de JavaScript non plus. Utilisez overflow: auto;
. Cette solution fonctionne même avec le Safari mobile:
Structure HTML
<div class="overlay">
<div class="overlay-content"></div>
</div>
<div class="background-content">
lengthy content here
</div>
style
.overlay{
position: fixed;
top: 0px;
left: 0px;
right: 0px;
bottom: 0px;
background-color: rgba(0, 0, 0, 0.8);
.overlay-content {
height: 100%;
overflow: scroll;
}
}
.background-content{
height: 100%;
overflow: auto;
}
voir la démo ici et le code source ici .
mise à Jour:
pour les personnes qui veulent clavier barre d'espace, page Haut/Bas pour travailler: vous devez vous concentrer sur la superposition, par exemple, en cliquant dessus, ou manuellement js se concentrer sur elle avant que cette partie du div
répondra au clavier. Il en est de même lorsque la superposition est "éteinte", car elle ne fait que déplacer la superposition sur le côté. Sinon pour le navigateur, ce ne sont que deux div
normaux et il ne saurait pas pourquoi il devrait se concentrer sur l'un d'eux.
Il est intéressant de noter que, parfois, l'ajout de "overflow:hidden" de la balise "body" de ne pas faire le travail. Dans ces cas, vous devez ajouter la propriété à la balise html.
html, body {
overflow: hidden;
}
la plupart des solutions ont le problème qu'ils ne conservent pas la position de défilement, donc j'ai regardé comment Facebook le fait. En plus de régler le contenu sous-jacent à position: fixed
, ils ont aussi réglé le haut dynamiquement pour conserver la position de défilement:
scrollPosition = window.pageYOffset;
mainEl.style.top = -scrollPosition + 'px';
ensuite, lorsque vous supprimez à nouveau la superposition, vous devez réinitialiser la position de défilement:
window.scrollTo(0, scrollPosition);
j'ai créé un petit exemple pour démontrer cette solution
let overlayShown = false;
let scrollPosition = 0;
document.querySelector('.toggle').addEventListener('click', function() {
if (overlayShown) {
showOverlay();
} else {
removeOverlay();
}
overlayShown = !overlayShown;
});
function showOverlay() {
scrollPosition = window.pageYOffset;
const mainEl = document.querySelector('.main-content');
mainEl.style.top = -scrollPosition + 'px';
document.body.classList.add('show-overlay');
}
function removeOverlay() {
document.body.classList.remove('show-overlay');
window.scrollTo(0, scrollPosition);
const mainEl = document.querySelector('.main-content');
mainEl.style.top = 0;
}
.main-content {
background-image: repeating-linear-gradient( lime, blue 103px);
width: 100%;
height: 200vh;
}
.show-overlay .main-content {
position: fixed;
left: 0;
right: 0;
overflow-y: scroll; /* render disabled scroll bar to keep the same width */
}
.overlay {
display: none;
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: rgba(0, 0, 0, 0.3);
overflow: auto;
}
.show-overlay .overlay {
display: block;
}
.overlay-content {
margin: 50px;
background-image: repeating-linear-gradient( grey, grey 20px, black 20px, black 40px);
height: 120vh;
}
.toggle {
position: fixed;
top: 5px;
left: 15px;
padding: 10px;
background: red;
}
<main class="main-content"></main>
<div class="overlay">
<div class="overlay-content"></div>
</div>
<button class="toggle">Overlay</button>
overscroll-behavior
la propriété css permet d'Outrepasser le comportement de défilement par défaut du navigateur lors de l'atteinte du haut/bas du contenu.
il suffit d'ajouter les styles suivants pour superposer:
.overlay {
overscroll-behavior: contain;
...
}
fonctionne actuellement en Chrome, Firefox et IE ( caniuse )
pour plus de détails, consultez les développeurs de google de l'article .
en général, si vous voulez qu'un parent (le corps dans ce cas) l'empêche de défiler lorsqu'un enfant (la superposition dans ce cas) défile, alors faites de l'enfant un frère ou une sœur du parent pour empêcher l'événement de défiler vers le parent. Si le parent est le corps, cela nécessite un élément d'emballage supplémentaire:
<div id="content">
</div>
<div id="overlay">
</div>
Voir Défilement particulier DIV contenu dans le navigateur principal de la barre de défilement pour voir son travail.
la réponse choisie est correcte, mais comporte certaines limites:
- flings" Super durs "avec votre doigt fera toujours défiler
<body>
en arrière-plan - ouvrir le clavier virtuel en tapant un
<input>
dans le modal dirigera tous les rouleaux futurs vers<body>
Je n'ai pas de solution pour le premier numéro, mais je voulais faire la lumière sur le second. De façon confuse, Bootstrap utilisé pour avoir le problème du clavier documenté, mais ils ont affirmé qu'il a été corrigé, citant http://output.jsbin.com/cacido/quiet comme exemple de fixation.
en effet, cet exemple fonctionne très bien sur iOS avec mes tests. Cependant, la mise à niveau vers le dernier Bootstrap (v4) la casse.
dans une tentative de comprendre quelle était la différence entre eux, j'ai réduit un cas de test à ne plus dépendre de Bootstrap, http://codepen.io/WestonThayer/pen/bgZxBG .
les facteurs déterminants sont bizarres. Éviter la question du clavier semble exiger que background-color
ne soit pas placé sur la racine <div>
contenant le modal et le contenu du modal doit être imbriqué dans un autre <div>
, qui peut avoir background-color
ensemble.
pour le tester, désactiver la ligne ci-dessous dans l'exemple Codepen:
.modal {
position: fixed;
top: 0;
right: 0;
bottom: 0;
left: 0;
z-index: 2;
display: none;
overflow: hidden;
-webkit-overflow-scrolling: touch;
/* UNCOMMENT TO BREAK */
/* background-color: white; */
}
pour les appareils tactiles, essayez d'ajouter un div transparent de min-height de 1px de large, 101vh
dans l'enveloppe de la superposition. Puis Ajouter -webkit-overflow-scrolling:touch; overflow-y: auto;
à l'emballage. Cette astuce Safari mobile dans la pensée de la superposition est scrollable, interceptant ainsi l'événement de toucher du corps.
voici un exemple de page. Ouvert sur le safari mobile: http://www.originalfunction.com/overlay.html
https://gist.github.com/YarGnawh/90e0647f21b5fa78d2f678909673507f
j'ai trouvé cette question en essayant de résoudre le problème que j'avais avec ma page sur Ipad et Iphone - corps défilait lorsque j'affichais fixe div comme popup avec l'image.
Certaines réponses sont bonnes, cependant aucun n'a résolu mon problème. J'ai trouvé le post de blog suivant par Christoffer Pettersson. La Solution présentée là a aidé la question que j'avais avec les appareils iOS et il a aidé mon problème de fond de défilement.
Six choses que j'ai appris sur iOS Safari en caoutchouc de la bande de défilement
comme il a été suggéré que j'inclue les points principaux du billet de blog dans le cas où le lien devient périmé.
" afin de désactiver que l'utilisateur puisse faire défiler la page d'arrière-plan pendant que le "menu est ouvert", il est possible de contrôler quels éléments devraient être autorisés à être défilés ou non, en appliquant un certain JavaScript et une classe CSS.
basé sur ce Stackoverflow vous répondre peuvent contrôler que les éléments de désactiver le défilement ne devrait pas exécutez leur action de défilement par défaut lorsque l'événement touchmove est déclenché."
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();
}
};
et ensuite mettre la classe disable-scrolling sur la page div:
<div class="page disable-scrolling">
je voudrais ajouter à des réponses précédentes parce que j'ai essayé de le faire, et une certaine disposition cassé dès que j'ai commuté le corps à la position:fixe. Pour éviter cela, j'ai dû aussi régler la hauteur du corps à 100%:
function onMouseOverOverlay(over){
document.getElementsByTagName("body")[0].style.overflowY = (over?"hidden":"scroll");
document.getElementsByTagName("html")[0].style.position = (over?"fixed":"static");
document.getElementsByTagName("html")[0].style.height = (over?"100%":"auto");
}
utiliser le HTML suivant:
<body>
<div class="page">Page content here</div>
<div class="overlay"></div>
</body>
puis JavaScript pour intercepter et arrêter le scrolling:
$(".page").on("touchmove", function(event) {
event.preventDefault()
});
puis revenir à la normale:
$(".page").off("touchmove");
le comportement que vous voulez empêcher est appelé chaîne de défilement. Pour le désactiver, mettez
overscroll-behavior: contain;
sur votre superposition dans CSS.
si l'intention est de désactiver sur les appareils mobiles/ tactiles, alors la façon la plus simple de le faire est d'utiliser touch-action: none;
.
exemple:
const app = document.getElementById('app');
const overlay = document.getElementById('overlay');
let body = '';
for (let index = 0; index < 500; index++) {
body += index + '<br />';
}
app.innerHTML = body;
app.scrollTop = 200;
overlay.innerHTML = body;
* {
margin: 0;
padding: 0;
}
html,
body {
height: 100%;
}
#app {
background: #f00;
position: absolute;
height: 100%;
width: 100%;
overflow-y: scroll;
line-height: 20px;
}
#overlay {
background: rgba(0,0,0,.5);
position: fixed;
top: 0;
left: 0;
right: 0;
height: 100%;
padding: 0 0 0 100px;
overflow: scroll;
}
<div id='app'></div>
<div id='overlay'></div>
(l'exemple ne fonctionne pas dans le contexte du débordement de la pile. Vous aurez besoin de le recréer dans une page autonome.)
si vous voulez désactiver le défilement du conteneur #app
, il suffit d'ajouter touch-action: none;
.
essayez cette
var mywindow = $('body'), navbarCollap = $('.navbar-collapse');
navbarCollap.on('show.bs.collapse', function(x) {
mywindow.css({visibility: 'hidden'});
$('body').attr("scroll","no").attr("style", "overflow: hidden");
});
navbarCollap.on('hide.bs.collapse', function(x) {
mywindow.css({visibility: 'visible'});
$('body').attr("scroll","yes").attr("style", "");
});
dans mon cas, aucune de ces solutions n'a fonctionné sur iPhone (iOS 11.0).
Le seul moyen de réparer cela fonctionne sur tous mes appareils, est-ce un ios-10-safari-prévention-défilement-derrière-un-fixe-overlay-et maintenir--scroll-position
utilisez le code ci-dessous pour désactiver et activer la barre de défilement.
Scroll = (
function(){
var x,y;
function hndlr(){
window.scrollTo(x,y);
//return;
}
return {
disable : function(x1,y1){
x = x1;
y = y1;
if(window.addEventListener){
window.addEventListener("scroll",hndlr);
}
else{
window.attachEvent("onscroll", hndlr);
}
},
enable: function(){
if(window.removeEventListener){
window.removeEventListener("scroll",hndlr);
}
else{
window.detachEvent("onscroll", hndlr);
}
}
}
})();
//for disabled scroll bar.
Scroll.disable(0,document.body.scrollTop);
//for enabled scroll bar.
Scroll.enable();