Impedisci lo scorrimento del corpo ma consenti lo scorrimento dell'overlay


446

Ho cercato una soluzione di tipo "lightbox" che lo consenta ma non ne ho ancora trovata una (per favore, suggeriscimi se ne conosci qualcuno).

Il comportamento che sto cercando di ricreare è proprio come quello che vedresti su Pinterest quando fai clic su un'immagine. La sovrapposizione è scorrevole ( come nell'intera sovrapposizione si sposta verso l'alto come una pagina in cima a una pagina ) ma il corpo dietro la sovrapposizione è fisso.

Ho tentato di creare questo con solo CSS ( cioè una divsovrapposizione nella parte superiore dell'intera pagina e del corpo conoverflow: hidden ), ma non impedisce divdi essere scorrevole.

Come impedire lo scorrimento del corpo / della pagina ma continuare a scorrere all'interno del contenitore a schermo intero?


non è solo un plug-in "overlay" come hundrets là fuori che usa la posizione fissa con overflow-y scroll?
ggzone,

3
Il collegamento a Pinterest non aiuta, poiché il suo contenuto è dietro una bacheca di accesso.
2540625

4
Aggiornamento 2017: anche se accedi a Pinterest, ciò che scopri è che l'effetto di sovrapposizione descritto nell'OP non esiste più - invece quando fai clic su un'immagine vai semplicemente a una pagina normale che visualizza una versione grande dell'immagine.
Annabel,

Risposte:


624

Teoria

Osservando l'implementazione corrente del sito pinterest (potrebbe cambiare in futuro), quando si apre l'overlay noscrollviene applicata una classe bodyall'elemento e overflow: hiddenviene impostata, quindi bodynon è più scorrevole.

La sovrapposizione (creata on-the-fly o già all'interno della pagina e reso visibile via display: block, non fa differenza) ha position : fixede overflow-y: scroll, con top, left, righte bottomle proprietà impostata 0: questo stile rende la sovrapposizione riempire l'intera finestra.

L' divinterno della sovrapposizione è invece appena in position: staticpoi la barra di scorrimento verticale che vedi è correlata a quell'elemento. Di conseguenza, il contenuto è scorrevole ma l'overlay rimane fisso.

Quando chiudi lo zoom nascondi l'overlay (tramite display: none) e puoi anche rimuoverlo completamente tramite javascript (o solo il contenuto all'interno, dipende da te come iniettarlo).

Come passaggio finale devi anche rimuovere la noscrollclasse in body(quindi la proprietà di overflow ritorna al suo valore iniziale)


Codice

Esempio Codepen

(funziona modificando l' aria-hiddenattributo dell'overlay per mostrarlo e nasconderlo e aumentarne l'accessibilità).

Markup
(pulsante Apri)

<button type="button" class="open-overlay">OPEN LAYER</button>

(pulsante di sovrapposizione e chiusura)

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

});

Infine, ecco un altro esempio in cui l'overlay si apre con un effetto di dissolvenza da un CSS transitionapplicato alla opacityproprietà. Viene inoltre padding-rightapplicato un simbolo per evitare un riflusso del testo sottostante quando la barra di scorrimento scompare.

Esempio di Codepen (dissolvenza)

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

6
Questa è la risposta corretta Qualcuno dovrebbe dare un segno di spunta in quanto è corretto.
Scoota P,

64
Questo è assolutamente corretto, ma attenzione, non funziona nel safari mobile. Lo sfondo continuerà a scorrere e il tuo overlay verrà corretto.
a10s

20
Funziona bene Il contenitore di finestre scorrevoli divdeve avere uno stile CSS position:fixede deve essere scorrevole in eccesso verticale. Ho usato con successo overflow-y:auto;e per lo scorrimento di momento / inerzia di iOS, ho aggiunto -webkit-overflow-scrolling:touch;al CSS. Ho usato display:block;, width:100%;e height:100%;CSS per avere una finestra a tutta pagina.
Slink

10
Scusa non capisco. Impostazione del trabocco del corpo: nascosto non disabilita lo scorrimento del corpo elastico sul mio iPad iOS7. Quindi ho dovuto aggiungere nel mio js: document.body.addEventListener ('touchmove', function (event) {event.preventDefault ();}, false); che SADLY disabilita anche la scorrevolezza di tutti gli elementi. Finora non ho trovato alcuna soluzione (senza plug-in extra)
Garavani

22
Se l'utente ha già fatto scorrere il corpo verso il basso verso il basso quando la sovrapposizione è attivata, questo farà saltare il corpo verso l'alto quando si imposta l'overflow: nascosto; Qualche modo per aggirare questo?
Björn Andersson,

75

Se vuoi evitare l'overcrolling su iOS, puoi aggiungere la posizione fissa alla tua classe .noscroll

body.noscroll{
    position:fixed;
    overflow:hidden;
}

106
Con questa soluzione, a causa della posizione fissa, il corpo scorrerà automaticamente all'inizio del contenuto. Questo potrebbe essere inquietante per i tuoi utenti.
Tzi,

5
Un altro problema con l'utilizzo position:fixedè che stava ridimensionando il mio corpo principale. Forse era in conflitto con altri CSS, ma overflow:hiddenè tutto ciò che era necessario
Dex,

3
questo interrompe il focus focus quando
scorri i

@ Atav32 Sembra un problema separato, la posizione e l'overflow non dovrebbero influire sull'ordine di tabulazione.
am80l,

@ am80l dispiace il mio commento precedente era super vaga: l'ordine di tabulazione opere trovano, ma le scosse dello schermo quando si scheda attraverso i campi su iOS Safari ( stackoverflow.com/questions/31126330/... )
Atav32

44

Non usare overflow: hidden;su body. Fa scorrere automaticamente tutto verso l'alto. Non è necessario neanche JavaScript. Utilizzare overflow: auto;. Questa soluzione funziona anche con Safari mobile:

Struttura HTML

<div class="overlay">
    <div class="overlay-content"></div>
</div>

<div class="background-content">
    lengthy content here
</div>

Messa in piega

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

Guarda la demo qui e il codice sorgente qui .

Aggiornare:

Per le persone che vogliono che la barra spaziatrice della tastiera funzioni, la pagina su / giù funziona: è necessario concentrarsi sulla sovrapposizione, ad esempio, fare clic su di essa o JS focalizzarsi su di essa prima che questa parte della divtastiera risponda. Lo stesso vale per quando l'overlay è "spento", poiché sposta semplicemente l'overlay lateralmente. Altrimenti al browser, questi sono solo due normali dive non saprebbero perché dovrebbe concentrarsi su nessuno di essi.


5
bella soluzione, ma poi devo racchiudere tutti i miei contenuti in un div, cosa che non avevo intenzione di fare ...
Jacob Raccuia,

2
Questa è una soluzione ideale Se qualcuno ha problemi con questo, potrebbe essere necessario aggiungere html, body { height: 100%; }(come nella demo) per farlo funzionare correttamente.
Giovanni,

4
@ user18490 la parte angolare / materiale non ha nulla a che fare con il fatto che questa soluzione sta funzionando.
Lucia,

10
Ciò interrompe la UX dei dispositivi mobili in diversi modi (ad esempio: nascondere la barra dell'URL, la convenienza di overscroll, ...), persino la barra spaziatrice per scorrere sui desktop.
nettamente il

2
Questa è l'opzione più solida, ma complica un po 'le cose. Ad esempio, PageUp e PageDown non funzioneranno sull'aggiornamento. tutto ciò che utilizza i .offset()valori per il calcolo viene incasinato, ecc ...
BlackPanther,

42

overscroll-behavior La proprietà css consente di ignorare il comportamento di scorrimento di overflow predefinito del browser quando si raggiunge la parte superiore / inferiore del contenuto.

Aggiungi i seguenti stili per sovrapporre:

.overlay {
   overscroll-behavior: contain;
   ...
}

Demo Codepen

Attualmente funziona in Chrome, Firefox e IE ( caniuse )

Per maggiori dettagli consulta l' articolo degli sviluppatori di Google .


4
Sono arrivato fino in fondo per dirti che questo funziona su Chrome, Mozilla e Opera più recenti. Divertiti!
Wannabe JavaGeek

3
Qualcuno dovrebbe aggiungere una taglia su questo. Questa è la soluzione corretta JavaScript non necessario. :)
joseph.l.hunsaker

9
Il problema con questa soluzione è che funziona solo se la sovrapposizione è scorrevole. Se hai un modale e tutto si adatta allo schermo in modo che non ci sia scorrimento all'interno, non fermerà lo scorrimento del corpo. Questo e anche non funziona affatto in Safari :)
waterplea

1
Ho risolto con una combinazione di questo con overflow-y: scroll(dalla tua demo di Codepen). Il punto sollevato da @pokrishka è valido, ma nel mio caso non è un problema. In ogni caso, mi chiedo se le implementazioni dei browser copriranno questo dettaglio in futuro. Ho scoperto che, in Firefox, ridimensionando il browser in modo che il modale non si adatta allo schermo e quindi ridimensionandolo di nuovo in modo da far funzionare questa proprietà (cioè lo scorrimento è contenuto) anche dopo il ridimensionamento a dimensione intera - fino a quando la pagina viene ricaricata , almeno.
Marc.2377,

33

La maggior parte delle soluzioni ha il problema di non mantenere la posizione di scorrimento, quindi ho dato un'occhiata a come lo fa Facebook. Oltre a impostare il contenuto sottostante, position: fixedimpostano anche la parte superiore in modo dinamico per mantenere la posizione di scorrimento:

scrollPosition = window.pageYOffset;
mainEl.style.top = -scrollPosition + 'px';

Quindi, quando si rimuove nuovamente l'overlay, è necessario reimpostare la posizione di scorrimento:

window.scrollTo(0, scrollPosition);

Ho creato un piccolo esempio per dimostrare questa soluzione

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

/* reset CSS */
body {
  margin: 0;
}
<main class="main-content"></main>

  <div class="overlay">
    <div class="overlay-content"></div>
  </div>
  
  <button class="toggle">Overlay</button>


Questa sembra essere la soluzione più elegante per me. So che Google utilizza gli stessi trucchi per scorrere la pagina e spostare spesso gli elementi.
forallepsilon,

Questa è l'unica soluzione che ha funzionato al 100% per me, vorrei aver trovato prima questa risposta.
user0103

31

Vale la pena notare che a volte l'aggiunta di "overflow: nascosto" al tag body non fa il lavoro. In questi casi, dovrai aggiungere la proprietà anche al tag html.

html, body {
    overflow: hidden;
}

Per iOS, devi anche impostarewidth: 100%; height: 100%;
Gavin il

2
Questo non risolve un problema riscontrato anche in molte altre soluzioni per questa domanda: se la finestra viene fatta scorrere ovunque ma nella parte superiore della pagina quando il modale è aperto, causerà uno scorrimento verso la parte superiore della pagina. Ho trovato la soluzione di Philipp Mitterer qui come l'opzione migliore che copre la maggior parte dei casi.
CLL

1
Non ho mai riscontrato questo problema di cui parli (sul Web o sul cellulare). Potresti condividere il codice completo (con DOM) che non funziona in un violino o jsbin? In ogni caso, sono cauto nell'utilizzare soluzioni alternative Javascript per affrontare questo problema.
Francisco Hodge,

7

In generale, se si desidera che un genitore (il corpo in questo caso) gli impedisca di scorrere quando un bambino (la sovrapposizione in questo caso) scorre, quindi rendere il figlio un fratello del genitore per impedire che l'evento di scorrimento si diffonda fino a il genitore. Nel caso in cui il genitore sia il corpo, ciò richiede un elemento di avvolgimento aggiuntivo:

<div id="content">
</div>
<div id="overlay">
</div>

Vedi Scorri contenuti DIV particolari con la barra di scorrimento principale del browser per vederne il funzionamento.


1
La migliore soluzione, vero pensiero "out of the box", ma non così ben definito.
Segna

Se il corpo non è l'elemento che scorre, le barre superiori non scivoleranno verso l'alto sul dispositivo mobile mentre scorri verso il basso nella pagina.
Seph Reed,

7

La risposta scelta è corretta, ma presenta alcune limitazioni:

  • Le "avventure" dure con il dito continueranno a scorrere <body>sullo sfondo
  • L'apertura della tastiera virtuale toccando un <input>nel modale indirizzerà tutti i futuri scroll verso<body>

Non ho una soluzione per il primo numero, ma volevo far luce sul secondo. In modo confuso, Bootstrap era solito documentare il problema della tastiera, ma hanno affermato che era stato risolto, citando http://output.jsbin.com/cacido/quiet come esempio della correzione.

In effetti, quell'esempio funziona bene su iOS con i miei test. Tuttavia, l'aggiornamento all'ultimo Bootstrap (v4) lo interrompe.

Nel tentativo di capire quale fosse la differenza tra loro, ho ridotto un caso di test per non dipendere più da Bootstrap, http://codepen.io/WestonThayer/pen/bgZxBG .

I fattori decisivi sono bizzarri. Evitare il problema della tastiera sembra richiedere che background-color non sia impostato sulla radice <div>contenente il modale e il contenuto del modale deve essere nidificato in un altro <div>, che può essere background-colorimpostato.

Per provarlo, decommenta la riga sotto nell'esempio 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; */
}

4

Per i dispositivi touch, prova ad aggiungere un 101vhdiv trasparente 1px di altezza minima nell'involucro della sovrapposizione. Quindi aggiungi -webkit-overflow-scrolling:touch; overflow-y: auto;al wrapper. Questo induce il safari mobile a pensare che l'overlay sia scorrevole, intercettando così l'evento touch dal corpo.

Ecco una pagina di esempio. Apri su Safari per dispositivi mobili: http://www.originalfunction.com/overlay.html

https://gist.github.com/YarGnawh/90e0647f21b5fa78d2f678909673507f


Jup, quello ha funzionato. Ho dovuto solo aggiungere -webkit-overflow-scrolling:touch;per ogni contenitore scorrevole per intercettare gli eventi touch.
Mati,

Sono ancora in grado di scorrere su iPhone
Simone Zandara,

@SeniorSimoneZandara non lo sono. funziona benissimo per impedire lo scroll in background
godblessstrawberry,

3

Il comportamento che si desidera prevenire si chiama concatenamento per scorrimento. Per disabilitarlo, impostare

overscroll-behavior: contain;

sul tuo overlay in CSS.


3

Ho trovato questa domanda cercando di risolvere il problema che avevo con la mia pagina su Ipad e Iphone - il corpo scorreva mentre visualizzavo div fissi come popup con immagine.

Alcune risposte sono buone, tuttavia nessuna di esse ha risolto il mio problema. Ho trovato il seguente post sul blog di Christoffer Pettersson. La soluzione presentata mi ha aiutato a risolvere il problema che avevo con i dispositivi iOS e il mio problema con lo sfondo in scorrimento.

Sei cose che ho imparato sullo scorrimento dell'elastico di iOS Safari

Come è stato suggerito, includo i punti principali del post del blog nel caso in cui il link diventi obsoleto.

"Per disabilitare che l'utente possa scorrere la pagina di sfondo mentre il" menu è aperto ", è possibile controllare quali elementi devono essere fatti scorrere o meno, applicando JavaScript e una classe CSS.

Sulla base di questa risposta StackOverflow è possibile controllare che gli elementi con lo scorrimento disabilitato non debbano eseguire la loro azione di scorrimento predefinita quando viene attivato l'evento touchmove. "

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

E quindi inserisci la classe di disabilitazione-scorrimento nella pagina div:

<div class="page disable-scrolling">

2

Puoi farlo facilmente con alcuni "nuovi" CSS e JQuery.

Inizialmente: body {... overflow:auto;} con JQuery puoi passare dinamicamente tra 'overlay' e 'body'. Quando sei su "corpo", usa

body {
   position: static;
   overflow: auto;
}

Quando su 'overlay' utilizzare

body {
   position: sticky;
   overflow: hidden;
}

JQuery per l'interruttore ('body' -> 'overlay'):

$("body").css({"position": "sticky", "overflow": "hidden"});

JQuery per l'interruttore ('overlay' -> 'body'):

$("body").css({"position": "static", "overflow": "auto"});

1

Vorrei aggiungere le risposte precedenti perché ho provato a farlo, e un po 'di layout si è rotto non appena ho cambiato il corpo in posizione: fisso. Per evitarlo, ho dovuto anche impostare l'altezza del corpo al 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

Usa il seguente HTML:

<body>
  <div class="page">Page content here</div>
  <div class="overlay"></div>
</body>

Quindi JavaScript per intercettare e interrompere lo scorrimento:

$(".page").on("touchmove", function(event) {
  event.preventDefault()
});

Quindi per riportare le cose alla normalità:

$(".page").off("touchmove");


1

Se l'intento è disabilitare su dispositivi mobili / touch, il modo più semplice per farlo è utilizzare touch-action: none;.

Esempio:

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'esempio non funziona nel contesto di Stack Overflow. Sarà necessario ricrearlo in una pagina autonoma.)

Se si desidera disabilitare lo scorrimento del #appcontenitore, è sufficiente aggiungere touch-action: none;.


1
Se l'azione touch ha funzionato su iOS, lo è. E in tal caso non sarebbe necessario avvolgere l'intera "app" in un div separato.
powerbuoy

0

prova questo

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

Se si desidera interrompere lo scorrimento di body / html, aggiungere come segue

CSS

    html, body {
        height: 100%;
    }

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

HTML

    <div class="overlay">
        <div class="overlay-content"></div>
    </div>

    <div class="background-content">
        lengthy content here
    </div>

Fondamentalmente, potresti farlo senza JS.

L'idea principale è quella di aggiungere html / body con altezza: 100% e overflow: auto. e all'interno del tuo overlay, puoi abilitare / disabilitare lo scroll in base alle tue esigenze.

Spero che sia di aiuto!



-2

Utilizzare il codice seguente per disabilitare e abilitare la barra di scorrimento.

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();
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.