Prevent iOS bounce without disabling scroll ability

前端 未结 7 1183
情歌与酒
情歌与酒 2020-11-28 06:14

I am trying to implement a solution to prevent the iOS bounce effect in Safari for iOS when a web page content is larger than the viewport.

The page I am working on

相关标签:
7条回答
  • 2020-11-28 06:40

    I managed to solve most problems supporting overflow: auto and overflow: scroll on mobile Safari:

    • without hanging the scroll view after touching at the beginning of list, then moving down and then up (mobile Safari runs its default bouncing behavior for the whole page in that case)
    • support for fixed header / action bar on top without ugly overlapping of it by a scrollbar

    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>

    The only caveat I have is that when user touches and starts moving down and then up, nothing happens (expected: the list should scroll down). But at least the method prevents "pseudo scrolling down" and not confuses user.

    To overcome that last problem, it's necessary to emulate a touch end and then touch start event when direction changed (I've put "TODO" comments).

    Update: it's possible to avoid using JavaScript code fix on iOS Cordova with DisallowOverscroll = true and WKWebView.

    0 讨论(0)
  • 2020-11-28 06:41

    I used iNoBounce https://github.com/lazd/iNoBounce and it works perfectly!

    If you need to allow horizontal scrolling as well, there is a pull request by santi6291 at https://github.com/lazd/iNoBounce/pull/36 which fixes that

    0 讨论(0)
  • 2020-11-28 06:44

    If I'm interpreting your question correctly, we've been having the same issue for years developing cross-platform mobile web apps, trying to get all the different proprietary scroll features to work correctly on each device: Apple iOS, Google Android, Windows Phone, laptop Chrome, laptop Safari, IE, and laptop Edge.

    jQuery Mobile continues to try and fix this within the confines of their framework, but it's too much whack-a-mole, with the constant updates from each device maker / OS maker.

    Yes, we've got solutions for each individual mobile device. And we have tested, but not seriously considered developing device-selective paging frameworks for each device, requiring us to detect each device and present a slightly different framework for each. Insanely bad idea with basically maintaining at least 3 and really up to a dozen different versions of your code.

    SOLUTION: We've had the most luck by just putting your persistent header and footer on top of your page framework. Here is the general solution using in-line styles for simplicity:

    <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:#ffffdffffd; 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>
    

    So, the Body could be any jQuery Mobile set of pages. In fact, theoretically, the Body could be almost any content from any framework.

    Special note, the line with height:calc(100% - 1px); is critical to the magic.

    The seemingly infinite combinations or permutations of this issue have almost become a crusade for us over the years, trying to find the most pure, simplest, and most universally compatible solution. So after dedicating an embarrassing number of man-hours to this topic, this is not only our best solution, it's also the ONLY universally compatible approach we've found that also allows you to stick with just a singular code-base. It has been successfully tested on the latest versions of iOS, Windows Phone, Android, laptop Chrome, laptop Safari, PhoneGap, laptop Firefox, IE 9-11, and Windows Edge.

    TAGS: mobile app, web app, fixed header, fixed footer, persistent header, persistent footer, scroll issue, iOS scroll bounce, Chrome scroll bounce, Android scroll bounce, webkit scroll issue, webkit touch scrolling, iOS touch scrolling issue

    0 讨论(0)
  • 2020-11-28 06:49

    For 2019 Safari on iOS 13, I was able to use this fix:

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

    At least for me it covers most of the cases.

    0 讨论(0)
  • 2020-11-28 06:55

    You just need to add a CSS property overscroll-behavior: none to your body

    body {
      overscroll-behavior: none
    }
    

    More details are in this article

    0 讨论(0)
  • 2020-11-28 06:57

    I went through a few answers on SO and things were looking bleak until stumbled upon this code.

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

    The style declarations for body can be put on any element that you want to have the ability to scroll. You can also alter overflow-x and overflow-y as needed. I personally needed it to NOT scroll to the sides so I declared it as so.

    update Sept 15 2017: I had to use this fix for another project and I was able to do without these declarations position: fixed and height: 100%; on the html selector. YMMV

    Update April 12 2018 (mentioned in comments): If you're using fixed elements on the page, those elements may have a visual "shakiness" when scrolling.

    0 讨论(0)
提交回复
热议问题