在不禁用滚动功能的情况下防止iOS反弹

时间:2015-04-27 11:54:31

标签: javascript jquery html ios css

当网页内容大于视口时,我正在尝试实施一种解决方案,以防止iOS for Safari中的iOS反弹效果。

我正在处理的页面在结构上非常具体,与此页面非常相似http://new.salt.ch/

  • 基本结构是基于bootstrap。
  • 顶部有一个固定的导航栏。
  • 它有一个全屏背景幻灯片。
  • 幻灯片显示有一个固定在视口底部的叠加层。
  • 有一个页脚元素可以在画布外加载,只有在滚动内容时才可见。
  • 内容滚动在导航栏后面。
  • 内容包含一个位于导航栏下方20px的标题和一系列位于视口上方20px的按钮。
  • 滚动时,按钮和标题都会向上移动以显示页脚。

我遇到的问题与页面http://new.salt.ch/上的问题相同,因为当您向上滚动时,屏幕底部会出现一个反弹效果,并显示背景和叠加效果。

我尝试了各种解决方案,包括iNoBounce.js,Nonbounce.js以及我在SO上找到的其他一些建议。

我总是遇到同样的问题...当我尝试禁用反弹时,所有滚动都会被禁用。我猜这是因为内容(除了页脚)总是足够大,不需要滚动,因此滚动被禁用,滚动时不再可以访问页脚。

11 个答案:

答案 0 :(得分:16)

如果我正确地解释您的问题,我们多年来一直在开发跨平台移动网络应用程序时遇到同样的问题,试图让所有不同的专有滚动功能在每台设备上正常运行:Apple iOS,Google Android,Windows Phone,笔记本电脑Chrome,笔记本电脑Safari,IE和笔记本电脑Edge。

jQuery Mobile继续尝试在他们的框架范围内修复这个问题,但是由于每个设备制造商/操作系统制造商的不断更新,这个问题太多了。

是的,我们为每个移动设备提供了解决方案。我们已经测试过,但没有认真考虑为每个设备开发设备选择性分页框架,要求我们检测每个设备并为每个设备提供略微不同的框架。基本上维护至少3个以及最多十几个不同版本的代码,这是一个非常糟糕的主意。

解决方案:只需将持久性页眉和页脚放在页面框架之上,我们就能获得最大的收益。以下是使用内联样式的简单通用解决方案:

<html>
<head>
  <title>Fixed Header and Footer on All Mobile Web Apps</title>
  <meta name="viewport" content="user-scalable=no, initial-scale=1.0, maximum-scale=1.0" />
  <style>
    html, body { height:100%; width:100%; }
  </style>
</head>
<body>
<div style="position: fixed; height:100%; width:100%; top:0; left:0;">
  <div style="height:calc(100% - 1px); width:100%; margin-top: 60px; z-index: 1; overflow-y: scroll; -webkit-overflow-scrolling: touch;">
    [Body Content That Can Scroll if it extends beyond screen...]

  </div>
  <div style="position: absolute; top:0; left:0; width:100%; height: 60px; background:#dddddd; z-index:10;">
    [Header Content...]

  </div>
  <div style="position: absolute; bottom:0; left:0; width:100%; height: 40px; background:#cccccc; z-index:11;">
    [Footer Content...]

  </div>
</div>
</body>
</html>

因此,Body可以是任何jQuery Mobile页面集。事实上,从理论上讲,Body几乎可以是任何框架中的任何内容。

特别说明,高度线:calc(100% - 1px);对魔术至关重要。

这个问题看似无限的组合或排列几乎已成为我们多年来的一次讨伐,试图找到最纯粹,最简单,最普遍兼容的解决方案。因此,在为这个主题投入了令人尴尬的工时后,这不仅是我们最好的解决方案,它也是我们发现的唯一普遍兼容的方法,也允许您坚持只有一个单一的代码库。它已经在最新版本的iOS,Windows Phone,Android,笔记本电脑Chrome,笔记本电脑Safari,PhoneGap,笔记本电脑Firefox,IE 9-11和Windows Edge上成功测试过。

标签:移动应用,网络应用,固定标题,固定页脚,持久标题,持久页脚,滚动问题,iOS滚动弹跳,Chrome滚动弹跳,Android滚动弹跳,webkit滚动问题,webkit触摸滚动​​,iOS触摸滚动问题

答案 1 :(得分:15)

此代码应停止反弹,因为它是反弹的HTML标记

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

答案 2 :(得分:13)

我在SO上经历了几个答案,事情看起来很惨淡,直到找到了这段代码。

html {
  position: fixed;
  height: 100%;
  overflow: hidden;
}

body {
  width: 100vw;
  height: 100vh;
  overflow-y: scroll;
  overflow-x: hidden;
  -webkit-overflow-scrolling: touch;
}

body的样式声明可以放在您想要滚动的任何元素上。您还可以根据需要更改overflow-xoverflow-y。我个人需要它不要滚动到两侧,所以我宣布它是这样。

2017年9月15日更新:我必须将此修复程序用于另一个项目,并且我能够在position: fixed选择器上没有这些声明height: 100%;html。 YMMV

2018年4月12日更新(在评论中提到):如果您在页面上使用固定元素,那么这些元素在滚动时可能会出现视觉上的“晃动”。

答案 3 :(得分:4)

我使用了iNoBounce https://github.com/lazd/iNoBounce,效果很好!

如果您还需要允许水平滚动,那么santi6291 https://github.com/lazd/iNoBounce/pull/36处的拉取请求会修复

答案 4 :(得分:1)

这些答案都无法在2019年用于iOS 13移动Safari。

答案 5 :(得分:0)

我设法在移动版Safari上解决了支持overflow: autooverflow: scroll的大多数问题:

  • 在触摸列表开头后挂起滚动视图,然后向下移动然后向上移动(在这种情况下,移动Safari会对整个页面运行默认的弹跳行为)
  • 支持固定标题/操作栏在顶部,而不会通过滚动条重叠它

window.addEventListener('DOMContentLoaded', function () {
                        
  var atTop = true;
  var atBottom = false;

  var body = document.getElementById('fixedBody');
  body.addEventListener('touchmove', function(event){
    event.preventDefault();
  });

  body.addEventListener('touchstart', function(event){
    event.preventDefault();
  });

  body.addEventListener('touchend', function(event){
    event.preventDefault();
  });

  var scrollingDiv = document.getElementById('scrollDiv');
  if (scrollingDiv.scrollHeight <= scrollingDiv.clientHeight) {
    atBottom = true;
  }

  scrollingDiv.addEventListener('scroll', function(event){
    
    if (event.target.scrollTop === 0) {
      atTop = true;
    } else {
      atTop = false;
    }
    
    if (event.target.scrollHeight - event.target.scrollTop === event.target.clientHeight) {
      atBottom = true;
    } else {
      atBottom = false;
    }
  });
  
  var lastY = 0;
  var topLock = false;
  var bottomLock = false;
  
  scrollingDiv.addEventListener('touchmove', function(event){
    event.stopPropagation();
    var currentY = event.touches[0].clientY;
    if (currentY > lastY) {
      // moved down
      if (atTop) {
        event.preventDefault();
        topLock = true;
      }

      if (bottomLock) {
        bottomLock = false;
        // TODO: Emulate finger remove and touch again here
      }
    } else if(currentY < lastY){
      // moved top
      if (atBottom) {
        event.preventDefault();
        bottomLock = true;
      }

      if (topLock) {
        topLock = false;
        // TODO: Emulate finger remove and touch again here
      }
    }
     
    lastY = currentY;
  });

  scrollingDiv.addEventListener('touchstart', function(event){
    lastY = event.touches[0].clientY;
    event.stopPropagation();
  });

  scrollingDiv.addEventListener('touchend', function(event){
    event.stopPropagation();
  });

});
<html>
<head>
  <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=0"/>
</head>
<body id="fixedBody" style="overflow: hidden;">
  <div style="position: fixed; height: 64px; width:100%; top:0; left:0; background-color: green; z-index: 1;">
    Header
  </div>
  <div id="scrollDiv" style="position: absolute; width: 100%; top: 64px; bottom: 0px; overflow-y: auto; overflow-x: hidden; -webkit-overflow-scrolling: touch; background-color: white;">
    <div style="height: 150px; background-color: blue;">
      First
    </div>
    <div style="height: 150px; background-color: red;">
      Second
    </div>
    <div style="height: 150px; background-color: green;">
      Third
    </div>
    <div style="height: 150px; background-color: black;">
      Another
    </div>
  </div>
</body>
</html>

我唯一需要注意的是,当用户触摸并开始向下然后向上移动时,没有任何反应(预期:列表应向下滚动)。但至少该方法可以防止“伪滚动”而不会混淆用户。

要克服最后一个问题,有必要模拟触摸结束,然后在方向改变时触摸开始事件(我已经发出“TODO”评论)。

更新:可以避免在{1}}和DisallowOverscroll = true的iOS Cordova上使用JavaScript代码修复。

答案 6 :(得分:0)

对于iOS 13上的2019 Safari,我可以使用此修复程序:

   html {
      overflow: hidden;
      height: 100%;
      position: fixed;
   }

   body {
      overflow: auto;
      height: 100%;
      position: relative;
  }

至少对我来说,它涵盖了大多数情况。

答案 7 :(得分:0)

您只需要在overscroll-behavior: none上添加CSS属性body

body {
  overscroll-behavior: none
}

更多详细信息在this article

答案 8 :(得分:0)

我尝试了所有CSS解决方案,但无济于事。当到达顶部/底部时,iOS仍会反弹某些div,或者在使用固定位置进行操作时,滚动本身确实变得不可靠且容易出错。因此,我想出了一个JavaScript解决方案,它实际上非常简单,似乎已经为我解决了这个问题:

    function onTouchStart(e) {
        // Save position of touch
        console.log("touchstart");
        const touch = e.touches[0] || e.changedTouches[0];
        window.lastY = touch.pageY;
    }

    function onTouchMove(e) {
        console.log("touchmove");
        // Check user isn't scrolling past content. If so, cancel move to prevent ios bouncing
        const touch = e.touches[0] || e.changedTouches[0];
        y = touch.pageY;
        if (y < window.lastY && e.srcElement.scrollTop == (e.srcElement.scrollHeight - e.srcElement.clientHeight)) {
            console.log("user is trying to scroll down without anywhere to scroll to. Canceling propagation.");
            e.preventDefault();
        } else if (y > window.lastY && e.srcElement.scrollTop == 0) {
            console.log("user is trying to scroll up without anywhere to scroll to. Canceling propagation.");
            e.preventDefault();
        }
    };

    document.addEventListener("touchstart", onTouchStart, { passive: false });
    document.addEventListener("touchmove", onTouchMove, { passive: false });
        

答案 9 :(得分:0)

对于离子用户 -

对我来说一个简单的解决方案是在 ion-content 的子 div 上使用 slot="fixed"

<ion-content>
    <div slot="fixed" />
</ion-content>

答案 10 :(得分:0)

就我而言,我希望地址栏在滚动时消失,并且在用户滚动时获得弹跳效果,但这为我解决了

html {
  position: relative;
  overflow-y: scroll;
}

body {
  overflow-y: scroll;
  overflow-x: hidden;
  -webkit-overflow-scrolling: touch;
}