防止正文滚动,但允许覆盖滚动


443

我一直在寻找一种“灯箱”类型的解决方案,它允许这样做,但还没有找到(请提出建议,如果您知道的话)。

我尝试重新创建的行为就像您在单击图片时在Pinterest上看到的一样。叠加层是可滚动的(因为整个叠加层像页面顶部的页面一样向上移动),但是叠加层后面的主体是固定的。

我试图仅使用CSS(div在整个页面和主体上使用的覆盖层)overflow: hidden创建此标签,但它并不能阻止div其滚动。

如何防止正文/页面滚动但在全屏容器中继续滚动?


这不是一个像hundrets这样的“ overlay”插件吗?
ggzone 2012年

3
链接到Pinterest没有帮助,因为其内容位于登录墙后面。
2540625 2015年

4
2017年更新:即使您登录到Pinterest,也会发现OP中描述的叠加效果不再存在-而是当您单击图像时,您导航到显示该图像较大版本的普通页面。
Annabel

Answers:


622

理论

查看pinterest网站的当前实现(将来可能会更改),当您打开叠加层时,会将一个noscroll类应用于该body元素并overflow: hidden进行设置,因此该类body不再可滚动。

叠加(上即时或已经在页面内,并通过可见创建display: block,它使没有区别)有position : fixedoverflow-y: scroll,与topleftrightbottom属性设置为0:这种风格使得覆盖填充整个视口。

div覆盖里面,而不是仅仅在position: static然后就看到垂直滚动条是有关该元素。结果,内容是可滚动的,但是覆盖保持固定。

关闭缩放时,您可以隐藏叠加层(通过display: none),然后也可以通过javascript将其完全删除(或仅删除其中的内容,具体取决于注入方式)。

作为最后一步,您还必须将noscroll类删除到body(这样,溢出属性将返回其初始值)


Codepen示例

(它通过更改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);

});

最后,这是另一个示例,其中,通过将CSS transition应用于opacity属性,叠加层以淡入效果打开。padding-right当滚动条消失时,还应用a来避免基础文本上的重排。

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

6
这是正确的答案。正确的人应该给它打勾。
Scoota P 2012年

64
这是绝对正确的,但请注意,它不适用于移动Safari。背景将继续滚动,并且叠加层将被修复。
2012年

20
它工作正常。可滚动的视口容器div必须具有CSS样式position:fixed,并且具有垂直可滚动的溢出。我成功使用overflow-y:auto;了iOS的动量/惯性滚动,并将其添加-webkit-overflow-scrolling:touch;到CSS中。我使用display:block;width:100%;height:100%;CSS来拥有整页视口。
凌晨

10
对不起,我不明白。将主体设置为溢出:隐藏不会禁用iPad iOS7上的弹性主体滚动。所以我不得不在我的js中添加:document.body.addEventListener('touchmove',function(event){event.preventDefault();},false); 这也使所有元素也无法滚动。到目前为止尚未找到任何解决方案(没有额外的插件)
Garavani 2014年

21
如果激活覆盖层时用户已经将主体向下滚动到底部,则在设置溢出时将导致主体向上跳到顶部。可以解决吗?
比约恩·安德森

75

如果要防止在iOS上过度滚动,可以将固定位置添加到.noscroll类

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

106
使用此解决方案,由于位置固定,正文将自动滚动到内容的顶部。这可能会打扰您的用户。
tzi 2014年

5
使用的另一个问题position:fixed是它正在调整我的主体大小。也许它与其他CSS冲突,但overflow:hidden仅此而已
Dex 2014年

3
当您在输入字段中
切换

@ Atav32听起来像一个单独的问题,位置和溢出不应影响制表符的顺序。
am80l 2015年

@ am80l对不起我以前的评论是超级含糊:Tab键顺序找工作,但是屏幕的颠簸,当你透过iOS的Safari(字段标签stackoverflow.com/questions/31126330/...
Atav32

44

不要overflow: hidden;在上使用body。它会自动将所有内容滚动到顶部。也不需要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的,并且不知道为什么要重点关注其中任何一个。


5
很酷的解决方案,但是我需要将我所有的内容都包装在一个div中,我无意这样做……
Jacob Raccuia

2
这是一个理想的解决方案。如果有人对此有疑问,则可能需要添加html, body { height: 100%; }(如演示中所示)才能使其正常工作。
约翰

4
@ user18490角度/材质部分与该解决方案正在起作用的事实无关。
露西亚(Lucia)2015年

10
这会以多种方式破坏移动设备的UX(例如,URL栏隐藏,过度滚动负担等),甚至是空格键也可以在台式机上滚动
2015年

2
这是更可靠的选项,但确实会使事情复杂化。例如,PageUp和PageDown在刷新时将不起作用。使用该.offset()值进行计算的任何内容都会弄乱,等等……
BlackPanther

42

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

只需添加以下样式即可覆盖:

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

Codepen示范

目前可在Chrome,Firefox和IE(caniuse)中使用

有关更多详细信息,请参阅Google开发人员文章


4
我一直告诉您,这正在最新的Chrome,Mozilla和Opera上运行。有一个美好的时光!
Wannabe JavaGeek

3
有人应该为此加赏金。这是正确的解决方案。无需JavaScript。:)
joseph.l.hunsaker

9
该解决方案的问题在于它仅在叠加层可滚动时才有效。如果您有一个模态,并且所有模态都适合在屏幕上显示,那么里面没有滚动—它不会停止主体滚动。那而且它也根本无法在Safari中使用:)
waterplea

1
我将其与overflow-y: scroll(从您的Codepen演示中)结合使用。@pokrishka提出的观点是正确的,但就我而言,这不是问题。无论如何,我想知道将来浏览器的实现是否将涵盖这一细节。我发现,在Firefox中,调整浏览器的大小以使模式不适合屏幕,然后再次调整它的大小,以便即使将其调整为完整大小后也可以使此属性正常工作(即包含滚动)-直到重新加载页面, 至少。
Marc.2377

33

大多数解决方案都存在不能保持滚动位置的问题,因此我看了一下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 */
}

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


这似乎是我最优雅的解决方案。我知道Google使用相同的技巧来滚动页面和经常移动元素。
forallepsilon '18

这是对我而言100%正确的唯一解决方案,我希望我能早点找到这个答案。
user0103 '19

31

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

html, body {
    overflow: hidden;
}

对于iOS,您还需要设置width: 100%; height: 100%;
加文

2
这不能解决在此问题的许多其他解决方案中也看到的问题-如果在打开模式时窗口滚动到页面顶部以外的任何位置,则将导致滚动到页面顶部。我发现菲利普米特雷尔的解决方案在这里是覆盖大多数情况下的最佳选择。
CLL

1
我从未遇到过您提到的这个问题(通过网络或移动)。您能否共享在小提琴或jsbin中不起作用的完整代码(与DOM)?无论如何,对于使用Javascript解决方法来解决此问题,我都持谨慎态度。
弗朗西斯科·霍奇

7

一般而言,如果希望父级(在这种情况下为主体)在子级(在此情况下为叠加层)滚动时阻止其滚动,则应使子级成为父级的同级,以防止滚动事件冒泡。父母。如果父级是主体,则需要附加的包装元素:

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

请参阅使用浏览器的主滚动条滚动特定的DIV内容,以查看其工作原理。


1
最好的解决方案,真正的“开箱即用”的思维,只是措辞不够好。
标记

如果正文不是滚动元素,那么当您向下滚动到页面时,顶栏将不会在移动设备上向上滑动。
Seph Reed

7

选择的答案是正确的,但有一些限制:

  • 用手指超硬的“手指”仍会<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; */
}

4

对于触摸设备,请尝试101vh在覆盖层的包装中添加1px宽,最小高度的透明div。然后加-webkit-overflow-scrolling:touch; overflow-y: auto;到包装器中。这会诱使移动浏览器认为覆盖层是可滚动的,从而拦截了来自人体的触摸事件。

这是示例页面。在移动浏览器中打开:http//www.originalfunction.com/overlay.html

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


乔普,那成功了。只需-webkit-overflow-scrolling:touch;为每个可滚动容器添加以拦截触摸事件。
马蒂

我仍然可以在iPhone上滚动
Simone Zandara

@SeniorSimoneZandara我不是。这可以很好地防止背景滚动
godblessstrawberry

3

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

overscroll-behavior: contain;

在CSS中的叠加层上。


3

我发现此问题试图解决我在Ipad和Iphone上的页面遇到的问题-当我将固定的div显示为带有图像的弹出窗口时,主体正在滚动。

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

我从iOS Safari的橡皮筋滚动中学到的六件事

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

“为了禁止用户在“菜单打开”时滚动背景页面,可以通过应用一些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">

2

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

最初:body {... overflow:auto;} 使用JQuery,您可以在“覆盖”和“主体”之间动态切换。在“身体”上使用时

body {
   position: static;
   overflow: auto;
}

在“覆盖”时使用

body {
   position: sticky;
   overflow: hidden;
}

jQuery的switch('body'->'overlay'):

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

jQuery的switch('overlay'->'body'):

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

1

我想添加到以前的答案中,因为我试图这样做,并且当我将主体切换到position:fixed时,某些布局中断了。为了避免这种情况,我还必须将身体的高度设置为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");
}

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

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


1
如果触摸动作确实可以在iOS上正常工作。在这种情况下,无需将整个“应用”包装在单独的div中。
powerbuoy

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

如果要停止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%,溢出为auto。在叠加层内部,您可以根据需要启用/禁用滚动。

希望这可以帮助!



-2

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

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();
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.