防止滚动,但允许覆盖滚动
我一直在寻找一个“灯箱”types的解决scheme,允许这个,但还没有find一个(请,build议如果你知道任何)。
我试图重新创build的行为就像点击图片时在Pinterest上看到的一样。 覆盖层是可滚动的( 因为整个覆盖层像页面顶部一样向上移动 ),但覆盖层后面的主体是固定的。
我试图用CSS创build它(例如,在整个页面顶部的div
覆盖和具有overflow: hidden
主体 ),但是它不阻止div
被滚动。
如何保持身体/页面滚动,但保持在全屏容器内滚动?
理论
查看当前pinterest站点的实现(它可能在将来会改变),当你打开覆盖时, noscroll
类被应用到body
元素并且overflow: hidden
被设置,因此body
不再是可滚动的。
覆盖(即时创build或已经在页面内部,通过display: block
,没有区别) position : fixed
和overflow-y: scroll
, top
, left
, right
和bottom
属性设置为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();