防止页面滚动但允许叠加层滚动

596

我一直在寻找一种“灯箱”类型的解决方案,可以实现此功能,但目前还没有找到(如果您知道任何信息,请提供建议)。

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

我尝试只使用CSS来创建它(例如,在整个页面和带有overflow:hidden的body上放置一个

覆盖层
),但无法防止
可滚动。

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


这不就是一个像其他数百个插件一样使用固定位置和纵向滚动溢出的“覆盖”插件吗? - ggzone
4
Pinterest的链接不太有用,因为其内容被登录墙限制了。 - 2540625
9
2017年更新:即使您登录Pinterest,您会发现OP所描述的覆盖效果已不再存在 - 相反,当您点击一张图片时,您只是导航到显示该图片大版本的普通页面。 - Annabel
一个相当不错的CodePen:https://codepen.io/anon/pen/oEMmrm - undefined
23个回答

7
所选答案是正确的,但有一些限制:
  • 使用手指进行超强烈的“flings”仍然会在后台滚动<body>
  • 通过点击模态中的<input>打开虚拟键盘将把所有未来的滚动转移到<body>

我无法解决第一个问题,但想阐明第二个问题。令人困惑的是,Bootstrap曾经记录了这个键盘问题,但他们声称已经修复了此问题,并引用http://output.jsbin.com/cacido/quiet作为修复示例。

事实上,在我的测试中,该示例在iOS上运行良好。然而,将其升级到最新版本的Bootstrap(v4)会使其崩溃。

为了找出它们之间的差异,我将测试案例简化,不再依赖于Bootstrap,http://codepen.io/WestonThayer/pen/bgZxBG

决定因素很奇怪。避免键盘问题似乎要求在包含模式的根<div>未设置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; */
}

4

我发现这个问题是为了解决我在iPad和iPhone上遇到的问题而提出的,当我显示固定的弹出图像时,页面会滚动。

一些答案很好,但是没有一个解决我的问题。我找到了Christoffer Pettersson的以下博客文章。他提出的解决方案帮助了我在iOS设备上遇到的问题,并解决了我的滚动背景问题。

iOS Safari橡皮筋滚动的六个要点

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

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

根据这个Stackoverflow答案,您可以控制带有disable-scrolling的元素,在触摸移动事件触发时不执行其默认滚动操作。"

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

然后将disable-scrolling类放在页面div上:

<div class="page disable-scrolling">

4

没错,这就是解决方法。只需要为每个可滚动的容器添加-webkit-overflow-scrolling:touch;以拦截触摸事件即可。 - Mati
我仍然能够在iPhone上滚动。 - Simone Zandara
@SeniorSimoneZandara 我不是。这个方法可以很好地防止背景滚动。 - godblessstrawberry

3

为body标签添加简单的内联样式:

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

2
如果意图是在移动设备/触摸设备上禁用它,则最直接的方法是使用 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;


2
如果在iOS上触摸操作真正起作用的话,那就好了。在这种情况下,就不需要将整个“应用程序”包装在单独的div中了。 - powerbuoy

1
使用以下 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");


1

我想补充之前的回答,因为我尝试这样做时,一些布局在将body设置为position:fixed后立即破裂。为了避免这种情况,我还必须将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");
}

0

试一下这个

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

0
一个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://dev59.com/JGkw5IYBdhLWcg3w9vJ-#69016517


虽然这个链接可能回答了问题,但最好在此处包含答案的基本部分并提供参考链接。如果链接页面更改,仅有链接的答案可能会失效。 - Flair
使用钩子的更简单的答案:https://dev59.com/1Gox5IYBdhLWcg3wVCxy#69910144 - Moe
@Flair:我按照要求包含了链接的答案。谢谢您的提醒。 - Alexandre Desroches

0

CSS

.noScroll {
    overflow: hidden;
}

Javascript

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

按钮

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

如果您有新的问题,请通过单击提问按钮来提出。如果它有助于提供上下文,请包含此问题的链接。- 来自审核 - Ethan

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接