25

I am creating an iPhone Web Application and you are now, since iOS5, able to use position: fixed; for headers etc. etc.

Although it works, if you scroll up at the top of a page, it displays the usual gray area for a while before you can't scroll anymore

Example

Is there a way to stop this scrolling? I've tried things like overflow: hidden; but I can't seem to find anything.

P.S. I only want the one thing to stop scrolling, I have a div named #container which I still want to have the ability to scroll.

alex
  • 479,566
  • 201
  • 878
  • 984
oyed
  • 572
  • 2
  • 14
  • 34
  • Probably, I'm not sure what it is called. – oyed Dec 13 '11 at 11:38
  • I have posted a CSS solution for this here: http://stackoverflow.com/questions/15732466/mobile-overflowscroll-and-overflow-scrolling-touch-prevent-viewport-bounce/18827652#18827652 The key was to wrap the content in three -webkit-overflow-scrolling: touch divs. I have tested the solution in iOS7 GM too. – romiem Dec 22 '13 at 14:32

6 Answers6

16

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();

  • As much as I like the fact that it stopped the bouncing, it completely removes horizontal scrolling inside the added class. I have bouncefix.add("nobounce"); in doc.ready ("nobounce" is on body). Then the line right after it, I have bouncefix.remove("scrollable"); which is the class on my container with a bunch of scrollable divs inside. What am I doing wrong? – whyoz Jun 04 '15 at 00:44
11

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.

Jiaaro
  • 74,485
  • 42
  • 169
  • 190
  • The `overflow: auto` seems to mess up fixed position elements on iOS. They scroll with the page, then snap into place when scroll ends. – Stoutie Aug 01 '14 at 23:01
  • @stoutie yeah I also ran into that. I worked around the issue by appending any fixed position elements to the body (outside the `.scrollable` element) – Jiaaro Aug 04 '14 at 13:29
  • I had tried this (and a few other methods) on an object element in a dedicated div with this code but the problem is when the user saves the website as an app to their start page, and launches that page's object from the app. For some reason, the bounce scrolling will then not be denied... – cbmtrx Apr 09 '15 at 15:34
10

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?

Community
  • 1
  • 1
Ben Clayton
  • 80,996
  • 26
  • 120
  • 129
  • 2
    I only want the one thing to stop scrolling, I have a div named #container which I still want to have the ability to scroll. – oyed Dec 13 '11 at 12:03
  • you can use the same thing on the element you do not want to be scrolling. – Naman Goel Oct 20 '12 at 19:46
  • Also, I'm pretty sure there some viewport tag for something like this – Naman Goel Oct 20 '12 at 19:47
  • I've notice this disables the scrolling also for any other element in the document with `overflow:scroll`. Is there any way to avoid it happen? – Alvaro Oct 08 '13 at 14:29
  • document.body.ontouchmove also works if you need a carousel/ swipeable component on the page to work too. This way only the body scroll will be disabled. – CodeUK Jun 13 '18 at 08:26
5

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 :)

Community
  • 1
  • 1
will
  • 4,557
  • 6
  • 32
  • 33
  • 1
    Um... Its documentation clearly says `ScrollFix doesn't prevent the page from being scrolled when if a touch is registered whilst the scrolling section is bouncing` So ScrollFix is not the solution for this problem. – Derek 朕會功夫 Oct 10 '12 at 03:06
  • Yes, true, but at the time, it was the best option. Now there are better solutions. Which I have added into my original answer. – will Oct 11 '12 at 10:47
1

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>
Shawn
  • 11
  • 1
0

A previous solution suggested :

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

However, this also stops the scrolling for the element of interest.

In order to overcome this, the following can be done:

  1. Detect when the top/bottom of an element of interest (eg. modal) has been scrolled to.
  2. If bottom scrolled to, use the above preventDefault technique for the ontouchmove event, but ONLY IF, the user attempts to scroll down further.
  3. If user attempts to scroll up, don't preventDefault anymore.
  4. Vice versa applies if top is scrolled to.

Here's the source code for an implementation of the above logic.

The npm package can also be downloaded for convenience (tested with vanilla JS/React on iOS mobile/desktop safari as well as Android/Desktop Chrome).

Checkout https://medium.com/jsdownunder/locking-body-scroll-for-all-devices-22def9615177 for more of an explanation of the different approaches.

Will Po
  • 221
  • 3
  • 4