Просто отключить прокрутку, а не скрыть это?
Я пытаюсь отключить полосу прокрутки html/body родительского, пока я использую лайтбокс. Главное слово здесь - отключить. Я не хочу скрывать это с overflow: hidden;
,
Причина этого в том, что overflow: hidden
заставляет сайт прыгать и занимать область, где был свиток.
Я хочу знать, возможно ли отключить полосу прокрутки, пока она еще отображается.
27 ответов
Если страница под оверлеем может быть "зафиксирована" вверху, при открытии оверлея вы можете установить
body { position: fixed; overflow-y:scroll }
вы все равно должны увидеть правую полосу прокрутки, но содержимое не прокручивается. Когда вы закрываете оверлей, просто возвращаете эти свойства
body { position: static; overflow-y:auto }
Я просто предложил этот способ только потому, что вам не нужно менять какое-либо событие прокрутки
Обновить
Вы также можете сделать небольшое улучшение: если вы получите document.documentElement.scrollTop
свойство через javascript непосредственно перед открытием слоя, вы можете динамически назначить это значение как top
свойство элемента body: при таком подходе страница будет стоять на своем месте, независимо от того, находитесь ли вы сверху или уже прокрутили.
Css
.noscroll { position: fixed; overflow-y:scroll }
JS
$('body').css('top', -(document.documentElement.scrollTop) + 'px')
.addClass('noscroll');
Четыре небольших дополнения к выбранному решению:
- Примените noscroll к html, а не к телу, чтобы избежать двойных полос прокрутки в IE
- Чтобы проверить, есть ли на самом деле полоса прокрутки, перед добавлением класса 'noscroll'. В противном случае сайт также будет перепрыгивать при нажатии новой полосы прокрутки без прокрутки.
- Чтобы сохранить любой возможный scrollTop, чтобы вся страница не возвращалась наверх (как обновление Фабрицио, но вам нужно получить значение перед добавлением класса 'noscroll')
- Не все браузеры обрабатывают scrollTop так же, как описано на http://help.dottoro.com/ljnvjiow.php
Полное решение, которое работает для большинства браузеров:
CSS
html.noscroll {
position: fixed;
overflow-y: scroll;
width: 100%;
}
Отключить прокрутку
if ($(document).height() > $(window).height()) {
var scrollTop = ($('html').scrollTop()) ? $('html').scrollTop() : $('body').scrollTop(); // Works for Chrome, Firefox, IE...
$('html').addClass('noscroll').css('top',-scrollTop);
}
Включить прокрутку
var scrollTop = parseInt($('html').css('top'));
$('html').removeClass('noscroll');
$('html,body').scrollTop(-scrollTop);
Спасибо Фабрицио и Деяну за то, что они поставили меня на правильный путь, и Бродинго за решение с двойной полосой прокрутки
С включенным jQuery:
запрещать
$.fn.disableScroll = function() {
window.oldScrollPos = $(window).scrollTop();
$(window).on('scroll.scrolldisabler',function ( event ) {
$(window).scrollTop( window.oldScrollPos );
event.preventDefault();
});
};
включить
$.fn.enableScroll = function() {
$(window).off('scroll.scrolldisabler');
};
использование
//disable
$("#selector").disableScroll();
//enable
$("#selector").enableScroll();
Я ОП
С помощью ответа от fcalderan я смог сформировать решение. Я оставляю свое решение здесь, поскольку оно дает ясность в том, как его использовать, и добавляет очень важные детали, width: 100%;
Я добавляю этот класс
body.noscroll
{
position: fixed;
overflow-y: scroll;
width: 100%;
}
это сработало для меня, и я использовал Fancyapp.
Это сработало очень хорошо для меня....
// disable scrolling
$('body').bind('mousewheel touchmove', lockScroll);
// enable scrolling
$('body').unbind('mousewheel touchmove', lockScroll);
// lock window scrolling
function lockScroll(e) {
e.preventDefault();
}
просто оберните эти две строки кода тем, что решит, когда вы собираетесь заблокировать прокрутку.
например
$('button').on('click', function() {
$('body').bind('mousewheel touchmove', lockScroll);
});
Вы не можете отключить событие прокрутки, но вы можете отключить связанные действия, которые приводят к прокрутке, такие как колесико мыши и перемещение пальцем:
$('body').on('mousewheel touchmove', function(e) {
e.preventDefault();
});
Кроме того, вы можете скрыть полосу прокрутки тела с overflow: hidden
и установить поле одновременно, чтобы содержимое не перепрыгивало:
let marginRightPx = 0;
if(window.getComputedStyle) {
let bodyStyle = window.getComputedStyle(document.body);
if(bodyStyle) {
marginRightPx = parseInt(bodyStyle.marginRight, 10);
}
}
let scrollbarWidthPx = window.innerWidth - document.body.clientWidth;
Object.assign(document.body.style, {
overflow: 'hidden',
marginRight: `${marginRightPx + scrollbarWidthPx}px`
});
А затем вы можете добавить отключенную полосу прокрутки на страницу, чтобы заполнить пробел:
textarea {
overflow-y: scroll;
overflow-x: hidden;
width: 11px;
outline: none;
resize: none;
position: fixed;
top: 0;
right: 0;
bottom: 0;
border: 0;
}
<textarea></textarea>
Я сделал именно это для моей собственной реализации лайтбокса. Кажется, до сих пор работает хорошо.
Вот рабочая демонстрация. Вот как вы можете сделать это с помощью чистого JavaScript:
const { body, documentElement } = document;
let { scrollTop } = document.documentElement;
function disableScroll() {
scrollTop = documentElement.scrollTop;
body.style.top = `-${scrollTop}px`;
body.classList.add("scroll-disabled");
}
function enableScroll() {
body.classList.remove("scroll-disabled");
documentElement.scrollTop = scrollTop;
body.style.removeProperty("top");
}
А это CSS:
.scroll-disabled {
position: fixed;
width: 100%;
overflow-y: scroll;
}
Мы используем on, чтобы предотвратить его прокрутку, и мы используем
overflow-y
чтобы показать полосу прокрутки. Нам также нужно установить
width
из-за того, как
position: fixed
работает.
Мы отслеживаем положение прокрутки и обновляем его при отключении прокрутки, чтобы мы могли правильно позиционировать, используя, когда прокрутка отключена, и восстанавливать позицию прокрутки, когда она включена. В противном случае будет продолжаться переход вверх при отключении или включении прокрутки.
При включении прокрутки мы удаляем
top
стиль из . Это предотвращает нарушение макета, если у вас другой
position
чем
static
на
body
.
Если вы используете
scroll-behavior: smooth
на
html
, вам также необходимо изменить
enableScroll
функционировать следующим образом:
function enableScroll() {
body.classList.remove("scroll-disabled");
// Set "scroll-behavior" to "auto"
documentElement.style.scrollBehavior = "auto";
documentElement.scrollTop = scrollTop;
// Remove "scroll-behavior: auto" after restoring scroll position
documentElement.style.removeProperty("scroll-behavior");
body.style.removeProperty("top");
}
Нам нужно временно установить
scroll-behavior
к
auto
чтобы не было скачков.
Еще одно решение избавиться от скачка содержимого на фиксированном модальном окне при удалении прокрутки тела - нормализовать ширину страницы:
body {width: 100vw; overflow-x: hidden;}
Затем вы можете играть с фиксированным положением или переполнением: скрыто для тела, когда модальное окно открыто. Но он скроет горизонтальные полосы прокрутки - обычно они не нужны на адаптивном веб-сайте.
Это решение, с которым мы пошли. Просто сохраните позицию прокрутки, когда оверлей открыт, прокрутите назад до сохраненной позиции каждый раз, когда пользователь пытался прокрутить страницу, и выключите слушателя, когда оверлей закрыт.
Это немного нервно в IE, но работает как шарм в Firefox/Chrome.
var body = $("body"),
overlay = $("#overlay"),
overlayShown = false,
overlayScrollListener = null,
overlaySavedScrollTop = 0,
overlaySavedScrollLeft = 0;
function showOverlay() {
overlayShown = true;
// Show overlay
overlay.addClass("overlay-shown");
// Save scroll position
overlaySavedScrollTop = body.scrollTop();
overlaySavedScrollLeft = body.scrollLeft();
// Listen for scroll event
overlayScrollListener = body.scroll(function() {
// Scroll back to saved position
body.scrollTop(overlaySavedScrollTop);
body.scrollLeft(overlaySavedScrollLeft);
});
}
function hideOverlay() {
overlayShown = false;
// Hide overlay
overlay.removeClass("overlay-shown");
// Turn scroll listener off
if (overlayScrollListener) {
overlayScrollListener.off();
overlayScrollListener = null;
}
}
// Click toggles overlay
$(window).click(function() {
if (!overlayShown) {
showOverlay();
} else {
hideOverlay();
}
});
/* Required */
html, body { margin: 0; padding: 0; height: 100%; background: #fff; }
html { overflow: hidden; }
body { overflow-y: scroll; }
/* Just for looks */
.spacer { height: 300%; background: orange; background: linear-gradient(#ff0, #f0f); }
.overlay { position: fixed; top: 20px; bottom: 20px; left: 20px; right: 20px; z-index: -1; background: #fff; box-shadow: 0 0 5px rgba(0, 0, 0, .3); overflow: auto; }
.overlay .spacer { background: linear-gradient(#88f, #0ff); }
.overlay-shown { z-index: 1; }
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<h1>Top of page</h1>
<p>Click to toggle overlay. (This is only scrollable when overlay is <em>not</em> open.)</p>
<div class="spacer"></div>
<h1>Bottom of page</h1>
<div id="overlay" class="overlay">
<h1>Top of overlay</h1>
<p>Click to toggle overlay. (Containing page is no longer scrollable, but this is.)</p>
<div class="spacer"></div>
<h1>Bottom of overlay</h1>
</div>
Мне нравится придерживаться метода "overflow: hidden" и просто добавить padding-right, равный ширине полосы прокрутки.
Получить функцию ширины полосы прокрутки, по lostsource.
function getScrollbarWidth() {
var outer = document.createElement("div");
outer.style.visibility = "hidden";
outer.style.width = "100px";
outer.style.msOverflowStyle = "scrollbar"; // needed for WinJS apps
document.body.appendChild(outer);
var widthNoScroll = outer.offsetWidth;
// force scrollbars
outer.style.overflow = "scroll";
// add innerdiv
var inner = document.createElement("div");
inner.style.width = "100%";
outer.appendChild(inner);
var widthWithScroll = inner.offsetWidth;
// remove divs
outer.parentNode.removeChild(outer);
return widthNoScroll - widthWithScroll;
}
При отображении наложения добавьте класс "noscroll" в html и добавьте padding-right для body:
$(html).addClass("noscroll");
$(body).css("paddingRight", getScrollbarWidth() + "px");
Когда прячешься, убираешь класс и отступы:
$(html).removeClass("noscroll");
$(body).css("paddingRight", 0);
Стиль Noscroll таков:
.noscroll { overflow: hidden; }
Обратите внимание, что если у вас есть какие-либо элементы с позиции position: fixed, вам также необходимо добавить отступы к этим элементам.
Все модальные / лайтбоксовые системы на основе JavaScript используют переполнение при отображении модального / лайтбокса, тега html или тега body.
Когда лайтбокс отображается, js вызывает переполнение, скрытое в теге html или body. Когда лайтбокс скрыт, некоторые удаляют скрытый, другие нажимают на авто переполнение в тегах html или body.
Разработчики, работающие на Mac, не видят проблемы с полосой прокрутки.
Просто замените скрытое на unset, чтобы контент не проскальзывал под модальностью удаления полосы прокрутки.
Лайтбокс открыть / показать:
<html style="overflow: unset;"></html>
Лайтбокс закрыть / скрыть:
<html style="overflow: auto;"></html>
Вы можете сохранить переполнение: скрытое, но управлять позицией прокрутки вручную:
перед показом следите за фактическим положением прокрутки:
var scroll = [$(document).scrollTop(),$(document).scrollLeft()];
//show your lightbox and then reapply scroll position
$(document).scrollTop(scroll[0]).scrollLeft(scroll[1]);
он должен работать
Я сделал одну функцию, которая решает эту проблему с помощью JS. Этот принцип можно легко расширить и настроить, что для меня является большим профессионалом.
Используя эту функцию js DOM API:
const handleWheelScroll = (element) => (event) => {
if (!element) {
throw Error("Element for scroll was not found");
}
const { deltaY } = event;
const { clientHeight, scrollTop, scrollHeight } = element;
if (deltaY < 0) {
if (-deltaY > scrollTop) {
element.scrollBy({
top: -scrollTop,
behavior: "smooth",
});
event.stopPropagation();
event.preventDefault();
}
return;
}
if (deltaY > scrollHeight - clientHeight - scrollTop) {
element.scrollBy({
top: scrollHeight - clientHeight - scrollTop,
behavior: "smooth",
});
event.stopPropagation();
event.preventDefault();
return;
}
};
Короче говоря, эта функция остановит распространение событий и поведение по умолчанию, если прокрутка будет прокручивать что-то еще, кроме данного элемента (того, который вы хотите прокрутить).
Затем вы можете зацепить и отцепить это вот так:
const wheelEventHandler = handleWheelScroll(elementToScrollIn);
window.addEventListener("wheel", wheelEventHandler, {
passive: false,
});
window.removeEventListener("wheel", wheelEventHandler);
Обратите внимание на то, что это функция более высокого порядка, поэтому вам нужно сохранить ссылку на данный экземпляр.
Я подключаю
addEventListener
часть мыши введите и отцепите
removeEventListener
в мыши оставьте события в jQuery, но вы можете использовать его как хотите.
У меня была похожая проблема: левое меню, которое, когда оно появляется, предотвращает прокрутку. Как только высота была установлена на 100vh, полоса прокрутки исчезла, и содержимое дернулось вправо.
Поэтому, если вы не против оставить полосу прокрутки включенной (но установить окно на полную высоту, чтобы оно нигде не прокручивалось), тогда другой возможностью является установка крошечного нижнего поля, при котором полосы прокрутки будут отображаться:
body {
height: 100vh;
overflow: hidden;
margin: 0 0 1px;
}
<div id="lightbox">
находится внутри <body>
элемент, таким образом, когда вы прокручиваете лайтбокс, вы также прокручиваете тело. Решение состоит в том, чтобы не расширять <body>
элемент более 100%, чтобы поместить длинный контент в другой div
элемент и добавить полосу прокрутки, если это необходимо div
элемент с overflow: auto
,
html {
height: 100%
}
body {
margin: 0;
height: 100%
}
#content {
height: 100%;
overflow: auto;
}
#lightbox {
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
}
<html>
<body>
<div id="content">much content</div>
<div id="lightbox">lightbox<div>
</body>
</html>
Теперь, прокручивая лайтбокс (и body
также) не имеет никакого эффекта, потому что тело не превышает 100% высоты экрана.
Если страница под оверлеем может быть "зафиксирована" вверху, при открытии оверлея вы можете установить
.disableScroll { position: fixed; overflow-y:scroll }
предоставить этот класс для прокручиваемого тела, вы все равно должны увидеть правую полосу прокрутки, но содержимое не прокручивается.
Чтобы сохранить положение страницы, сделайте это в jquery
$('body').css('top', - ($(window).scrollTop()) + 'px').addClass('disableScroll');
Когда вы закрываете оверлей, просто возвращаете эти свойства
var top = $('body').position().top;
$('body').removeClass('disableScroll').css('top', 0).scrollTop(Math.abs(top));
Я просто предложил этот способ только потому, что вам не нужно менять какое-либо событие прокрутки
Это остановит прыжок области просмотра вверх, сохраняя положение прокрутки и восстанавливая его при включении прокрутки.
CSS
.no-scroll{
position: fixed;
width:100%;
min-height:100vh;
top:0;
left:0;
overflow-y:scroll!important;
}
JS
var scrollTopPostion = 0;
function scroll_pause(){
scrollTopPostion = $(window).scrollTop();
$("body").addClass("no-scroll").css({"top":-1*scrollTopPostion+"px"});
}
function scroll_resume(){
$("body").removeClass("no-scroll").removeAttr("style");
$(window).scrollTop(scrollTopPostion);
}
Теперь все, что вам нужно сделать, это вызвать функции
$(document).on("click","#DISABLEelementID",function(){
scroll_pause();
});
$(document).on("click","#ENABLEelementID",function(){
scroll_resume();
});
В position: fixed;
У решения есть недостаток - при применении этого стиля страница перескакивает вверх. У Angular's Material Dialog есть хорошее решение, в котором они имитируют положение прокрутки, применяя позиционирование кhtml
элемент.
Ниже мой измененный алгоритм только для вертикальной прокрутки. Блокировка левой прокрутки выполняется точно так же.
// This class applies the following styles:
// position: fixed;
// overflow-y: scroll;
// width: 100%;
const NO_SCROLL_CLASS = "bp-no-scroll";
const coerceCssPixelValue = value => {
if (value == null) {
return "";
}
return typeof value === "string" ? value : `${value}px`;
};
export const blockScroll = () => {
const html = document.documentElement;
const documentRect = html.getBoundingClientRect();
const { body } = document;
// Cache the current scroll position to be restored later.
const cachedScrollPosition =
-documentRect.top || body.scrollTop || window.scrollY || document.scrollTop || 0;
// Cache the current inline `top` value in case the user has set it.
const cachedHTMLTop = html.style.top || "";
// Using `html` instead of `body`, because `body` may have a user agent margin,
// whereas `html` is guaranteed not to have one.
html.style.top = coerceCssPixelValue(-cachedScrollPosition);
// Set the magic class.
html.classList.add(NO_SCROLL_CLASS);
// Return a function to remove the scroll block.
return () => {
const htmlStyle = html.style;
const bodyStyle = body.style;
// We will need to seamlessly restore the original scroll position using
// `window.scroll`. To do that we will change the scroll behavior to `auto`.
// Here we cache the current scroll behavior to restore it later.
const previousHtmlScrollBehavior = htmlStyle.scrollBehavior || "";
const previousBodyScrollBehavior = bodyStyle.scrollBehavior || "";
// Restore the original inline `top` value.
htmlStyle.top = cachedHTMLTop;
// Remove the magic class.
html.classList.remove(NO_SCROLL_CLASS);
// Disable user-defined smooth scrolling temporarily while we restore the scroll position.
htmlStyle.scrollBehavior = bodyStyle.scrollBehavior = "auto";
// Restore the original scroll position.
window.scroll({
top: cachedScrollPosition.top
});
// Restore the original scroll behavior.
htmlStyle.scrollBehavior = previousHtmlScrollBehavior;
bodyStyle.scrollBehavior = previousBodyScrollBehavior;
};
};
Логика очень проста, и ее можно упростить еще больше, если вам не нужны определенные крайние случаи. Например, вот что я использую:
export const blockScroll = () => {
const html = document.documentElement;
const documentRect = html.getBoundingClientRect();
const { body } = document;
const screenHeight = window.innerHeight;
// Only do the magic if document is scrollable
if (documentRect.height > screenHeight) {
const cachedScrollPosition =
-documentRect.top || body.scrollTop || window.scrollY || document.scrollTop || 0;
html.style.top = coerceCssPixelValue(-cachedScrollPosition);
html.classList.add(NO_SCROLL_CLASS);
return () => {
html.classList.remove(NO_SCROLL_CLASS);
window.scroll({
top: cachedScrollPosition,
behavior: "auto"
});
};
}
};
Я заметил, что сайт YouTube делает именно это. Итак, немного изучив его, я смог определить, что они используют
@polymer/iron-overlay-behavior
и, к счастью, его можно довольно ненавязчиво использовать вне веб-компонентов / Polymer:
import {
pushScrollLock,
removeScrollLock,
} from '@polymer/iron-overlay-behavior/iron-scroll-manager';
// lock scroll everywhere except scrollElement
pushScrollLock(scrollElement);
// restore scrolling
removeScrollLock(scrollElement);
- Позволяет прокручивать выбранный элемент
- Ни в коем случае не портит укладку
- Проверено в боях на сайте YouTube
Это похоже на зрелое решение и, безусловно, лучшее, что я смог найти. Пакет немного тяжелый, но я предполагаю, что большая его часть рассыпается при импорте только
iron-scroll-manager
.
Ваше здоровье
У меня есть некоторые другие фиксированные элементы на странице и настройка
body
х
position
к
fixed
вызвал кучу других проблем, поэтому я сделал это хакерским способом:
const scrollbarWidth = window.innerWidth - document.documentElement.clientWidth;
// on opening modal
document.body.style.overflow = "hidden"
document.body.style.paddingRight = `${scrollbarWidth}px`
// on closing modal
document.body.style.overflow = "unset",
document.body.style.paddingRight = "0px"
Идея состоит в том, чтобы добавить
padding-right
с той же шириной, что и полоса прокрутки браузера, чтобы имитировать фальшивую полосу прокрутки и предотвратить сдвиг содержимого.
JavaScript:
const disableScroll = () => {
window.scrollTo({
top: 0,
behavior: "instant"
})
}
document.addEventListener("scroll", disableScroll, false)
Реакция + ТС:
type Props = {
open: boolean
onClose: () => void
disableClickOutside?: boolean
disabledEscKey?: boolean
width?: string
theme?: Theme
size?: Size
children?: ReactNode
}
export const Modal = ({
open,
children,
width,
theme = "light",
onClose,
disableClickOutside,
disabledEscKey,
size = "md",
}: Props) => {
const contentRef: RefObject<HTMLDivElement> = useRef<HTMLDivElement>(null)
useEffect(() => {
const { scrollTop } = document.documentElement
const disableScroll = () => {
window.scrollTo({
top: scrollTop,
behavior: "instant" as ScrollBehavior,
})
}
if (open) {
contentRef?.current?.focus()
document.addEventListener("scroll", disableScroll, false)
} else if (!open) {
document.removeEventListener("scroll", disableScroll)
}
return () => {
document.removeEventListener("scroll", disableScroll)
}
}, [open])
const handleClickOutside = (event: React.MouseEvent<Element>) => {
if (disableClickOutside) return
const clickedElement = event.target as HTMLDivElement
if (!contentRef?.current?.contains(clickedElement) && open) onClose()
}
const handleKeydown = (event: React.KeyboardEvent<Element>) => {
if (disabledEscKey) return
if (event.key === "Escape") onClose()
}
return (
<Wrapper
aria-label="modal-wrapper"
isOpen={open}
onKeyDown={(e: React.KeyboardEvent<Element>) => handleKeydown(e)}
onClick={(e: React.MouseEvent<Element>) => handleClickOutside(e)}
tabIndex={-1}
>
<Content
theme={theme}
ref={contentRef}
width={width}
size={size}
tabIndex={1}
>
{children}
</Content>
</Wrapper>
)
}
type StyledModalProps = {
width?: string
theme?: Theme
size: Size
}
const Wrapper = styled.div`
display: ${(props: { isOpen: boolean }) => (props.isOpen ? "flex" : "none")};
align-items: center;
justify-content: center;
z-index: 3;
position: fixed;
top: 0;
bottom: 0;
left: 0;
right: 0;
background-color: rgba(0, 0, 0, 0.3);
&:focus {
outline: none;
}
`
const Content = styled.div`
display: flex;
justify-content: center;
background-color: ${({ theme }: StyledModalProps) =>
match(theme)
.with("dark", () => Gray90)
.otherwise(() => White)};
padding: 2rem;
border-radius: 1rem;
box-shadow: 0px 0px 1rem
${({ theme }: StyledModalProps) =>
match(theme)
.with("dark", () => "rgba(0, 0, 0, 0.48)")
.otherwise(() => "rgba(28, 28, 31, 0.12)")};
width: ${({ width, size }: StyledModalProps) =>
width ||
match(size)
.with("lg", () => "67.5rem")
.with("md", () => "45rem")
.with("sm", () => "28.75rem")
.with("xs", () => "20rem")
.exhaustive()};
`
Грубым, но рабочим способом будет принудительная прокрутка вверх, таким образом, эффективно отключая прокрутку:
var _stopScroll = false;
window.onload = function(event) {
document.onscroll = function(ev) {
if (_stopScroll) {
document.body.scrollTop = "0px";
}
}
};
Когда вы открываете лайтбокс, поднимите флаг и, закрыв его, опустите флаг.
Реакт версия:
- сохраняет положение прокрутки
- нет перекомпоновки макета
- машинопись
https://gist.github.com/airtonix/c8c9af146185646e7451faa0f2ac96b7
используйте его как:
// app
<BlanketProvider color='red'>
<YourView />
</BlanketProvider>
// YourView
...
const { isOpen, setIsOpen } = useBlanket();
return (
<>
<Blanket>
{isOpen && <SomeThingWithHigherZindex />}
</Blanket>
<Button onClick={() => setIsOpen(true)}>Do A Thing</Button>
</>
)
При настройкеisOpen
в true мы отслеживаем текущую прокрутку сверху и сохраняем ее.
Мы устанавливаем его только для стилей кузова, если мы открываем, потому что то, что мы делаем дальше, вызоветwindow.scrollY
быть0
.
Затем мы проверяем, выше ли документ, чем окно просмотра. Если это так, мы устанавливаем переполнение Y наscroll
чтобы убедиться, что полоса прокрутки не исчезает и нет скачка перекомпоновки макета.
Установите тело в фиксированное положение, предотвращая его прокрутку, и убедитесь, что документ находится в правильном положении прокрутки, чтобы противодействоватьposition: fixed;
Вы можете сделать это с помощью Javascript:
// Classic JS
window.onscroll = function(ev) {
ev.preventDefault();
}
// jQuery
$(window).scroll(function(ev) {
ev.preventDefault();
}
А затем отключите его, когда ваш лайтбокс закрыт.
Но если ваш лайтбокс содержит полосу прокрутки, вы не сможете прокручивать ее, пока она открыта. Это потому что window
содержит как body
а также #lightbox
, Поэтому вы должны использовать архитектуру, подобную следующей:
<body>
<div id="global"></div>
<div id="lightbox"></div>
</body>
А затем применить onscroll
событие только на #global
,
Я создал репозиторий для решения именно этой проблемы. Ниже я свел решение к сути, но репозиторий с поддержкой горизонтальной прокрутки и другими параметрами настройки можно найти здесь:
Вот ссылка прямо на рабочую демонстрационную страницу: http://alexanderspirgel.com/no-scroll/demo/
Мое решение работает путем сравнения разницы в размерах внешнего элемента с полосой прокрутки и его внутреннего элемента. После расчета размера полосы прокрутки элемент прокрутки можно установить наoverflow: hidden
и можно применить смещение, равное ширине полосы прокрутки, чтобы предотвратить сдвиг содержимого. Кроме того, это решение помещает псевдоэлемент с отключенной полосой прокрутки в новое пространство, созданное после отключения прокрутки.
Сначала настройте HTML так, чтобы внутренний элемент был обернут внешним элементом (внешним и внутренним элементами могут быть и<body>
элементы).
<html>
<body>
content here...
</body>
</html>
Скрипт устанавливает переменные CSS и применяет правильные атрибуты данных HTML, но остальное контролируется CSS. Включите этот CSS, чтобы элементы имели необходимые стили.
[data-no-scroll-element=outer] {
--no-scroll--y-scrollbar-width: 0px;
box-sizing: border-box;
position: relative;
padding: 0; /* Padding on outer elements breaks disabled scrollbar placeholder spacing. */
overflow: auto; /* Accounts for space of internal margins. */
}
[data-no-scroll-element=outer][data-no-scroll-y-state=no-scroll] {
overflow-y: hidden;
}
[data-no-scroll-element=outer]::after {
content: "";
box-sizing: border-box;
display: none;
position: sticky;
top: 0;
bottom: 0;
left: 0;
right: 0;
width: 100%;
height: 100%;
overflow-x: hidden;
overflow-y: hidden;
pointer-events: none;
}
[data-no-scroll-element=outer][data-no-scroll-y-state=no-scroll]::after {
display: block;
overflow-y: scroll;
}
html[data-no-scroll-element=outer]::after {
height: 100vh; /* If the outer element is <html> the after element must be set to the viewport height to work properly. */
}
[data-no-scroll-element=inner] {
box-sizing: border-box;
display: block;
width: auto;
height: auto;
max-width: none;
max-height: none;
border: 0; /* Border on inner elements breaks scrollbar size calculations. */
margin: 0; /* Margin on inner elements breaks scrollbar size calculations. */
padding: 0; /* Padding on inner elements breaks scrollbar size calculations. */
overflow: auto; /* Accounts for space of internal margins. */
}
[data-no-scroll-element=outer][data-no-scroll-y-state=no-scroll] > [data-no-scroll-element=inner] {
margin-right: var(--no-scroll--y-scrollbar-width, 0px);
}
Включите этот JavaScript для обработки расчета ширины полосы прокрутки и переключения между включением и отключением прокрутки.
class noScroll {
static isOuterElementDocumentElement(outerElement) {
if (outerElement === document.documentElement) {
return true;
}
else {
return false;
}
}
static getElementYScrollbarWidth(outerElement, innerElement) {
let size = 0;
let outerSize = outerElement.offsetWidth;
if (this.isOuterElementDocumentElement(outerElement)) {
outerSize = window.innerWidth;
}
const innerSize = innerElement.offsetWidth;
const outerElementComputedStyles = window.getComputedStyle(outerElement);
const borderLeftWidth = parseInt(outerElementComputedStyles.borderLeftWidth);
const borderRightWidth = parseInt(outerElementComputedStyles.borderRightWidth);
size = (outerSize - borderLeftWidth - borderRightWidth) - innerSize;
if (size < 0) {
size = 0;
}
return size;
}
static setYScrollbarWidthCSSVariable(outerElement, innerElement, value) {
if (typeof value === 'undefined') {
value = this.getElementYScrollbarWidth(outerElement, innerElement);
}
if (typeof value === 'number') {
value = value.toString() + 'px';
}
outerElement.style.setProperty('--no-scroll--y-scrollbar-width', value);
if (this.isOuterElementDocumentElement(outerElement)) {
outerElement.style.setProperty('--no-scroll--document-width-offset', value);
}
}
static isScrollEnabled(outerElement) {
if (outerElement.getAttribute('data-no-scroll-y-state') === 'no-scroll') {
return false;
}
return true;
}
static disableScroll(outerElement, innerElement) {
outerElement.setAttribute('data-no-scroll-element', 'outer');
innerElement.setAttribute('data-no-scroll-element', 'inner');
this.setYScrollbarWidthCSSVariable(outerElement, innerElement);
outerElement.setAttribute('data-no-scroll-y-state', 'no-scroll');
}
static enableScroll(outerElement, innerElement) {
this.setYScrollbarWidthCSSVariable(outerElement, innerElement, 0);
outerElement.setAttribute('data-no-scroll-y-state', 'scroll');
}
static toggleScroll(outerElement, innerElement) {
const isScrollEnabled = this.isScrollEnabled(outerElement);
if (isScrollEnabled) {
this.disableScroll(outerElement, innerElement);
}
else {
this.enableScroll(outerElement, innerElement);
}
}
};
Затем прокрутку можно отключить/включить по вашему усмотрению, вызвав такие методы:
const outerElement = document.querySelector('html');
const innerElement = document.querySelector('body');
// disable
noScroll.disableScroll(outerElement, innerElement);
// enable
noScroll.enableScroll(outerElement, innerElement);
// toggle
noScroll.toggleScroll(outerElement, innerElement);
Если вы отключите прокрутку элемента, ширина веб-сайта изменится. Это вызоветposition: fixed
элементы, обеспечивающие нежелательный «прыжок» в нужном положении. Чтобы решить эту проблему, при отключении прокрутки<html>
элемент, переменная CSS, называемая--no-scroll--document-width-offset
создается со значением, равным изменению ширины. Вы можете включить эту переменную CSS в положение фиксированных элементов, чтобы предотвратить «переход» следующим образом:
.fixed-centered {
position: fixed;
left: calc((100% - var(--no-scroll--document-width-offset, 0px)) / 2);
transform: translateX(-50%);
}
Я надеюсь, что это сработает для вас. Опять же, это сокращенная версия моего более полного решения, расположенного здесь: https://github.com/alexspirgel/no-scroll .https://github.com/alexspirgel/no-scroll .
Я решил эту проблему с помощью метода scrollLock, который настраивает прослушиватели событий колесика прокрутки и событий нажатия клавиш, а также метода preventScroll, обрабатывающего эти события. Что-то вроде этого:
preventScroll = function (e) {
// prevent scrollwheel events
e.preventDefault();
e.stopPropagation();
// prevent keydown events
var keys = [32, 33, 34, 35, 37, 38, 39, 40];
if (keys.includes(e.keyCode)) {
e.preventDefault();
}
return false;
}
scrollLock = function (lock) {
if (lock) {
document.querySelector("#container").addEventListener("wheel", preventScroll);
document.addEventListener("keydown", preventScroll);
}
else {
document.querySelector("#container").removeEventListener("wheel", preventScroll);
document.querySelector("#container").removeEventListener("keydown", preventScroll);
}
}