我一直在寻找一个“灯箱”类型的解决方案,允许这一点,但还没有找到一个(请建议,如果你知道任何)。
我试图重现的行为就像你在Pinterest上点击图片时看到的一样。覆盖层是可滚动的(整个覆盖层向上移动,就像一页在一页的顶部),但覆盖层后面的主体是固定的。
我试图用CSS创建这个(即一个div覆盖在整个页面和身体溢出:隐藏),但它不阻止div是可滚动的。
如何保持主体/页面从滚动,但保持滚动在全屏容器内?
我一直在寻找一个“灯箱”类型的解决方案,允许这一点,但还没有找到一个(请建议,如果你知道任何)。
我试图重现的行为就像你在Pinterest上点击图片时看到的一样。覆盖层是可滚动的(整个覆盖层向上移动,就像一页在一页的顶部),但覆盖层后面的主体是固定的。
我试图用CSS创建这个(即一个div覆盖在整个页面和身体溢出:隐藏),但它不阻止div是可滚动的。
如何保持主体/页面从滚动,但保持滚动在全屏容器内?
当前回答
要防止的行为称为滚动链接。要禁用它,请设置
overscroll-behavior: contain;
在你的CSS覆盖。
其他回答
如果意图是禁用移动/触摸设备,那么最直接的方法是使用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;。
理论
看看pinterest网站的当前实现(未来可能会改变),当你打开覆盖时,一个noscroll类被应用到body元素(设置溢出:隐藏),使body不再可滚动。
动态创建的覆盖或已经注入到页面中并通过display: block可见-这没有什么区别-具有position: fixed和overflow-y: scroll,顶部,左侧,右侧和底部属性设置为0:这种风格使覆盖填充整个视口(但现在我们是在2022年,所以你可以使用inset: 0代替)。
覆盖层内的div位于position: static,因此垂直滚动条与该元素相关。这将产生一个可滚动但固定的覆盖。
当你关闭覆盖时,你必须隐藏它(使用display: none),你甚至可以通过javascript删除节点(或者只是里面的内容,这取决于你,但也取决于内容的性质)。
最后一步是删除应用于主体的noscroll类(这样溢出属性就会回到它之前的值)
Code
Codepen例子
(它通过改变overlay的aria-hidden属性来显示和隐藏它,并增加其可访问性)。
标记 (打开按钮)
<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; }
Javascript (vanilla-JS)
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转换以淡入效果打开。此外,还应用右填充来避免滚动条消失时底层文本的回流。
代码示例(淡出)
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;
}
CSS
.noScroll {
overflow: hidden;
}
Javascript
<script>
function toggleNav() {
document.body.classList.toggle("noScroll");
}
</script>
按钮
<button onclick="toggleNav()">
Toggle Nav
</button>
如果你想在ios上防止过度滚动,你可以在你的.noscroll类中添加位置固定
body.noscroll{
position:fixed;
overflow:hidden;
}
如果有人正在寻找React函数组件的解决方案,你可以把它放在模态组件中:
useEffect(() => {
document.body.style.overflowY = 'hidden';
return () =>{
document.body.style.overflowY = 'auto';
}
}, [])