iOS - css / js - 覆盖滚动但阻止身体滚动

时间:2016-06-08 22:25:43

标签: javascript html css

我知道有一些类似的问题,但它们要么对我的用例不起作用,要么接受的答案有一个对我不起作用的缺陷。所以......

我有一个包含元素列表的页面。单击列表中的元素将打开一个叠加层,其中包含有关该元素的详细信息。我需要覆盖层可滚动但我不希望覆盖层下面的其余部分滚动,这样一旦覆盖层关闭,你就处于相同的位置(覆盖层也略微透明,因此对用户来说很烦人)看到页面在下面滚动,也是为什么我无法保存滚动并重置关闭)。

现在除了iOS之外我到处都有工作。这基本上就是我所拥有的:

<html>
   <body>
      <ul id="list">
         <li>something 1</li>
         <li>something 2</li>
         <li>something 3</li>
         <li>something 4</li>
         <li>something 5</li>
      </ul>
      <div id="overlay"></div>
   </body>
</html>

CSS:

body.hidden {
   overflow: hidden;
}
#overlay {
   opacity: 0;
   top: -100vh;
}
#overlay.open {
   opacity: 1;
   overflow-y: scroll;
   overflow-x: hidden;
   top: 0;
}

然后在我的点击处理器中切换hidden上的body课程,open上的#overlay课程,并使用我的内容填充#overlay元素。就像我说的,除了iOS之外,它在任何地方都可以正常工作。

解决方案我见过其他地方说我需要在position:fixed和/或height:100%标签上使用bodyhtml。此解决方案的问题在于您丢失了滚动位置,当您关闭叠加层时,您将返回页面顶部。其中一些列表可能很长,所以这对我来说不是一个选择。

我无法阻止身体上的preventDefault完全滚动,因为我需要覆盖内容可滚动。

还有其他建议吗?

9 个答案:

答案 0 :(得分:15)

现在没办法解决这个问题。从iOS 9.3开始,仍然没有好办法阻止身体上的滚动。我目前在所有需要它的网站上实施的最佳方法是锁定html和正文的高度和溢出。

html, body {
  height: 100%;
  overflow: hidden;
}

这是阻止iOS滚动覆盖/模态背后内容的最佳方法。

然后为了保留滚动位置,我将内容向后移动,看​​起来像保留它,然后当模态关闭时恢复身体的位置。

我使用jQuery中的锁定和解锁功能

var $docEl = $('html, body'),
  $wrap = $('.content'),
  $.scrollTop;

$.lockBody = function() {
  if(window.pageYOffset) {
    scrollTop = window.pageYOffset;

    $wrap.css({
      top: - (scrollTop)
    });
  }

  $docEl.css({
    height: "100%",
    overflow: "hidden"
  });
}

$.unlockBody = function() {
  $docEl.css({
    height: "",
    overflow: ""
  });

  $wrap.css({
    top: ''
  });

  window.scrollTo(0, scrollTop);
  window.setTimeout(function () {
    scrollTop = null;
  }, 0);
}

当你将所有这些拼凑在一起时,如果你想在手机上测试它,那么你得到http://codepen.io/jerrylow/pen/yJeyoG只是结果:http://jerrylow.com/demo/ios-body-lock/

答案 1 :(得分:7)

为什么在滚动模态时页面会滚动?

如果您在模式后面的元素上启用了css属性-webkit-overflow-scrolling: touch;,则会出现一些本机代码,它们似乎会侦听我们无法捕获的touchmove事件。

那么现在呢?

我已经为我的应用程序修复了这个问题,当模态可见时,通过添加一个类来否定css属性。这是一个完全有效的例子。

let pageEl = document.querySelector(".page");
let modalEl = document.querySelector(".modal");

function openModal(e){
  e.preventDefault();
  pageEl.classList.add("page--has-modal");
  modalEl.classList.remove("hidden");
  window.addEventListener("wheel", preventScroll);
  window.addEventListener("touchmove", preventScroll);
}
function closeModal(e){
  e.preventDefault();
  pageEl.classList.remove("page--has-modal");
  modalEl.classList.add("hidden");
  
  window.removeEventListener("wheel", preventScroll);
  window.removeEventListener("touchmove", preventScroll);
}

window.addEventListener("click", function(){
  console.log(modalEl.scrollHeight);
  console.log(modalEl.clientHeight);
});

function preventScroll(e){
  if (!isDescendant(modalEl, e.target)){
    e.preventDefault();
    return false;
  }
  
  let modalTop = modalEl.scrollTop === 0;
  let modalBottom = modalEl.scrollTop === (modalEl.scrollHeight -      modalEl.clientHeight);
  
  if (modalTop && e.deltaY < 0){
    e.preventDefault();
  } else if (modalBottom && e.deltaY > 0){
    e.preventDefault();
  }
}

function isDescendant(parent, child) {
     var node = child.parentNode;
     while (node != null) {
         if (node == parent) {
             return true;
         }
         node = node.parentNode;
     }
     return false;
}
.page { 
  -webkit-overflow-scrolling: touch; 
}
.page--has-modal { 
  -webkit-overflow-scrolling: auto;  
}

.modal {
  position: absolute;
  top: 50px;
  left: 50px;
  right: 50px;
  bottom: 50px;
  background: #c0c0c0;
  padding: 50px;
  text-align: center;
  overflow: auto;
  -webkit-overflow-scrolling: auto; 
}
.hidden {
  display: none;
}
<div class="page">
<button onclick="openModal(event);">Open modal</button>
<p>Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Integer consequat sapien a lectus gravida euismod. Sed vitae nisl non odio viverra accumsan. Curabitur nisi neque, egestas sed, vulputate sit amet, luctus vitae, dolor. Cras lacus massa, sagittis ut, volutpat consequat, interdum a, nulla. Vivamus rhoncus molestie nulla. Ut porttitor turpis sit amet turpis. Nam suscipit, justo quis ullamcorper sagittis, mauris diam dictum elit, suscipit blandit ligula ante sit amet mauris. Integer id arcu. Aenean scelerisque. Sed a purus. Pellentesque nec nisl eget metus varius tempor. Curabitur tincidunt iaculis lectus. Aliquam molestie velit id urna. Suspendisse in ante ac nunc commodo placerat.</p>

<p>Morbi gravida posuere est. Fusce id augue. Sed facilisis, felis quis ornare consequat, neque risus faucibus dui, quis ullamcorper tellus lacus vitae felis. Phasellus ac dolor. Integer ante diam, consectetuer in, tempor vitae, volutpat in, enim. Integer diam felis, semper at, iaculis ut, suscipit quis, dolor. Vestibulum semper, velit et tincidunt vehicula, nisl risus eleifend ipsum, vel consectetuer enim dolor id magna. Praesent hendrerit urna ac lacus. Maecenas porttitor ipsum sed orci. In ac odio vel lorem tincidunt pellentesque. Nam tempor pulvinar turpis. Nunc in leo in libero ultricies interdum. Proin ut urna. Donec ultricies nunc dapibus justo. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Praesent vulputate, lectus pulvinar nonummy eleifend, sapien urna posuere metus, vel auctor risus odio eu augue. Cras vitae dolor. Phasellus dolor. Etiam enim. Donec erat felis, tincidunt quis, luctus in, faucibus at, est.</p>
<div class="modal hidden">
Hi there!
<button onclick="closeModal(event);">Close me</button>
<p>Morbi gravida posuere est. Fusce id augue. Sed facilisis, felis quis ornare consequat, neque risus faucibus dui, quis ullamcorper tellus lacus vitae felis. Phasellus ac dolor. Integer ante diam, consectetuer in, tempor vitae, volutpat in, enim. Integer diam felis, semper at, iaculis ut, suscipit quis, dolor. Vestibulum semper, velit et tincidunt vehicula, nisl risus eleifend ipsum, vel consectetuer enim dolor id magna. Praesent hendrerit urna ac lacus. Maecenas porttitor ipsum sed orci. In ac odio vel lorem tincidunt pellentesque. Nam tempor pulvinar turpis. Nunc in leo in libero ultricies interdum. Proin ut urna. Donec ultricies nunc dapibus justo. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Praesent vulputate, lectus pulvinar nonummy eleifend, sapien urna posuere metus, vel auctor risus odio eu augue. Cras vitae dolor. Phasellus dolor. Etiam enim. Donec erat felis, tincidunt quis, luctus in, faucibus at, est.</p>
</div>
</div>

答案 2 :(得分:6)

我们遇到了这个确切的问题-最终使用以下方法解决了该问题:

https://github.com/lazd/iNoBounce

脚本加载后立即出现一个陷阱,我们不得不调用iNoBounce.disable(),因为它已启动,因此阻止了任何滚动行为。

答案 3 :(得分:2)

我找到的最佳解决方案,当您使用vanilla javascript滚动到叠加层的顶部或末尾(固定元素)时,也会阻止背景滚动:

// "fixed-element" is the class of the overlay (fixed element) what has "position: fixed"
// Call disableScroll() and enableScroll() to toggle

var freeze = function(e) {
  if (!document.getElementsByClassName("fixed-element")[0].contains(e.target)) {
    e.preventDefault();
  }
}

var disableScroll = function() {
  document.body.style.overflow = "hidden"; // Or toggle using class: document.body.className += "overflow-hidden-class";

  // Only accept touchmove from fixed-element
  document.addEventListener('touchmove', freeze, false);

  // Prevent background scrolling
  document.getElementsByClassName("fixed-element")[0].addEventListener("touchmove", function(e) {
    var top = this.scrollTop,
      totalScroll = this.scrollHeight,
      currentScroll = top + this.offsetHeight;

    if (top === 0 && currentScroll === totalScroll) {
      e.preventDefault();
    } else if (top === 0) {
      this.scrollTop = 1;
    } else if (currentScroll === totalScroll) {
      this.scrollTop = top - 1;
    }
  });
}

var enableScroll = function() {
  document.removeEventListener("touchmove", freeze);
  document.body.style.overflow = "";
}

<强>优点:
1.打开覆盖(固定元素)时不会使主体“固定”,因此页面不会滚动到顶部 2.使用固定元素阻止背景滚动。

请参阅Gist

答案 4 :(得分:1)

似乎iOS只会在叠加达到最小或最大滚动时滚动身体。因此,将叠加层的scrollTop设置为1而不是零,并检测onscroll事件(在滚动结束后在iOS上触发)并且如果在max(app.scrollHeight - app.scrollTop - app.clientHeight&lt; 1)设置它缩短了一个像素。例如

    var overlay = document.getElementById('overlay');

    function onScroll() {
        if (overlay.scrollTop < 1) {
            overlay.scrollTop = 1;
        } else if (overlay.scrollHeight - overlay.scrollTop - overlay.clientHeight < 1)                         {
            overlay.scrollTop = overlay.scrollTop - 1;
        }
    }


    overlay.addEventListener('scroll', onScroll);

如果在iOS中运行,您可能需要添加检查并仅附加事件。

答案 5 :(得分:0)

我在寻找解决方案的同时发现了这个问题。也许我发现的解决方案在这里会有所启发。

在我的情况下,问题是在该容器中使用可滚动小部件时如何防止可滚动容器滚动(例如,使用css变换垂直旋转的HTML5滑块)。使用CSS中的ID选择器将可滚动容器定义为“ overflow-y:scroll”。

我首先尝试使用具有'overflow-y:hidden'的.scroll-lock类,然后在所有可滚动小部件上使用touchstart和touchend事件侦听器在可滚动容器上启用/禁用此功能。没用尝试使用小部件会导致容器滚动。然后,我尝试了所有形式的javascript解决方案,直到在这里找到了,然后更接近解决了。

就我而言,问题是特异性规则。类选择器被ID选择器所压倒,因此我切换的类无法覆盖ID所应用的容器上的默认溢出设置。当我直接在容器div上使用style属性应用“ overflow:hidden”时,一切正常。

const $scrollableWidget = $('.vertical-slider-container');
$scrollableWidget.on('touchstart',function (e) {
    console.log("Disabling scroll on content area");
    $(`#content`).css("overflow-y", "hidden");
});
$scrollableWidget.on('touchend',function (e) {
    console.log("Re-enabling scroll on content area");
    $(`#content`).removeAttr("style");
});

有关详细信息,请参见此小提琴。 (使用移动浏览器进行尝试。)

https://jsfiddle.net/daffinm/fwgnm7hs/12/

希望这对某人有帮助。 (我花了太长时间解决了这个问题。)

答案 6 :(得分:0)

我有类似的问题。打开模式窗口时-叠加层可在iOS设备上滚动。

玩完css和touch事件后,我决定下一步(仅在iOS设备上):

  • 打开模式之前-将滚动位置保存到变量并固定主体
  • 关闭模态后-使主体不固定并恢复滚动位置

答案 7 :(得分:0)

有一个简单的解决方案。这个对我有用。您可以尝试。

$(document).ready(function(){
    if( navigator.userAgent.match(/iPhone|iPad|iPod/i) ) {
        function preventBehavior(e) {
            e.preventDefault(); 
        };

        $(document).on('show.bs.modal', () => {
            document.addEventListener("touchmove", preventBehavior, {passive: false});

        }).on('hidden.bs.modal', () => {
            document.removeEventListener("touchmove", preventBehavior, {passive: true});
        });
    }
});

答案 8 :(得分:0)

除了将位置设置为固定和顶部为 0...如果您在标题中使用视口元代码(见下文),请确保将三个比例变量设置为 1.0:

<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0, user-scalable=no, target-densityDpi=device-dpi" />