Запретить прокрутку тела, но разрешить прокрутку наложения
Я искал решение типа "лайтбокс", которое позволяет это, но еще не нашел его (пожалуйста, предложите, если вы знаете о нем).
Поведение, которое я пытаюсь воссоздать, похоже на то, что вы видите на 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
Ответ 18
В моем случае ни одно из этих решений не разработано на iPhone (iOS 11.0).
Единственное эффективное решение, которое работает на всех моих устройствах, - это ios-10-safari-prevent-scrolling-behind-a-fixed-overlay-and-maintain- положение прокрутки
Ответ 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();