防止身体滚动,但允许覆盖滚动

我一直在寻找一个“灯箱”类型的解决方案,允许这一点,但还没有找到一个(请建议,如果你知道任何)。

我试图重新创建的行为就像您在Pinterest中单击图像时所看到的那样。覆盖是可滚动的(整个覆盖层向上移动,就像一页在一页的上面),但主体后面覆盖是固定的。

我试图用CSS (例如,在整个页面的顶部使用#EYZ0覆盖,并使用overflow: hidden覆盖正文)创建这个,但它不阻止div是可滚动的。

如何保持主体/页面从滚动,但保持滚动在全屏容器内?

1034423 次浏览

理论

看看pinterest网站的当前实现(未来可能会改变),当你打开覆盖,noscroll类应用于body元素(设置overflow: hidden)使body不再可滚动。

覆盖创建的动态或已经注入到页面中,并通过display: block - 没有区别可见,有position : fixedoverflow-y: scrolltopleftrightbottom属性设置为0:这种风格使覆盖填充整个视口(但现在我们是在2022年,所以你可以使用inset: 0代替)。

覆盖层中的div位于position: static中,因此垂直滚动条与该元素相关。这将产生一个可滚动但固定的覆盖。

当你关闭覆盖时,你必须隐藏它(使用display: none),你甚至可以通过javascript删除节点(或者只是里面的内容,这取决于你,但也取决于内容的性质)。

最后一步是删除应用于bodynoscroll类(这样overflow属性就会回到它之前的值)


代码

< a href = " https://codepen.io/fcalderan/pen/qBYyRgE?editors=0010" rel="nofollow noreferrer">代码示例 .

(它通过改变覆盖的aria-hidden属性来显示和隐藏它,并增加其可访问性)。

< p > # EYZ0
(打开按钮)< / em >

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

(覆盖和关闭按钮)

<section class="overlay" aria-hidden="true" tabindex="-1">
<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;
inset: 0; }


[aria-hidden="true"]  { display: none; }
[aria-hidden="false"] { display: block; }

# EYZ0 # EYZ1

var body = document.body,
overlay = document.querySelector('.overlay'),
overlayBtts = document.querySelectorAll('button[class$="overlay"]'),
openingBtt;
    

[].forEach.call(overlayBtts, function(btt) {


btt.addEventListener('click', function() {
     

/* Detect the button class name */
var overlayOpen = this.className === 'open-overlay';
     

/* storing a reference to the opening button */
if (overlayOpen) {
openingBtt = this;
}
     

/* 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;
     

/* forcing focus for Assistive technologies but note:
- if your modal has just a phrase and a button move the
focus on the button
- if your modal has a long text inside (e.g. a privacy
policy) move the focus on the first heading inside
the modal
- otherwise just focus the modal.


When you close the overlay restore the focus on the
button that opened the modal.
*/
if (overlayOpen) {
overlay.focus();
}
else {
openingBtt.focus();
openingBtt = null;
}


}, false);


});


/* detect Escape key when the overlay is open */
document.body.addEventListener('keyup', (ev) => {
if (ev.key === "Escape" && overlay.getAttribute('aria-hidden') === 'false') {
overlay.setAttribute('aria-hidden', 'true');
body.classList.toggle('noscroll', false);
openingBtt.focus();
openingBtt = null;
}
})

最后,这里有另一个例子,在这个例子中,覆盖通过CSS transition应用到opacity属性来打开一个渐隐效果。此外,还应用padding-right来避免滚动条消失时底层文本的回流。

< a href = " https://codepen.io/fcalderan/pen/mdLjRYW?editors=0110" rel="nofollow noreferrer">代码依赖示例(fade) .

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

如果你想在ios上防止过度滚动,你可以在你的.noscroll类中添加位置固定

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

一般来说,如果你想让父对象(在本例中是body)在子对象(在本例中是overlay)滚动时阻止它滚动,那么让子对象成为父对象的兄弟对象,以防止滚动事件冒泡到父对象。在父元素是body的情况下,这需要一个额外的包装元素:

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

参见使用浏览器的主滚动条滚动特定DIV内容查看它的工作情况。

值得注意的是,有时在body标签上添加"overflow:hidden"并不能完成这项工作。在这些情况下,您还必须将属性添加到html标记中。

html, body {
overflow: hidden;
}

我想补充之前的答案,因为我试图这样做,一些布局打破了我切换到身体的位置:固定。为了避免这种情况,我还必须将body的高度设置为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");
}

不要在body上使用overflow: hidden;。它会自动将所有内容滚动到顶部。也不需要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的这一部分将响应键盘。当覆盖层被“关闭”时也是一样,因为它只是将覆盖层移动到一边。否则对浏览器来说,这只是两个正常的# eyz0,它不知道为什么它应该关注其中的任何一个。

使用以下HTML:

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

然后用JavaScript拦截并停止滚动:

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

然后让事情恢复正常:

# EYZ0

我发现这个问题试图解决我在Ipad和Iphone上的页面问题-当我显示固定div为弹出图像时,身体正在滚动。

有些答案很好,但没有一个解决了我的问题。我找到了下面这篇由Christoffer Pettersson撰写的博文。解决方案提出了帮助问题,我与iOS设备,它帮助我的滚动背景问题。

< a href = " https://blog.christoffer。关于iOS Safari的橡皮筋滚动,我了解到的6件事

正如建议的那样,我包括博客文章的主要观点,以防链接过时。

为了禁止用户在“菜单打开”时滚动后台页面,可以通过应用一些JavaScript和CSS类来控制应该允许滚动或不允许滚动哪些元素。

基于这个Stackoverflow答案,你可以控制禁用滚动的元素不应该 当touchmove事件被触发时,执行默认的滚动操作。" < / p >
 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">

对于触摸设备,尝试在覆盖的包装中添加1px宽,101vh min-height的透明div。然后将-webkit-overflow-scrolling:touch; overflow-y: auto;添加到包装器。这将使移动safari误以为覆盖层是可滚动的,从而拦截来自主体的触摸事件。

这是一个示例页面。在移动safari上打开:http://www.originalfunction.com/overlay.html

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

使用下面的代码禁用和启用滚动条。

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

所选的答案是正确的,但有一些局限性:

  • 用你的手指用力“投掷”仍然会在背景中滚动<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; */
}

如果目的是禁用移动/触摸设备,那么最直接的方法是使用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>

(该示例在Stack Overflow上下文中不起作用。您需要在一个独立的页面中重新创建它。)

如果你想禁用#app容器的滚动,只需添加touch-action: none;

大多数解决方案都存在无法保留滚动位置的问题,所以我研究了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 */
/* Suggestion to put: overflow-y: hidden;
Disabled scrolling still makes a mess with its width. Hiding it does the trick. */
}


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

就我而言,这些解决方案都不适用于iPhone (iOS 11.0)。

唯一有效的修复工作在我的所有设备是这一个- ios-10-safari-prevent-scrolling-behind-a-fixed-overlay-and-maintain-scroll-position

overscroll-behavior css属性允许在到达内容的顶部/底部时覆盖浏览器默认的溢出滚动行为。

只需添加以下样式叠加:

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

< a href = " https://codepen。io/anon/pen/oEMmrm" rel="noreferrer"> code depen demo . io/anon/pen/oEMmrm" rel="noreferrer">

目前工作在Chrome, Firefox和IE(caniuse)

更多细节请查看谷歌开发者文章

试试这个

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

要防止的行为称为滚动链接。要禁用它,请设置

overscroll-behavior: contain;

在你的CSS覆盖。

如果你想停止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;
}

超文本标记语言

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


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

基本上,你不需要JS也能做到。

主要思想是添加html/body, height: 100%, overflow: auto。 在你的覆盖层中,你可以根据你的需求启用/禁用滚动

希望这能有所帮助!

你可以用一些“新”css和JQuery轻松做到这一点。

< p >最初:# EYZ0 使用JQuery,你可以在“overlay”和“body”之间动态切换。当在'body'时,使用

body {
position: static;
overflow: auto;
}

在“叠加”时使用

body {
position: sticky;
overflow: hidden;
}

JQuery for switch('body'->'overlay'):

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

JQuery for switch('overlay'->'body'):

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

一个React功能组件的解决方案是使用useEffect钩

下面是下面的代码示例(请注意useEffect定义):

import {useEffect, useRef} from "react";


export default function PopoverMenu({className, handleClose, children}) {
const selfRef = useRef(undefined);


useEffect(() => {
const isPopoverOpenned = selfRef.current?.style.display !== "none";
const focusedElement = document?.activeElement;
const scrollPosition = {x: window.scrollX, y: window.scrollY};
if (isPopoverOpenned) {
preventDocBodyScrolling();
} else {
restoreDocBodyScrolling();
}


function preventDocBodyScrolling() {
const width = document.body.clientWidth;
const hasVerticalScrollBar = (window.innerWidth > document.documentElement.clientWidth);
document.body.style.overflowX = "hidden";
document.body.style.overflowY = hasVerticalScrollBar ? "scroll" : "";
document.body.style.width = `${width}px`;
document.body.style.position = "fixed";


}


function restoreDocBodyScrolling() {
document.body.style.overflowX = "";
document.body.style.overflowY = "";
document.body.style.width = "";
document.body.style.position = "";
focusedElement?.focus();
window.scrollTo(scrollPosition.x, scrollPosition.y);
}




return () => {
restoreDocBodyScrolling(); // cleanup on unmount
};
}, []);


return (
<>
<div
className="backdrop"
onClick={() => handleClose && handleClose()}
/>
<div
className={`pop-over-menu${className ? (` ${className}`) : ""}`}
ref={selfRef}
>
<button
className="pop-over-menu--close-button" type="button"
onClick={() => handleClose && handleClose()}
>
X
</button>
{children}
</div>
</>
);
}

最初发表于另一个与Stackoverflow相关的问题:https://stackoverflow.com/a/69016517/14131330

如果有人正在寻找用于React函数组件的解决方案,你可以把它放在模态组件中:

 useEffect(() => {
document.body.style.overflowY = 'hidden';
return () =>{
document.body.style.overflowY = 'auto';
}
}, [])

body标签的简单内联样式:

<body style="position: sticky; overflow: hidden;">

CSS

.noScroll {
overflow: hidden;
}

Javascript

<script>
function toggleNav() {
document.body.classList.toggle("noScroll");
}
</script>


按钮

<button onclick="toggleNav()">
Toggle Nav
</button>