2012-02-14 73 views
287

我一直在寻找一个“收藏夹”式的解决方案,让这一点,但还没有找到一个还没有(请,建议如果你知道的话)。防止身体滚动,但允许叠加滚动

我试图重新的行为就像在图像上点击时,你会在Pinterest看到的。叠加是可滚动的,但背后覆盖固定身体在整个覆盖像一个页面在页面的顶部上移)。

我试图只用CSS(即一个div覆盖在整个页面的顶部和身体overflow: hidden)创造这一点,但它并不妨碍div被滚动。

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

+0

不是这只是一个“覆盖”插件像hundrets那里使用固定溢出-y滚动位置? – ggzone 2012-02-14 16:17:07

+3

链接到Pinterest没有帮助,因为它的内容位于登录墙后面。 – jtheletter 2015-06-10 02:54:31

+1

** 2017更新:**即使您登录到Pinterest,您发现OP中描述的叠加效果不再存在 - 而是当您单击某个图像时,您只需导航到一个显示大图的普通页面版本的图像。 – Annabel 2017-11-23 00:48:44

回答

472

理论

纵观目前执行的Pinterest的网站(它可能在未来改变),当您打开一个noscroll类应用到body元素,overflow: hidden设置覆盖,从而body是不再滚动。

的覆盖(建立在即时或已经在页面内并通过display: block可见,这都没有区别)有position : fixedoverflow-y: scroll,与topleftrightbottom属性设置为0:这种风格品牌覆盖图填充整个视口。

叠加层内的div只是在position: static那么您看到的垂直滚动条与该元素有关。因此内容是可滚动的,但覆盖保持不变。

当你关闭缩放时,你会隐藏覆盖图(通过display: none),然后你也可以通过javascript(或者只是里面的内容,它取决于你如何注入它)完全删除它。

正如你也必须去除noscroll类的body最后一步(所以overflow属性回到初始值)


代码

Codepen Example

(它通过更改aria-hidden属性的覆盖,以显示和隐藏它,并增加其可访问性)。

标记
(开门按钮)

<button type="button" class="open-overlay">OPEN LAYER</button> 

(覆盖和关闭按钮)

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 Example (fade)

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

这是正确的答案。有人应该给它一个复选标记,因为它是正确的。 – 2012-02-14 21:11:18

+46

这是绝对正确的,但请注意,它在移动Safari中不起作用。背景将继续滚动,您的覆盖图将被修复。 – a10s 2012-04-27 17:33:58

+3

有没有人有iOS 5 Mobile Safari的解决方案? – Michael 2013-06-19 01:36:29

58

如果你想防止滚动反弹在iOS上,您可以添加位置固定为您.noscroll类

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

有了这个解决方案,由于位置固定,主体会自动滚动到您的内容的顶部。这可能会让您的用户感到不安。 – tzi 2014-07-02 12:21:46

+2

使用'position:fixed'的另一个问题是它正在调整我的主体。也许它与其他CSS冲突,但是'overflow:hidden'只是需要的所有 – Dex 2014-07-17 19:56:48

+2

当你通过输入字段选项卡时,这是突破焦点跳跃 – Atav32 2015-07-17 18:42:50

5

一般speakin g,如果你想要一个父(在这种情况下是正文)来防止当一个孩子(在这种情况下是覆盖)滚动时它滚动,那么使该孩子成为父代的兄弟,以防止滚动事件冒泡到家长。在父是身体的情况下,这需要一个额外的包装元素:

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

Scroll particular DIV contents with browser's main scrollbar看到它的工作。

+1

最好的解决方案,真正的“开箱即用”的思想,只是不太好措辞。 – 2016-05-04 06:01:22

26

值得注意的是,有时在body标签中添加“overflow:hidden”并不能完成这项工作。在这些情况下,您也必须将该属性添加到html标记中。

html, body { 
    overflow: hidden; 
} 
+0

对于iOS,您还需要设置'width:100%; ' – Gavin 2016-11-24 16:57:11

+0

这并没有解决在这个问题的许多其他解决方案中也出现的问题 - 如果窗口在任何地方滚动,但在页面顶部打开模式时,它将导致滚动到页面的顶部。我发现Philipp Mitterer的解决方案[这里](https://stackoverflow.com/a/45230674/1533191)是涵盖大多数情况的最佳选择。 – CLL 2018-02-17 21:58:24

1

我想添加到以前的答案,因为我想这样做,当我打开身体位置的一些布局,爆发在即:固定。为了避免这种情况,我不得不身体的高度也设置为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"); 
} 
26

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

观看演示here和源代码here

更新:

对于谁想要键盘空格键,页面上/下工作的人:你需要把重点放在覆盖,例如,点击它,或手动JS专注于这部分内容之前的div将响应键盘。与覆盖层“关闭”时相同,因为它只是将覆盖层移到侧面。否则,浏览器,这只是两个正常的div s,它不知道为什么它应该关注其中的任何一个。

+2

很酷的解决方案,但然后我需要将我的所有内容包裹在一个div中,而我并不打算这么做...... – 2015-09-03 00:07:38

+2

这是一个理想的解决方案。如果有人遇到这个问题,你可能需要添加'html,body {height:100%; '(如在演示中)这样才能正常工作。 – John 2015-09-18 16:07:38

+0

好主意,虽然你仍然在后面使用一个沉重的库(AngularJS)来使它工作,所以它不是真的完全回答这个问题,虽然它在行动中很酷。 – user18490 2015-09-23 21:26:09

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

+0

这是我唯一的解决方案。谢谢。 hvala :) – riogrande 2017-09-01 09:31:42

3

我发现这个问题试图解决我在Ipad上的页面问题而当我将固定div显示为弹出图像时,Iphone - body正在滚动。

有些答案很好,但是没有一个解决了我的问题。我发现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"> 
2

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

下面是一个示例页面。打开移动Safari浏览器:下面的代码http://www.originalfunction.com/overlay.html

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

+0

Jup,这个伎俩。只需为每个可滚动容器添加'-webkit-overflow-scrolling:touch;'来拦截触摸事件。 – Mati 2016-09-27 07:37:05

+0

我仍然可以滚动iPhone – 2017-02-24 14:21:31

+0

@SeniorSimoneZandara我不是。这工作正常,以防止背景滚动 – godblessstrawberry 2018-02-27 07:34:57

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

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

  • 超硬“甩”用你的手指仍然会滚动的背景
  • 通过点击一个<input>打开虚拟键盘<body>在模式将直接将所有未来的卷轴<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; */ 
} 
0

如果目的是取消对移动/触摸设备则最直接的方法做到这一点是利用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>

(。这个例子并不在堆栈溢出的情况下工作,你将需要重新创建它在一个单独的页面)

如果您想禁用滚动#app容器,只需添加touch-action: none;即可。

+0

如果触摸操作实际上在iOS上工作。在这种情况下,不需要将整个“应用程序”封装在单独的div中。 – powerbuoy 2017-12-05 18:57:35

10

大多数解决方案都存在这样的问题,即他们不保留滚动位置,所以我查看了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) { 
 
\t \t showOverlay(); 
 
    } else { 
 
    removeOverlay(); 
 
    } 
 
    overlayShown = !overlayShown; 
 
}); 
 

 
function showOverlay() { 
 
    scrollPosition = window.pageYOffset; 
 
    \t const mainEl = document.querySelector('.main-content'); 
 
    mainEl.style.top = -scrollPosition + 'px'; 
 
    \t document.body.classList.add('show-overlay'); 
 
} 
 

 
function removeOverlay() { 
 
\t \t document.body.classList.remove('show-overlay'); 
 
    \t 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; 
 
}
<main class="main-content"></main> 
 

 
    <div class="overlay"> 
 
    <div class="overlay-content"></div> 
 
    </div> 
 
    
 
    <button class="toggle">Overlay</button>

-1

由于这个问题已经很多年前问,我有一个更新的答案。编码器/程序员并不总是具有与元素相同的设置,这意味着某些答案对某些用户有效,而对其他用户则不适用。

这个答案,如果对于那些谁不能有自己的元素位置:固定 - 移动平台

- 短答案 -

document.body.preventDefault(); 事件 .preventDefault();

- 解释/长的答案 -

document.body.preventDefault(); event.preventDefault();之间的差异,这是非常重要的。当你阻止身体时,你明确地阻碍了身体上的滚动位置(这在后场很明显)。

如果您的主要元素需要滚动而身体不应该滚动,则需要检测触摸事件。不提供完整代码,但您也需要检测触摸位置。

document.addEventListener('touchmove', handleTouchMove, false); 

function handleTouchMove(evt) { 

} 

在你的handleTouchMove中,检测上下滑动。

if (yDiff > 0) { 
    /* UP SWIPE */ 
}else{ 
    /* DOWN SWIPE */ 
} 

你已经这样做了之后,发现如果需要滚动的元素是它的滚动的完整的顶部或底部。

var ExampleElement= document.getElementById('ExampleElement'); 
if(ExampleElement.scrollHeight - ExampleElement.scrollTop === 0){ 
    evt.preventDefault(); 
}else{ 
    document.body.preventDefault(); 
} 
return; 

对于本示例,以前列出的代码仅检测用户何时滚动到元素的顶部。当用户向下滑动时,应将此代码插入到检测中。据说这样,当他们到达顶端时,身体根本不会让你滚动身体;因为它一直在滚动ExampleElement,它从来没有滚动过正文。当用户向上滑动时添加极性反向以进行检测时,它也会对其反向执行相同的操作。

我很抱歉,但我不会发布完整的编码示例。

2

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

只需添加以下样式叠加:

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

Codepen demo

现任职于Chrome和Firefox(caniuse

有关详情,请google developers article

+1

我一路走来告诉你,这是最新的Chrome,Mozilla和Opera的工作。有一个美好的时光! – 2018-03-06 19:44:29