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

我一直在寻找一个“灯箱”types的解决scheme,允许这个,但还没有find一个(请,build议如果你知道任何)。

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

我试图用CSS创build它(例如,在整个页面顶部的div覆盖和具有overflow: hidden主体 ),但是它不阻止div被滚动。

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

理论

查看当前pinterest站点的实现(它可能在将来会改变),当你打开覆盖时, noscroll类被应用到body元素并且overflow: hidden被设置,因此body不再是可滚动的。

覆盖(即时创build或已经在页面内部,通过display: block ,没有区别) position : fixedoverflow-y: scrolltopleftrightbottom属性设置为0 :这种风格使覆盖填充整个视口。

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

当你closures缩放,你可以隐藏覆盖(通过display: none ),然后你也可以完全通过JavaScript(或只是内部的内容,它取决于你如何注入它)删除它。

作为最后一步,你还必须删除noscroll类的body (所以溢出属性返回到它的初始值)


Codepen示例

(它通过改变叠加层的aria-hidden属性来工作,以显示和隐藏它并增加其可访问性)。

标记
(打开button)

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

(覆盖和closuresbutton)

 <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; top: 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上过度滚动,你可以添加固定到你的.noscroll类的位置

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

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

 html, body { overflow: hidden; } 

不要使用overflow: hidden;body 。 它会自动将所有内容滚动到顶部。 也不需要JavaScript。 利用overflow: auto; 。 该解决scheme甚至适用于移动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; } 

在这里看到演示和源代码。

大多数解决scheme都有问题,他们不保留滚动的位置,所以我看看Facebook如何做。 除了将下层内容设置为position: fixed之外,他们还会dynamic设置顶部以保留滚动位置:

 scrollPosition = window.pageYOffset; mainEl.style.top = -scrollPosition + 'px'; 

然后,当您再次移除叠加层时,您需要重置滚动位置:

 window.scrollTo(0, scrollPosition); 

我创build了一个小例子来演示这个解决scheme

 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; } 
  <main class="main-content"></main> <div class="overlay"> <div class="overlay-content"></div> </div> <button class="toggle">Overlay</button> 

一般来说,如果你想要一个父(在这种情况下)的身体来防止它滚动,当一个孩子(在这种情况下,覆盖)滚动,然后使该孩子的父母的兄弟,以防止滚动事件冒泡父母。 如果父母是身体,则需要额外的包装元素:

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

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

我发现这个问题试图解决我的问题,我的页面上Ipad和Iphone – 身体正在滚动,当我显示固定的div作为与图像popup。

有些答案是好的,但是没有一个解决了我的问题。 我find了Christoffer Pettersson的博客文章。 在那里提出的解决scheme,帮助我的iOS设备的问题,它帮助我的滚动背景问题。

我学习iOS Safari的橡皮筋滚动的六件事

正如有人build议我包括博客文章的主要观点,以防链接过期。

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

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

这是一个示例页面。 在移动Safari上打开: http : //www.originalfunction.com/overlay.html

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

我想添加到以前的答案,因为我试图做到这一点,一旦我切换到正确的位置,一些布局中断:固定。 为了避免这一点,我还必须将身体的高度设置为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");

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

  • 用手指超级硬“fl”仍然会在背景中滚动<body>
  • 通过敲击模式中的<input>来打开虚拟键盘,将把所有将来的卷轴指向<body>

对于第一个问题,我没有解决的办法,但是想澄清第二个问题。 令人困惑的是,Bootstrap用来logging键盘问题,但他们声称已经修复了,引用http://output.jsbin.com/cacido/quiet作为修复的例子。;

事实上,这个例子在我的testing中正常工作。 但是,将其升级到最新的Bootstrap(v4)将会破坏它。

为了弄清楚它们之间的区别,我减less了一个testing用例,不再依赖于Bootstrap, http://codepen.io/WestonThayer/pen/bgZxBG

决定因素是奇怪的。 避免键盘问题似乎要求在包含模态的根<div>设置background-color 并且模式的内容必须嵌套在可以设置background-color另一个<div>

要testing它,请在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; */ } 

如果意图是禁用移动/触摸设备,那么最直接的方法是使用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> 

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

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