Запретить прокрутку тела, но разрешить прокрутку наложения

Я искал решение типа "лайтбокс", которое позволяет это, но еще не нашел его (пожалуйста, предложите, если вы знаете о нем).

Поведение, которое я пытаюсь воссоздать, похоже на то, что вы видите на Pinterest при нажатии на изображение. Оверлей прокручивается (как и во всем оверлее, перемещается вверх как страница вверху страницы), но тело позади накладывается.

Я попытался создать это с помощью только CSS (например, наложения div поверх всей страницы и тела с помощью overflow: hidden), но это не предотвращает прокрутку div.

Как сохранить прокрутку тела/страницы, но продолжать прокрутку внутри полноэкранного контейнера?

Ответ 1

теория

Глядя на текущей реализации сайта Pinterest (она может измениться в будущем), при открытии наложения noscroll класса применяются к body элементу и overflow: hidden устанавливаются, таким образом, body больше не прокручивается.

Оверлей (созданный на лету или уже внутри страницы и видимый с помощью display: block, без разницы) имеет position: fixed и overflow-y: scroll, со свойствами top, left, right и bottom установленными в 0: этот стиль заставляет оверлей заполнять весь видовой экран.

Вместо этого div внутри оверлея находится просто в position: static а вертикальная полоса прокрутки, которую вы видите, связана с этим элементом. В результате содержимое прокручивается, но наложение остается фиксированным.

Когда вы закрываете масштаб, вы скрываете оверлей (с помощью display: none), а затем вы также можете полностью удалить его с помощью javascript (или только содержимого внутри, это зависит от вас, как его вставить).

В качестве последнего шага вы также должны удалить класс noscroll в body (чтобы свойство overflow возвращалось к своему начальному значению)


Код

Пример Codepen

(он работает, изменяя aria-hidden атрибут aria-hidden наложения, чтобы показать и скрыть его и увеличить его доступность).

наценка
(кнопка открытия)

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

(наложение и кнопка закрытия)

<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 (ваниль-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);

});

Наконец, вот еще один пример, в котором наложение открывается с эффектом постепенного изменения при помощи CSS- transition примененного к свойству opacity. Также применяется padding-right чтобы избежать перекомпоновки основного текста, когда полоса прокрутки исчезает.

Пример Codepen (исчезает)

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

Ответ 2

Если вы хотите предотвратить переключение на ios, вы можете добавить позицию, закрепленную к вашему классу .noscroll

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

Ответ 3

Не используйте overflow: hidden; на body. Он автоматически прокручивает все наверх. Там нет необходимости для JavaScript либо. Используйте overflow: auto; , Это решение работает даже с мобильным Safari:

Структура HTML

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

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

стайлинг

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

Смотрите демо здесь и исходный код здесь.

Обновить:

Для людей, которым нужна клавиша пробела клавиатуры, страница вверх/вниз работает: вам нужно сосредоточиться на оверлее, например, щелкнув по нему, или вручную JS, сфокусировавшись на нем, прежде чем эта часть div будет реагировать на клавиатуру. То же самое с тем, когда оверлей "выключен", так как он просто перемещает оверлей в сторону. В противном случае для браузера это всего лишь два обычных div и он не будет знать, почему он должен фокусироваться на каком-либо из них.

Ответ 4

Стоит отметить, что иногда добавление "overflow: hidden" в тег body не выполняет эту работу. В этих случаях вам также нужно добавить свойство в тег html.

html, body {
    overflow: hidden;
}

Ответ 5

У большинства решений есть проблема, заключающаяся в том, что они не сохраняют позицию прокрутки, поэтому я посмотрел, как это делает Facebook. В дополнение к установке нижележащего контента в position: fixed они также динамически устанавливают верх, чтобы сохранить положение прокрутки:

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

Затем, когда вы снова удалите оверлей, вам нужно сбросить положение прокрутки:

window.scrollTo(0, scrollPosition);

Я создал небольшой пример, чтобы продемонстрировать это решение

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>

Ответ 6

overscroll-behavior css overscroll-behavior позволяет переопределить поведение прокрутки переполнения браузера по умолчанию при достижении верхней/нижней части содержимого.

Просто добавьте следующие стили для наложения:

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

Codepen демо

В настоящее время работает в Chrome, Firefox и IE (caniuse).

Для более подробной информации обратитесь к статье разработчиков Google.

Ответ 7

Вообще говоря, если вы хотите, чтобы родительский (тело в этом случае) предотвращал его прокрутку, когда прокручивается дочерний элемент (наложение в этом случае), затем создайте дочернего родителя родителя, чтобы предотвратить событие прокрутки из пузырь до родителя. В случае, если родительский элемент является телом, для этого требуется дополнительный элемент упаковки:

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

Смотрите Прокрутите определенное содержимое DIV с помощью главной полосы прокрутки браузера, чтобы увидеть его работу.

Ответ 8

Выбранный ответ является правильным, но имеет некоторые ограничения:

  • Сверхтвердый "взмах" пальцем все равно будет прокручивать <body> на заднем плане
  • Если открыть виртуальную клавиатуру, нажав <input> в модальном режиме, все будущие прокрутки будут перенаправлены на <body>

У меня нет решения для первой проблемы, но я хотел бы пролить свет на второй вопрос. Смущает, что Bootstrap раньше документировал проблему с клавиатурой, но они утверждали, что она исправлена, ссылаясь на http://output.jsbin.com/cacido/quiet в качестве примера исправления.

Действительно, этот пример отлично работает на iOS с моими тестами. Тем не менее, обновление до последней версии Bootstrap (v4) ломает его.

В попытке выяснить, в чем разница между ними, я сократил контрольный пример, чтобы он больше не зависел от Bootstrap, http://codepen.io/WestonThayer/pen/bgZxBG.

Решающие факторы странные. Чтобы избежать проблемы с клавиатурой, требуется, чтобы background-color не был установлен в корневом <div> содержащем модал, и модальное содержимое должно быть вложено в другой <div>, для которого может быть установлен background-color.

Чтобы проверить это, раскомментируйте следующую строку в примере 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; */
}

Ответ 9

Для сенсорных устройств попробуйте добавить в оболочку наложения прозрачный div шириной 1px, 101vh min-height. Затем добавьте -webkit-overflow-scrolling:touch; overflow-y: auto; в обертку. Это помогает мобильному сафари думать, что оверлей прокручивается, тем самым перехватывая событие касания от тела.

Вот пример страницы. Открыть на мобильном сафари: http://www.originalfunction.com/overlay.html

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

Ответ 10

Я обнаружил, что этот вопрос пытается решить проблему с моей страницей на Ipad и Iphone - тело прокручивалось, когда я отображал фиксированный div в виде всплывающего окна с изображением.

Некоторые ответы хорошие, но ни один из них не решил мою проблему. Я нашел следующее сообщение в блоге Кристоффера Петтерссона. Представленное там решение помогло решить проблему с iOS-устройствами и помогло решить проблему с прокруткой.

Шесть вещей, которые я узнал о прокрутке резиновой ленты для iOS Safari

Как было предложено, я включаю основные пункты в блоге на случай, если ссылка устарела.

"Чтобы отключить возможность прокрутки фоновой страницы, когда" открыто меню ", можно контролировать, какие элементы следует прокручивать или нет, применяя некоторый JavaScript и класс CSS.

На основе этого fooobar.com/questions/14533/... ответа вы можете контролировать, что элементы с отключенной прокруткой не должны выполнять действие прокрутки по умолчанию, когда срабатывает событие 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();
    }
};

А затем поместите класс disable-scrolling на страницу div:

<div class="page disable-scrolling">

Ответ 11

Я хотел бы добавить к предыдущим ответам, потому что я попытался это сделать, и некоторые макеты сломались, как только я переключил тело на позицию: исправлено. Чтобы этого избежать, мне также нужно было установить высоту тела на 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");
}

Ответ 12

Используйте следующий HTML:

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

Затем JavaScript для перехвата и остановки прокрутки:

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

Затем, чтобы вернуться к нормальному состоянию:

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

Ответ 13

Если намерение отключить на мобильных/сенсорных устройствах, то самый простой способ сделать это - использовать touch-action: none;.

Пример:

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>

Ответ 14

попробуй это

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

Ответ 15

Поведение, которое вы хотите предотвратить, называется цепочкой прокрутки. Чтобы отключить его, установите

overscroll-behavior: contain;

на вашем оверлее в CSS.

Ответ 16

Если вы хотите остановить прокрутку body/html, добавьте следующее

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>

В принципе, вы могли бы сделать это без JS.

Основная идея заключается в добавлении html/body с высотой: 100% и переполнением: авто. и внутри вашего оверлея вы можете включить или отключить прокрутку в зависимости от ваших требований.

Надеюсь это поможет!

Ответ 17

На всякий случай, если кто-нибудь попробует быстрое решение: Body Scroll Lock

Ответ 19

Используйте приведенный ниже код для отключения и включения полосы прокрутки.

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