我一直在寻找一种“灯箱”类型的解决方案,它允许这样做,但还没有找到(如果你知道的话,请提出建议)。
我试图重现的行为就像您在 Pinterest 中点击图像时看到的一样。叠加层是可滚动的(整个叠加层像页面顶部的页面一样向上移动),但叠加层后面的主体是固定的。
我尝试仅使用 CSS(即 div
覆盖在整个页面顶部和带有 overflow: hidden
的正文)来创建它,但它不会阻止 div
可滚动。
如何保持正文/页面不滚动但继续在全屏容器内滚动?
理论
查看 pinterest 网站的当前实现(将来可能会更改),当您打开叠加层时,noscroll
类将应用于 body
元素并设置 overflow: hidden
,因此 body
不再可滚动.
叠加层(即时创建或已在页面内创建并通过 display: block
显示,没有区别)具有 position : fixed
和 overflow-y: scroll
,以及 top
、left
、right
和 { 7} 属性设置为 0
:此样式使叠加层填充整个视口。
叠加层内的 div
只是在 position: static
中,然后您看到的垂直滚动条与该元素相关。结果,内容是可滚动的,但覆盖仍然是固定的。
当您关闭缩放时,您会隐藏叠加层(通过 display: none
),然后您也可以通过 javascript 将其完全删除(或者只是其中的内容,由您决定如何注入它)。
作为最后一步,您还必须将 noscroll
类删除到 body
(因此溢出属性返回到其初始值)
代码
代码笔示例
(它通过更改叠加层的 aria-hidden
属性来显示和隐藏它并增加其可访问性)。
标记(打开按钮)
<button type="button" class="open-overlay">OPEN LAYER</button>
(覆盖和关闭按钮)
<section class="overlay" aria-hidden="true">
<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;
top: 0; right: 0; bottom: 0; left: 0; }
[aria-hidden="true"] { display: none; }
[aria-hidden="false"] { display: block; }
Javascript(原版 JS)
var body = document.body,
overlay = document.querySelector('.overlay'),
overlayBtts = document.querySelectorAll('button[class$="overlay"]');
[].forEach.call(overlayBtts, function(btt) {
btt.addEventListener('click', function() {
/* Detect the button class name */
var overlayOpen = this.className === 'open-overlay';
/* 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;
}, false);
});
最后,这是另一个示例,其中通过应用于 opacity
属性的 CSS transition
以淡入效果打开叠加层。当滚动条消失时,还应用 padding-right
以避免底层文本重排。
Codepen 示例(淡入淡出)
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;
top: 0; left: 0; right: 0; bottom: 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 上过度滚动,可以将 position fixed 添加到 .noscroll 类
body.noscroll{
position:fixed;
overflow:hidden;
}
position:fixed
的另一个问题是它正在调整我的主体大小。也许它与其他 CSS 冲突,但只需要 overflow:hidden
overscroll-behavior
css 属性允许在到达内容的顶部/底部时覆盖浏览器的默认溢出滚动行为。
只需将以下样式添加到叠加层:
.overlay {
overscroll-behavior: contain;
...
}
Codepen 演示
目前适用于 Chrome、Firefox 和 IE(caniuse)
有关更多详细信息,请查看 google developers article。
overflow-y: scroll
结合使用(来自您的 Codepen 演示)。 @pokrishka 提出的观点是有效的,但就我而言,这不是问题。无论如何,我想知道浏览器的实现是否会在未来涵盖这个细节。我发现,在 Firefox 中,调整浏览器的大小以使模式不适合屏幕,然后再次调整它的大小,以便即使在调整为全尺寸后它确实使该属性工作(即包含滚动) - 直到重新加载页面, 至少。
不要在 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
的这一部分将响应键盘。与“关闭”叠加层时相同,因为它只是将叠加层移到一边。否则对于浏览器来说,这只是两个正常的div
,它不知道为什么要关注其中任何一个。
html, body { height: 100%; }
(如演示中所示)以使其正常工作。
.offset()
值进行计算的东西都会搞砸,等等......
大多数解决方案都有不保留滚动位置的问题,所以我看看 Facebook 是如何做到的。除了将底层内容设置为 position: fixed
之外,它们还动态设置顶部以保留滚动位置:
scrollPosition = window.pageYOffset;
mainEl.style.top = -scrollPosition + 'px';
然后,当您再次删除覆盖时,您需要重置滚动位置:
window.scrollTo(0, scrollPosition);
我创建了一个小例子来演示这个解决方案
让overlayShown = false;让滚动位置 = 0; document.querySelector('.toggle').addEventListener('click', function() { if (!overlayShown) { showOverlay(); } else { removeOverlay(); } overlayShown = !overlayShown; });函数 showOverlay() { scrollPosition = window.pageYOffset; const mainEl = document.querySelector('.main-content'); mainEl.style.top = -scrollPosition + 'px'; document.body.classList.add('show-overlay'); } 功能 removeOverlay() { document.body.classList.remove('show-overlay'); window.scrollTo(0, scrollPosition); const mainEl = document.querySelector('.main-content'); mainEl.style.top = 0; } .main-content { 背景图像:重复线性渐变(石灰,蓝色 103 像素);宽度:100%;高度:200vh; } .show-overlay .main-content { 位置:固定;左:0;右:0;溢出-y:滚动; /* 渲染禁用的滚动条以保持相同的宽度 */ /* 建议放置:overflow-y: hidden;禁用滚动仍然会弄乱它的宽度。隐藏它就可以了。 */ } .overlay { 显示:无;位置:固定;顶部:0;左:0;宽度:100%;高度:100%;背景:rgba(0, 0, 0, 0.3);溢出:自动; } .show-overlay .overlay { 显示:块; } .overlay-content { 边距:50px;背景图像:重复线性渐变(灰色,灰色20px,黑色20px,黑色40px);高度:120vh; } .toggle { 位置:固定;顶部:5px;左:15px;填充:10px;背景:红色; } /* 重置 CSS */ body { margin: 0; }
值得注意的是,有时将“overflow:hidden”添加到正文标签并不能完成这项工作。在这些情况下,您还必须将属性添加到 html 标记。
html, body {
overflow: hidden;
}
width: 100%; height: 100%;
您可以使用一些“新”css 和 JQuery 轻松完成此操作。
最初:body {... overflow:auto;}
使用 JQuery,您可以在“覆盖”和“正文”之间动态切换。在“身体”上时,使用
body {
position: static;
overflow: auto;
}
在“覆盖”时使用
body {
position: sticky;
overflow: hidden;
}
用于开关的 JQuery('body'->'overlay'):
$("body").css({"position": "sticky", "overflow": "hidden"});
用于开关的 JQuery('overlay'->'body'):
$("body").css({"position": "static", "overflow": "auto"});
一般来说,如果您希望父级(本例中为主体)在子级(本例中为叠加层)滚动时阻止其滚动,则使子级成为父级的兄弟姐妹以防止滚动事件冒泡到父母。如果父对象是主体,则需要一个额外的包装元素:
<div id="content">
</div>
<div id="overlay">
</div>
请参阅 Scroll particular DIV contents with browser's main scrollbar 以了解其工作情况。
选择的答案是正确的,但有一些限制:
用手指超硬“甩动”仍会在后台滚动
通过点击模态中的 打开虚拟键盘会将所有未来滚动到
我没有解决第一个问题,但想阐明第二个问题。令人困惑的是,Bootstrap 曾经记录过键盘问题,但他们声称已修复,并引用 http://output.jsbin.com/cacido/quiet 作为修复示例。
事实上,这个例子在我的测试中在 iOS 上运行良好。但是,将其升级到最新的 Bootstrap (v4) 会破坏它。
为了弄清楚它们之间的区别是什么,我减少了一个测试用例,使其不再依赖于 Bootstrap,http://codepen.io/WestonThayer/pen/bgZxBG。
决定因素很奇怪。避免键盘问题似乎需要在包含模态 的根 <div>
上设置 background-color
不,并且模态的内容必须嵌套在另一个 <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; */
}
您要防止的行为称为滚动链接。要禁用它,请设置
overscroll-behavior: contain;
在 CSS 中的叠加层上。
如果有人正在寻找 React 函数组件的解决方案,您可以将其放在 modal 组件中:
useEffect(() => {
document.body.style.overflowY = 'hidden';
return () =>{
document.body.style.overflowY = 'auto';
}
}, [])
对于触摸设备,请尝试在叠加层的包装器中添加一个 1 像素宽、101vh
最小高度的透明 div。然后将 -webkit-overflow-scrolling:touch; overflow-y: auto;
添加到包装器中。这使移动 Safari 认为覆盖是可滚动的,从而拦截来自身体的触摸事件。
这是一个示例页面。在移动 Safari 上打开:http://www.originalfunction.com/overlay.html
https://gist.github.com/YarGnawh/90e0647f21b5fa78d2f678909673507f
-webkit-overflow-scrolling:touch;
即可拦截触摸事件。
我发现这个问题试图解决我在 Ipad 和 Iphone 上的页面遇到的问题 - 当我将固定 div 显示为带有图像的弹出窗口时,正文正在滚动。
有些答案很好,但是没有一个能解决我的问题。我发现了 Christoffer Pettersson 的以下博客文章。那里提出的解决方案帮助了我在 iOS 设备上遇到的问题,它帮助了我的滚动背景问题。
Six things I learnt about iOS Safari's rubber band scrolling
正如建议的那样,我会在博客文章中包含要点,以防链接过时。
“为了禁止用户在“菜单打开”时滚动背景页面,可以通过应用一些 JavaScript 和 CSS 类来控制哪些元素应该被允许滚动。
根据这个 Stackoverflow 答案,您可以控制具有禁用滚动功能的元素在触发 touchmove 事件时不应执行其默认滚动操作。”
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">
body 标签的简单内联样式:
<body style="position: sticky; overflow: hidden;">
如果意图在移动/触摸设备上禁用,那么最直接的方法是使用 touch-action: none;
。
例子:
const app = document.getElementById('app');常量覆盖 = document.getElementById('overlay');让身体 = ''; for (let index = 0; index < 500; index++) { body += index + '
'; } app.innerHTML = 正文; app.scrollTop = 200; overlay.innerHTML = 正文; * {边距:0;填充:0; } html, 身体 { 高度: 100%; } #app { 背景:#f00;位置:绝对;高度:100%;宽度:100%;溢出-y:滚动;行高:20px; } #overlay { 背景:rgba(0,0,0,.5);位置:固定;顶部:0;左:0;右:0;高度:100%;填充:0 0 0 100px;溢出:滚动; }
(该示例在 Stack Overflow 的上下文中不起作用。您需要在独立页面中重新创建它。)
如果您想禁用 #app
容器的滚动,只需添加 touch-action: none;
。
我想添加到以前的答案,因为我试图这样做,一旦我将主体切换到位置:固定,一些布局就中断了。为了避免这种情况,我还必须将 body's height 设置为 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");
}
使用以下 HTML:
<body>
<div class="page">Page content here</div>
<div class="overlay"></div>
</body>
然后 JavaScript 拦截并停止滚动:
$(".page").on("touchmove", function(event) {
event.preventDefault()
});
然后让事情恢复正常:
$(".page").off("touchmove");
就我而言,这些解决方案都没有在 iPhone (iOS 11.0) 上运行。
在我的所有设备上运行的唯一有效修复是这个 - ios-10-safari-prevent-scrolling-behind-a-fixed-overlay-and-maintain-scroll-position
尝试这个
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", "");
});
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
CSS
.noScroll {
overflow: hidden;
}
Javascript
<script>
function toggleNav() {
document.body.classList.toggle("noScroll");
}
</script>
按钮
<button onclick="toggleNav()">
Toggle Nav
</button>
如果你想停止 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;
}
HTML
<div class="overlay">
<div class="overlay-content"></div>
</div>
<div class="background-content">
lengthy content here
</div>
基本上,你可以在没有 JS 的情况下做到这一点。
主要思想是添加 html/body 高度:100% 和溢出:自动。在您的叠加层内,您可以根据您的要求启用/禁用滚动。
希望这可以帮助!
使用下面的代码来禁用和启用滚动条。
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();
不定期副业成功案例分享
div
必须具有 CSS 样式position:fixed
,并且具有垂直溢出可滚动。我成功使用了overflow-y:auto;
,对于 iOS 动量/惯性滚动,我将-webkit-overflow-scrolling:touch;
添加到 CSS。我使用display:block;
、width:100%;
和height:100%;
CSS 来获得整页视口。