Не допускайте прокрутки тіла, але дозволяйте прокручувати накладення


446

Я шукав рішення типу "лайтбокс", яке дозволяє це, але ще не знайшов його (будь-ласка, підкажіть, чи знаєте ви про це).

Поведінка, яку я намагаюся відтворити, подібна до того, що ви побачили на Pinterest, натиснувши на зображення. Накладка може прокручуватися ( як і вся накладка рухається вгору, як сторінка вгорі сторінки ), але тіло за накладкою фіксується.

Я спробував створити це лише за допомогою CSS ( тобто divнакладення вгорі по всій сторінці та тіліoverflow: hidden ), але це не заважає divпрокручуватися.

Як уберегти тіло / сторінку від прокрутки, але продовжувати прокручування всередині контейнера на повноекранному екрані?


хіба це не просто плагін "накладення", як гарнітури, використовуючи положення, зафіксоване прокручуванням y?
ggzone

3
Посилання на Pinterest не допомагає, оскільки його вміст знаходиться за стіною входу.
2540625

4
Оновлення 2017 року: навіть якщо ви ввійшли в Pinterest, ви виявите, що ефекту накладання, описаного в ОП, більше не існує - замість цього, коли ви натискаєте на зображення, ви просто переходите на звичайну сторінку, де відображається велика версія зображення.
Аннабель

Відповіді:


624

Теорія

Дивлячись на поточну реалізацію сайту pinterest (це може змінитися в майбутньому), коли ви відкриєте накладення, noscrollклас застосовується до bodyелемента і overflow: hiddenвстановлюється, таким чином body, більше не можна прокручувати.

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

Натомість divвсередині накладення знаходиться лише position: staticтоді, коли ви бачите вертикальну смугу прокрутки, пов’язану з цим елементом. В результаті вміст прокручується, але накладення залишаються виправленими.

Коли ви закриєте масштабування, ви приховуєте накладення (через display: none), а потім можете також повністю видалити його через javascript (або просто вміст всередині, вирішувати, як його ввести).

На завершальному етапі ви також повинні видалити noscrollклас до body(так властивість переповнення повертається до початкового значення)


Код

Приклад Codepen

(це працює, змінюючи 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властивості. Також "a" 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; 
}

6
Це правильна відповідь. Хтось повинен дати йому галочку як правильно.
Scoota P

64
Це абсолютно правильно, але застережте, що це не працює в мобільному сафарі. Фон буде продовжувати прокручуватися, і ваше накладення буде виправлено.
a10s

20
Це чудово працює. Контейнер вікна прокрутки, що прокручується, divповинен мати стиль CSS position:fixedта мати вертикальний прокручуваний переповнення. Я успішно використовував overflow-y:auto;і для прокрутки імпульсу / інерції iOS додав -webkit-overflow-scrolling:touch;до CSS. Я використовував display:block;, width:100%;і height:100%;CSS для перегляду повних сторінок.
Slink

10
Вибачте, я не розумію. Налаштування корпусу для переповнення: прихований не вимикає прокрутку еластичного корпусу на моєму iPad iOS7. Тому мені довелося додати до свого js: document.body.addEventListener ('touchmove', функція (подія) {event.preventDefault ();}, false); який SADLY також забороняє прокручувати всі елементи. Чи не знайшли ніякого рішення до сих пір (без додаткових плагінів)
Гаравані

22
Якщо користувач вже прокручує тіло вниз до низу, коли активоване накладення, це призведе до того, що тіло скаче вгору, коли ви встановите переповнення: приховано; Як-небудь навколо цього?
Бьорн Андерссон

75

Якщо ви хочете не допустити перекручування на ios, ви можете додати положення, закріплене за вашим .noscroll класом

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

106
З цим рішенням через фіксовану позицію тіло автоматично прокручується до верхньої частини вашого вмісту. Це може турбувати ваших користувачів.
tzi

5
Ще одна проблема використання position:fixed- це те, що він змінив розмір основного корпусу. Можливо, це суперечило іншим CSS, але overflow:hiddenчи все, що було потрібно
Dex

3
це порушує стрибки фокусу, коли ви
переходите на

@ Atav32 Звучить як окрема проблема, позиція та переповнення не повинні впливати на порядок вкладки.
am80l

@ Am80l вибачте мій попередній коментар був супер туманні: вкладка порядок роботи знайти, але екран поштовхи при закладці через поля на IOS Safari ( stackoverflow.com/questions/31126330 / ... )
Atav32

44

Не використовуйте 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, і він не знає, чому слід зосередитися на будь-якому з них.


5
класне рішення, але тоді мені потрібно загортати весь вміст у діві, чого я не мав наміру робити ...
Jacob Raccuia

2
Це ідеальне рішення. Якщо у когось є проблеми з цим, вам може знадобитися додати html, body { height: 100%; }(як у демонстраційній версії), щоб це працювало правильно.
Джон,

4
@ user18490 кутова / матеріальна частина не має нічого спільного з тим, що це рішення працює.
Lucia

10
Це розбиває мобільні пристрої UX різними способами (тобто: URL-панель приховування, перевернення дозволу, ...), навіть пробіл для прокрутки на робочих столах.
пристойно

2
Це більш надійний варіант, але він дещо ускладнює справи. Наприклад, PageUp та PageDown не працюватимуть при оновленні. все, що використовує .offset()значення для обчислення, псується, тощо ...
BlackPanther

42

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

Просто додайте наступні стилі для накладання:

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

Codepen demo

В даний час працює в Chrome, Firefox та IE ( канюза )

Щоб отримати докладнішу інформацію, перегляньте статтю розробників google .


4
Я прийшов увесь час сказати вам, що це працює на останніх Chrome, Mozilla та Opera. Приємно провести час!
Wannabe JavaGeek

3
Хтось повинен додати за це щедрість. Це правильне рішення. Не потрібно JavaScript. :)
joseph.l.hunsaker

9
Проблема цього рішення полягає в тому, що воно працює лише в тому випадку, якщо накладка прокручується. Якщо у вас є модальний режим, і він все вміщується на екрані, щоб не було прокрутки всередині - це не зупинить прокрутку тіла. Це, і це також не працює в Safari зовсім :)
waterplea

1
Я вирішив поєднання цього з overflow-y: scroll(з вашої демонстрації Codepen). Точка, яку підняв @pokrishka, є дійсною, але в моєму випадку це не хвилює. У будь-якому випадку, мені цікаво, чи будуть в майбутньому реалізація браузерів висвітлювати цю деталь. Я виявив, що в Firefox змінити розмір браузера так, щоб модал не підходив до екрана, а потім змінив його розмір знову, щоб він зробив цю властивість (тобто прокрутка міститься) навіть після зміни розміру до повного розміру - доки сторінка не буде завантажена , принаймні.
Марк.2377

33

У більшості рішень проблема полягає в тому, що вони не зберігають позицію прокрутки, тому я подивився, як це робить 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>


Це здається мені найелегантнішим рішенням. Я знаю, що Google використовує ті самі хитрощі прокручування сторінки та переміщення елементів.
forallepsilon

Це єдине рішення, яке працювало для мене на 100% правильно, я б хотів, щоб я знайшов цю відповідь раніше.
користувач0103

31

Варто зазначити, що іноді додавання тегу body "overflow: hidden" не справляється з цим завданням. У цих випадках вам доведеться також додати властивість до тегу html.

html, body {
    overflow: hidden;
}

Для iOS вам також потрібно встановитиwidth: 100%; height: 100%;
Gavin

2
Це не стосується проблеми, що спостерігається також у багатьох інших рішеннях цього питання - якщо вікно прокручується де-небудь, окрім верхньої частини сторінки, коли модал відкритий, це призведе до прокрутки до верхньої частини сторінки. Я знайшов рішення Philipp Міттерера тут , щоб бути кращим варіантом , який покриває більшість випадків.
CLL

1
Я ніколи не стикався з цим питанням, про яке ви говорите (в Інтернеті чи на мобільному пристрої). Чи можете ви поділитися повним кодом (з DOM), який не працює у скрипці чи jsbin? У будь-якому випадку я насторожено використовую обхідні шляхи Javascript для вирішення цієї проблеми.
Франсиско Ходж

7

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

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

Див. Розділ Прокручування конкретного вмісту DIV на головній панелі прокрутки, щоб побачити його роботу.


1
Найкраще рішення, справжнє мислення "поза межами", просто не так добре сформульоване.
Марк

Якщо тіло не є елементом, який прокручується, то верхні панелі не будуть ковзати на мобільних пристроях, коли ви прокручуєте сторінку вниз.
Seph Reed

7

Обрана відповідь правильна, але має деякі обмеження:

  • Супер жорсткі "пальці" пальцем все одно прокручуватимуться <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; */
}

4

Для сенсорних пристроїв спробуйте додати 101vhпрозору оболонку , що має мінімальну висоту в 1 пікс., У обгортку накладки. Потім додайте-webkit-overflow-scrolling:touch; overflow-y: auto; в обгортку. Це наводить мобільний сафарі на думку про накладення прокрутки, перехоплюючи таким чином подія дотику від тіла.

Ось зразок сторінки. Відкрити на мобільному сафарі: http://www.originalfunction.com/overlay.html

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


Юпе, це зробило трюк. Просто потрібно було додати -webkit-overflow-scrolling:touch;для кожного контейнера, що прокручується, щоб перехопити дотики дотику.
Mati

Я все ще в змозі прокрутити iPhone
Симоне Зандара

@SeniorSimoneZandara Я ні. це добре працює, щоб запобігти прокрученню фону
godblessstrawberry

3

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

overscroll-behavior: contain;

на вашому накладенні в CSS.


3

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

Деякі відповіді хороші, проте жодна з них не вирішила мого питання. Я знайшов наступне повідомлення в блозі Christoffer Pettersson. Рішення, представлене там, допомогло випустити проблеми з пристроями iOS, і це допомогло моїй проблемі прокрутки фону.

Шість речей, які я дізнався про прокручування гуми iOS Safari

Як було запропоновано, я включаю основні моменти публікації в блозі у випадку, якщо посилання застаріло.

"Для того, щоб користувач не міг прокручувати фонову сторінку, поки" меню відкрите ", можна керувати, які елементи слід дозволити прокручувати чи ні, застосовуючи деякі JavaScript та клас CSS.

На основі цієї відповіді Stackoverflow ви можете керувати тим, що елементи з відключенням прокрутки не повинні виконувати дію прокрутки за замовчуванням, коли спрацьовує подія 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();
    }
};

А потім покладіть клас відключення-прокрутки на сторінку div:

<div class="page disable-scrolling">

2

Ви можете легко зробити це за допомогою "нового" css та JQuery.

Спочатку: body {... overflow:auto;} За допомогою JQuery ви можете динамічно перемикатися між 'накладанням' та 'тілом'. Якщо на тілі, використовуйте

body {
   position: static;
   overflow: auto;
}

У режимі "накладання"

body {
   position: sticky;
   overflow: hidden;
}

JQuery для комутатора ('body' -> 'overlay'):

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

JQuery для комутатора ('overlay' -> 'body'):

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

1

Мені хотілося б додати до попередніх відповідей, тому що я намагався це зробити, і деякий макет зламався, як тільки я перейшов корпус у положення: виправлено. Щоб уникнути цього, мені довелося також встановити висоту тіла на 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

Використовуйте наступний 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");


1

Якщо наміром є відключення на мобільних пристроях / сенсорних пристроях, тоді це найпростіший спосіб зробити це 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>

(Приклад не працює в контексті переповнення стека. Вам потрібно буде відтворити його на окремій сторінці.)

Якщо ви хочете відключити прокрутку #appконтейнера, просто додайте touch-action: none;.


1
Якщо сенсорні дії насправді працювали на iOS, це є. І в такому випадку не потрібно буде загортати весь "додаток" в окремий розділ.
powerbuoy

0

спробуйте це

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

Якщо ви хочете зупинити прокрутку 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% та переповненням: авто. і всередині вашого накладання ви можете або включити / вимкнути прокрутку, виходячи зі своєї вимоги.

Сподіваюся, це допомагає!



-2

Використовуйте код нижче для відключення та включення смуги прокрутки.

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();
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.