devlsh
devlsh

Reputation: 570

iPhone Web App - Stop body scrolling

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.

Upvotes: 25

Views: 40640

Answers (6)

Will Po
Will Po

Reputation: 221

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.

Upvotes: 0

Shawn
Shawn

Reputation: 11

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>

Upvotes: 1

Jarid R. Margolin
Jarid R. Margolin

Reputation: 526

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

Upvotes: 16

Jiaaro
Jiaaro

Reputation: 76968

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.

Upvotes: 11

will
will

Reputation: 4575

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

Upvotes: 5

Ben Clayton
Ben Clayton

Reputation: 82257

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?

Upvotes: 10

Related Questions