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?

328
demandé sur Konstantin Grushetsky 2012-02-14 20:13:47

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

Exemple Codepen

(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.

Codepen exemple (fade)

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; 
}
515
répondu fcalderan 2018-02-27 08:18:41

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;
}
63
répondu am80l 2013-11-17 07:54:36

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.

33
répondu Luxiyalu 2018-01-31 06:47:06

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;
}
28
répondu Francisco Hodge 2016-11-18 19:58:24

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>
17
répondu Philipp Mitterer 2017-07-21 06:38:28

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;
   ...
}

Codepen démo

fonctionne actuellement en Chrome, Firefox et IE ( caniuse )

pour plus de détails, consultez les développeurs de google de l'article .

13
répondu Igor Alemasow 2018-10-04 11:07:58

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.

5
répondu NGLN 2017-05-23 12:26:35

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; */
}
5
répondu Weston 2017-02-15 20:25:12

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

4
répondu YarGnawh 2016-08-25 07:52:10

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">
3
répondu TheFullResolution 2017-05-23 12:10:45

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");
}
1
répondu Vic Seedoubleyew 2014-06-08 08:52:36

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

1
répondu Damir Kotoric 2016-02-09 11:45:44

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.

1
répondu 黄雨伞 2018-09-24 14:23:12

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; .

0
répondu Gajus 2017-06-22 13:06:34

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", "");
            });
0
répondu Junaid 2018-08-29 13:23:57

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

-1
répondu potar 2017-11-22 13:17:12

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();
-2
répondu Rakesh Chaudhari 2016-09-20 15:37:52