如何禁用滚动而不隐藏它?

我试图禁用父母的html/身体滚动条,而我正在使用一个灯箱。这里的主要词是禁用。我做想用overflow: hidden;隐藏它。

这样做的原因是overflow: hidden使站点跳转并占用滚动的区域。

我想知道是否有可能禁用滚动条,同时仍然显示它。

248317 次浏览

你可以用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>

然后只在#global上应用onscroll事件。

你可以保持overflow:隐藏,但手动管理滚动位置:

在显示实际滚动位置之前保持跟踪:

var scroll = [$(document).scrollTop(),$(document).scrollLeft()];
//show your lightbox and then reapply scroll position
$(document).scrollTop(scroll[0]).scrollLeft(scroll[1]);

应该可以

粗糙但有效的方法是强制滚动回顶部,从而有效地禁用滚动:

var _stopScroll = false;
window.onload = function(event) {
document.onscroll = function(ev) {
if (_stopScroll) {
document.body.scrollTop = "0px";
}
}
};

打开灯箱时升起旗子,关闭灯箱时降下旗子。

现场测试用例

如果覆盖层下的页面可以“固定”在顶部,当你打开覆盖层时,你可以设置

body { position: fixed; overflow-y:scroll }

您应该仍然可以看到右边的滚动条,但是内容是不可滚动的。当你关闭覆盖时,只需恢复这些属性

body { position: static; overflow-y:auto }

我之所以建议采用这种方式,只是因为您不需要更改任何滚动事件

更新

你还可以做一点改进:如果你在层打开之前通过javascript获得document.documentElement.scrollTop属性,你可以动态地将该值赋值为body元素的top属性:使用这种方法,页面将位于它的位置,无论你是在顶部还是已经滚动了。

Css

.noscroll { position: fixed; overflow-y:scroll }

JS

$('body').css('top', -(document.documentElement.scrollTop) + 'px')
.addClass('noscroll');

接受的解决方案的四个小补充:

  1. HTML而不是body应用'noscroll'来防止IE中的双滚动条
  2. 在添加'noscroll'类之前,检查是否有滚动条。否则,站点还会跳转推送新的非滚动滚动条。
  3. 为了保留任何可能的scrollTop,这样整个页面就不会回到顶部(就像Fabrizio的更新一样,但你需要在添加'noscroll'类之前获取这个值)
  4. 并非所有浏览器处理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);

感谢法布里齐奥和德扬把我带到正确的轨道上,感谢布罗丁戈为解决双滚动条

这是我们的解。简单地保存覆盖打开时的滚动位置,在用户试图滚动页面时滚动回保存的位置,并在覆盖关闭时关闭侦听器。

它在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”方法,只需添加与滚动条宽度相等的右填充。

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

当显示覆盖时,在html中添加"noscroll"类,并在body中添加padding-right:

$(html).addClass("noscroll");
$(body).css("paddingRight", getScrollbarWidth() + "px");

当隐藏时,删除类和填充:

$(html).removeClass("noscroll");
$(body).css("paddingRight", 0);

noscroll样式是这样的:

.noscroll { overflow: hidden; }

注意,如果你有任何带有position:fixed的元素,你也需要为这些元素添加填充。

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

<div id="lightbox">位于<body>元素内部,因此当你滚动灯箱时,你也会滚动灯箱的主体。解决方案是不要将<body>元素扩展到100%,将长内容放在另一个div元素中,并在需要时使用overflow: auto为这个div元素添加滚动条。

.
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%。

你不能禁用滚动事件,但是你可以禁用导致滚动的相关操作,比如鼠标滚轮和touchmove:

$('body').on('mousewheel touchmove', function(e) {
e.preventDefault();
});

我是OP

在fcalderan回答的帮助下,我能够形成一个解决方案。我把我的解决方案留在这里,因为它清楚地说明了如何使用它,并添加了一个非常关键的细节,width: 100%;

我添加了这个类

body.noscroll
{
position: fixed;
overflow-y: scroll;
width: 100%;
}

这对我很有用,我用的是fantyapp。

你可以用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>

I did exactly this for my own lightbox implementation. Seems to be working well so far.

我也遇到过类似的问题:左侧菜单出现时,无法滚动。一旦高度设置为100vh,滚动条就会消失,内容会向右抽搐。

因此,如果你不介意保持滚动条启用(但设置窗口全高,这样它实际上不会滚动到任何地方),那么另一种可能性是设置一个小的底部边距,这将保持滚动条显示:

body {
height: 100vh;
overflow: hidden;
margin: 0 0 1px;
}

这对我来说真的很好....

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

所有基于javascript的modal/lightbox系统在html标签或body标签上显示modal/lightbox时都会使用溢出。

当lightbox显示时,js会推送一个隐藏在html或body标签上的溢出。 当lightbox被隐藏时,一些人会删除隐藏的,另一些人会在html或body标签上推一个溢出自动

在Mac上工作的开发人员没有看到滚动条的问题。

只需替换隐藏的unset不看到内容滑动下的模式删除滚动条。

Lightbox开放/显示:

<html style="overflow: unset;"></html>

Lightbox隐藏/关闭:

<html style="overflow: auto;"></html>

如果覆盖层下的页面可以“固定”在顶部,当你打开覆盖层时,你可以设置

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

另一个在固定模式上摆脱内容跳转的解决方案是,当删除主体滚动时,将页面宽度归一化:

body {width: 100vw; overflow-x: hidden;}

然后你可以玩固定位置或溢出:隐藏的身体时,模式是开放的。但它会隐藏水平滚动条-通常他们不需要响应式网站。

position: fixed;解决方案有一个缺点-当应用此样式时,页面跳转到顶部。Angular的材质对话框有一个很好的解决方案,他们通过对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"
});
};
}
};

我已经做了这个函数,用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);

注意,它是一个高阶函数,所以必须保持对给定实例的引用。

在jQuery中,我在鼠标进入中挂钩addEventListener部分,在鼠标离开事件中取消挂钩removeEventListener,但你可以随心所欲地使用它。

我注意到YouTube网站就是这么做的。因此,通过检查它一点,我已经能够确定他们正在使用@polymer/iron-overlay-behavior,幸运的是,它可以在web组件/聚合物之外使用:

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时,它的大部分都被解绑定了。

干杯

这是一个演示工作。这是你如何用纯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;
}

我们在body上使用position: fixed来防止它是可滚动的,我们使用overflow-y来显示滚动条。我们还需要设置width,因为position: fixed是如何工作的。

我们跟踪滚动位置,并在禁用滚动时更新它,以便在禁用滚动时使用top适当地定位body,并在启用滚动时恢复滚动位置。否则,当禁用或启用滚动时,body将继续跳转到顶部。

当启用滚动时,我们从body中删除top样式。这可以防止它破坏你的布局,如果你在body上有不同的positionstatic

如果你在html上使用scroll-behavior: smooth,你还需要像这样修改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,这样就没有跳转了。

我在页面中有一些其他固定元素,将bodyposition设置为fixed会导致一系列其他问题,所以我用一种hack的方式来做:

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,以模仿假滚动条并防止内容移动。

我用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);
}
}