iPhone Web App - Stop body scrolling

Solution 1:

After reviewing several solutions, I began to create a custom solution:

bouncefix.js

http://jaridmargolin.github.io/bouncefix.js/

Usage:

bouncefix.add(el)

Apply fix so that the given element no longer causes a full body elastic bounce when scrolling at its extremes.

bouncefix.remove(el)

Remove all listeners/observers responsible for fixing the full body elastic bounce.

Why?

Scrollfix was a good start, however I noticed several problems:

  1. It only worked when there was scrollable content. If you had an empty page, the bounce effect on the body would occur.
  2. The API did not expose a method to remove the listeners. My app will have multiple pages, and it didn't feel right to keep all of the listeners attached as the user moved around the app.

How?

It uses a similar approach to that of scrollfix. The problem occurs when you are at one of the scrolling extremes. On touchstart, we look to see if we are at the top extreme or bottom extreme, adding 1px if we are at the top, and removing 1px if we are at the bottom.

Unfortunately, this trick only works if we are able to set the scrollTop value. If the content is not yet scrollable, for example, you only have 1 list item, the whole body will again scroll. Bouncefix.js will take care of all of this behind the scenes by using event delegation and checking the scrollHeight against the offsetHeight anytime touchstart is triggered. In the case that there is no scrollable content, all scrolling on the container is blocked with e.preventDefault();

Solution 2:

what worked for me:

html, body, .scrollable {
    overflow: auto; 
    -webkit-overflow-scrolling: touch;
}

plus (using jQuery...)

$(function() {
  $(document).on("touchmove", function(evt) { evt.preventDefault() });
  $(document).on("touchmove", ".scrollable", function(evt) { evt.stopPropagation() });
});

it's important that the evt.stopPropagation() call is using a delegate since it's also caught on the document but cancelled at the last second.

In effect this catches all touchmove events on the document but if the original event was bubbled up from a .scrollable we simply stopPropagation instead of cancelling the event.

Solution 3:

Try putting this at the top of your JS file..

document.ontouchmove = function(event){
    event.preventDefault();
}

This'll stop you being able to scroll your page at all, so you won't be able to see the 'grey area' at the top.

Source: Stop UIWebView from "bouncing" vertically?

Solution 4:

Update:

This issue has been a pain for lots of people, but here is a solid solution created by bjrn:

Demo: http://rixman.net/demo/scroll/
And there's a discussion about it here: https://github.com/joelambert/ScrollFix/issues/2

This was originally posted by ryan in answer to a question of mine: Prevent touchmove default on parent but not child.

I hope that helps some people.


Original answer:

I'm actually looking into the exact same issue and I've come across this:

https://github.com/joelambert/ScrollFix

I've tried it out and it works nearly perfectly. If you copy all the code into a demo and give it a go on iOS you'll not see the grey background unless you grab the black bar and try to scroll that. However, it should be pretty easy to use the code that Ben supplied to prevent that happening ( preventDefault() ).

Hope that helps (It's definitely helped me!) will :)

Solution 5:

Like others suggested I wrote a quick function that bumps my scrolling div one pixel whenever it is scrolled to the top or bottom to prevent the entire page from dragging. That's fine for my scrolling div but the fixed divs on my page were still susceptible to page drag. I accidentally noticed that when I layered a fixed div on top of my scrolling div (which no longer dragged the page because of my bump function) the touch drag events were passed through the fixed div to the scrolling div underneath and that fixed div was no longer a potential page dragger. Based on that I figured it might be possible to use a full screen scrolling div in the background as the foundation for the entire page so that all dragging would be pass through and my actual elements on top will be safe. The foundation div needs to actually scroll so I placed a filler div inside of it and made it slightly larger to guarantee it scrolls. And it worked! Below is an example using this idea to prevent page dragging. Sorry if this is obvious or already posted but I didn't see this anywhere else and it really improved my web app. Please try it out and let me know if it helps.

<html>
<head>
<meta name="viewport" content="width=device-width, initial-scale=1, user-scalable=no">

<style>
.myDiv {
    position:absolute;
    left:0;
    width:100%;
    color:white;
}

.myDiv.hidden {
    top:0;
    height:100%;
    -webkit-overflow-scrolling:touch;
    overflow-y:scroll;
}

.myDiv.title {
    top:0;
    height:30%;
    background-color:#6297BC;
}

.myDiv.scroll {
    bottom:0;
    height:70%;
    -webkit-overflow-scrolling:touch;
    overflow-y:scroll;
    background-color:#ADADAD;
}
</style>

<script>
function setup() {  
    hiddenScrollingDiv.addEventListener("scroll", preventWindowScroll);
    visibleScrollingDiv.addEventListener("scroll", preventWindowScroll);
    hiddenScrollingDiv.style.height=window.innerHeight; 
    visibleScrollingDiv.style.height=parseInt(window.innerHeight*.7)+1; 
    fillerDiv.style.height=window.innerHeight+2;
    hiddenScrollingDiv.scrollTop=1;
    visibleScrollingDiv.scrollTop=1;
}

function preventWindowScroll(evt) {
    if (evt.target.scrollTop==0) evt.target.scrollTop=1;
    else if (evt.target.scrollTop==(evt.target.scrollHeight-parseInt(evt.target.style.height))) evt.target.scrollTop=evt.target.scrollHeight-parseInt(evt.target.style.height)-1;
}
</script>
</head>

<body onload="setup()">
<div id="hiddenScrollingDiv" class="myDiv hidden"><div id="fillerDiv"></div></div>
<div id="visibleTitleDiv" class="myDiv title"><br><center>Non-scrolling Div</center></div>

<div id="visibleScrollingDiv" class="myDiv scroll">
<ul>
    <li style="height:50%">Scrolling Div</li>
    <li style="height:50%">Scrolling Div</li>
    <li style="height:50%">Scrolling Div</li>
</ul>
</div>

</body>
</html>