我一直在寻找一种“灯箱”类型的解决方案,可以实现此功能,但目前还没有找到(如果您知道任何信息,请提供建议)。
我试图重新创建的行为就像您在Pinterest上点击图像时看到的那样。该覆盖层是可滚动的(就像页面上一页上移一样整个覆盖层向上移动),但是覆盖层后面的主体是固定的。
我尝试只使用CSS来创建它(例如,在整个页面和带有overflow:hidden
的body上放置一个
如何保持主体/页面不滚动但在全屏容器内滚动?
我一直在寻找一种“灯箱”类型的解决方案,可以实现此功能,但目前还没有找到(如果您知道任何信息,请提供建议)。
我试图重新创建的行为就像您在Pinterest上点击图像时看到的那样。该覆盖层是可滚动的(就像页面上一页上移一样整个覆盖层向上移动),但是覆盖层后面的主体是固定的。
我尝试只使用CSS来创建它(例如,在整个页面和带有overflow:hidden
的body上放置一个
如何保持主体/页面不滚动但在全屏容器内滚动?
理论
查看当前Pinterest网站的实现(它可能会在将来更改),当您打开叠加层时,noscroll
类被应用于body
元素(设置overflow: hidden
),使得body
不再可滚动。
在页面中创建或已注入并通过display: block
显示的叠加层 - 没有区别 - 具有position: fixed
和overflow-y: scroll
,并将top
、left
、right
和bottom
属性设置为0
:这种样式使得叠加层填充整个视口(但现在是2022年,因此您可以使用inset: 0
代替)。
叠加层内部的div
处于position: static
状态,因此垂直滚动条与该元素相关。这导致了一个可滚动但固定的叠加层。
当您关闭叠加层时,必须隐藏它(使用display: none
),甚至可以通过javascript删除节点(或仅删除其中的内容,这取决于内容的性质)。
最后一步是还原应用于body
的noscroll
类(使overflow
属性恢复到先前的值)
代码
(它通过更改叠加层的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;
}
})
最后,这里有另一个示例,其中覆盖层通过应用于opacity
属性的CSS transition
实现淡入效果。此外,当滚动条消失时,还应用了padding-right
以避免底部文本的重新布局。
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;
}
div
必须有CSS样式position:fixed
,并具有垂直溢出滚动。我成功使用了overflow-y:auto;
,而对于iOS的惯性滚动,我在CSS中添加了-webkit-overflow-scrolling:touch;
。我使用了display:block;
,width:100%;
和height:100%;
CSS来实现全屏视窗。 - Slinkoverscroll-behavior
CSS 属性允许在滚动到内容顶部/底部时覆盖浏览器默认的溢出滚动行为。
只需将以下样式添加到叠加层即可:
.overlay {
overscroll-behavior: contain;
...
}
当前支持Chrome、Firefox和IE浏览器(具体兼容性请查看caniuse网站)
更多详细信息请参考Google开发者的文章。
overflow-y: scroll
的组合(来自你的 Codepen 演示)。@pokrishka 提出的观点是正确的,但在我的情况下不是问题。无论如何,我想知道浏览器的实现是否会在未来涵盖这个细节。我发现,在 Firefox 中,调整浏览器大小以使模态框不适合屏幕,然后再次调整大小以使其适合屏幕可以使此属性起作用(即滚动被包含),即使调整为全屏大小 - 至少在重新加载页面之前是这样的。 - Marc.2377如果您想在iOS上防止页面过度滚动,可以将position fixed添加到您的.noscroll类中。
body.noscroll{
position:fixed;
overflow:hidden;
}
position:fixed
的另一个问题是它会调整我的主体大小。也许与其他CSS冲突了,但只需要使用overflow:hidden
即可解决。 - Dex大多数解决方案存在一个问题,即它们无法保留滚动位置,因此我研究了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>
不要在 body
上使用 overflow: hidden;
。它会自动将所有内容滚动到顶部。也不需要使用 JavaScript。可以使用 overflow: auto;
。这种解决方案甚至适用于移动版 Safari。
<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
元素,它不会知道为什么应该关注其中一个。
html,body {height:100%;}
(如演示中所示)才能正常工作。 - John.offset()
值进行计算的内容都会出现问题。 - BlackPantherhtml, body {
overflow: hidden;
}
width: 100%; height: 100%;
。 - Gavin您想要防止的行为称为滚动链接。要禁用它,请设置
overscroll-behavior: contain;
在CSS中对您的覆盖图层进行样式处理。
如果有人在寻找一个React函数组件的解决方案,你可以将以下内容放在模态框组件中:
useEffect(() => {
document.body.style.overflowY = 'hidden';
return () =>{
document.body.style.overflowY = 'auto';
}
}, [])
body {
position: static;
overflow: auto;
}
当使用“覆盖层”时
body {
position: sticky;
overflow: hidden;
}
针对切换('body'到'overlay')的JQuery:
$("body").css({"position": "sticky", "overflow": "hidden"});
将JQuery用于开关('overlay' -> 'body'):
$("body").css({"position": "static", "overflow": "auto"});
通常来说,如果你想要一个父元素(在这个例子中是body)在一个子元素(在这个例子中是覆盖层overlay)滚动时阻止它自己滚动,那么就需要把子元素变成父元素的兄弟元素,以防止滚动事件冒泡到父元素。如果父元素是body,那么需要添加一个额外的包装元素:
<div id="content">
</div>
<div id="overlay">
</div>
请查看 使用浏览器主滚动条滚动特定 DIV 内容 以查看其工作方式。