Disable Inertia Scroll for "Single-Page" Webapp

Disable inertia scroll for single-page webapp

Update May/2020

There are an array of considerations when disabling inertia scroll, with respect to browser compatibility. Here is a repo which attempts to abstract away those compatibility problems: https://github.com/willmcpo/body-scroll-lock

This repo attempts to reconcile drawbacks in both older solutions listed below:

Update Jan/2019

There's a simpler CSS solution:

body {
overflow: hidden;
}

Original Answer:

I found a perfect solution - override the scroll events.

$body.addEventListener("mousewheel", function(e){ e.preventDefault(); });

Turns out that inertia scroll is really just an extension of normal scrolling, where a special mouse driver emits scroll events in such a way as to emulate the inertia effect. So overriding scroll events inherently prevents inertia scroll.

See this link for examples with cross-platform compatibility.

How to stop inertial scrolling in a web app

I do not think that you can stop the browser from emitting events. That is why one has to use preventDefault. Therefore, to answer your questions,

  1. No. However, you can highjack the event and use it to suit your intended behavior.
  2. Yes. The mousewheel event had a wheelDelta value which is larger the more inertia there is. It is important to notice that mousewheel is deprecated. You should use the wheel event. In this case, the event will have deltaX and deltaY values that you can use.

With these factors in mind, you can easily build your own behavior. I have simplified the code to emphasize the strategy. Briefly, you set the center of the picture and the interesting area around that center. You always prevent the default behavior (highjack the event). If the scrollLeft property is outside the interesting area around the center, you move the scroll depending on deltaX (with an optional multiplier). If it is inside the area, you can make the change in scrollLeft much lower by dividing it. Notice that in the example the divisor is probably too large to emphasize the effect.

const output = document.getElementById("output");
const container = document.getElementById("container");
container.addEventListener("wheel", mousewheel, false);

const multiplier = 2;
const center = 2500;
const interesting = 500;

function mousewheel(event) {
event.preventDefault();
let dx = event.deltaX * multiplier;
if (Math.abs(container.scrollLeft - center) < interesting){
dx /= 25;
}
container.scrollLeft += dx;

}
body {
margin: 0;
height: 100px;
}

div#container {
width: 500px;
height: 100%;
background-color: green;
overflow: auto;
}

div#content {
width: 5500px; /* Allows scrollLeft up to 5000 */
height: 100%;
background-color: red;
clip-path: polygon(0 0, 100% 0, 100% 100%)
}
<div id="container">
<div id="content"></div>
</div>
<pre id="output">0</p>

ipad safari: disable scrolling, and bounce effect?

This answer is no longer applicable, unless you are developing for a very old iOS device... Please see other solutions


2011 answer: For a web/html app running inside iOS Safari you want something like

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

For iOS 5 you may want to take the following into account: document.ontouchmove and scrolling on iOS 5

Update September 2014:
A more thorough approach can be found here: https://github.com/luster-io/prevent-overscroll. For that and a whole lot of useful webapp advice, see http://www.luster.io/blog/9-29-14-mobile-web-checklist.html

Update March 2016: That last link is no longer active - see https://web.archive.org/web/20151103001838/http://www.luster.io/blog/9-29-14-mobile-web-checklist.html for the archived version instead. Thanks @falsarella for pointing that out.

CSS: Disable bounce effect for scroll on iOS 13

Polyfilling this CSS property in Safari is pretty tricky.

For non-scrollable elements, you can prevent scroll chaining by simply turning off touch gestures. You can do that with a CSS property that is supported by Safari: touch-action: none.

But for scrollable elements, JavaScript will be required.

Remember that scroll chaining occurs when you reach the bounds of the element. So we need to ensure that the user is never able to fully scroll to the top or bottom. Doing this the wrong way can cause UX problems, because the user will clearly be fighting against the default inertia scroll.

So here's the trick:

Create an inner element that is at least 3px taller than the size of its scrolling parent, to force the area to get the overscroll behavior.
Immediately set the scroll position to 1px to prevent scroll chaining when scrolling up
With JavaScript, catch when the scroll position is exactly 0 or exactly at the bottom. After a requestAnimationFrame, set the scroll position to 1px from either the top or bottom.
The container will still get the inertia scroll (the user won't have to fight it) but it won't trigger scroll chaining.

Here's the JavaScript function:

this.addEventListener('scroll', async handleScroll() {
await new Promise(resolve => window.requestAnimationFrame(resolve))
const {
scrollTop,
scrollLeft,
scrollHeight,
clientHeight
} = this
const atTop = scrollTop === 0
const beforeTop = 1
const atBottom = scrollTop === scrollHeight - clientHeight
const beforeBottom = scrollHeight - clientHeight - 1

if (atTop) {
this.scrollTo(scrollLeft, beforeTop)
} else if (atBottom) {
this.scrollTo(scrollLeft, beforeBottom)
}
}

source: https://dev.to/mpuckett/the-holy-grail-web-app-shell-with-header-and-footer-for-iphone-549j

Remove bounce on scroll in browser, issue with position:fixed div

Bounce scroll in the browser is a feature of some versions of iOS / macOS.

To prevent it from happening on a website we can use the following:

CSS

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

#main-container {
position: absolute;
top: 0;
bottom: 0;
left: 0;
right: 0;
overflow: auto;
}

HTML

<body>
<div id="main-container">
...
</div>
</body>

Demo

How to disable bouncing in html5 fullscreen iphone app?

This will prevent scrolling on the whole page

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

In your case, where you want some divs to be scrollable, and some not to, you should be able to catch the event before it gets to the document

scrollableDiv.ontouchmove = function(e) {e.stopPropagation()};

Prevent overflow / rubberband scrolling on iOS

The solution:

Type 1:

The most basic solution to prevent overflow scrolling on the element itself is to prevent default on touch events.

document.body.addEventListener('touchmove', function(e) { 
e.preventDefault();
});

This method however disables the browsers native momentum scroll and is thereby not suitable for most applications. With some refinement however (only prevent if at top scrolling up or at bottom scrolling down, ...) this method fixes most problems. Many possible implementations can be found in this SO post.

Type 2:

Overflow scrolling on the body however is not prevented by methods described above.

One possible solution which seems reasonable is to prevent the element from ever being at its top or bottom position as described as best solution on mentioned question.

anElement.addEventListener('touchstart', function( event ){
if( this.scrollTop === 0 ) {
this.scrollTop += 1;
} else if( this.scrollTop + this.offsetHeight >= this.scrollHeight ) {
this.scrollTop -= 1;
}
}

This however did not reliably work on iOS 9.3.2.

What did work however is setting position: fixed on the <body> element to prevent the body from moving. Please note however that this still does not completely stop type 2 from happening, which is why sometimes you cannot scroll/focus any element because in the background type2 with its focus lock is still happening (again, after you stop touching the screen for a moment it again works as expected).

While this is still far from being the optimal solution it seems to be the best we can get for the time speaking.

Edit: Please note that I am not sure if it is safe to put position: fixed on a <body> element. To track possible issues I have created following SO post. Apparently it might be better to create a wrapper element as child of body and set that element to position: fixed to avoid zoom problemes.


Edit 2: The definite solution

The script iNoBounce works wonders. Just load it to the page and experience a bounce-free web application. So far I have not found any problems with this solution.



Related Topics



Leave a reply



Submit