Disable elastic scrolling in Safari [duplicate]
Asked Answered
D

9

62

I just wanted to diable the elastic scrolling/bounce effect in Safari (OSX Lion).

I found the solution to set overflow: hidden for body in css, but as expected it only disables the scrollbar, so if the website is "longer" than the screen you won't be able to scroll!

Any solutions or hints are welcome! Thanks!

Deletion answered 16/11, 2011 at 10:32 Comment(6)
I’m curious what need there might be for this. Let the system do its thang, is usually my motto.Ellyellyn
It's for single page apps, which are designed to look more like desktop apps I think.Varietal
It's a design decision made without our consent that simply doesn't suit all possible web apps.Somber
Just make a container within body for all of your content and keep the overflow:hidden;?Fadein
Elastic scroll severely interferes with the performance of setTimeout (in Chrome, at least) - if you're doing WebAudio scheduling on-the-fly disabling elastic scroll can reduce the risk of drop-outs.Intervalometer
Actually, strike that. Even if you disable elastic scrolling setTimeout still misbehaves when scrolling beyond the limit of the page (even if the page content does not move).Intervalometer
M
76

You can achieve this more universally by applying the following CSS:

html,
body {
  height: 100%;
  width: 100%;
  overflow: auto;
}

This allows your content, whatever it is, to become scrollable within body, but be aware that the scrolling context where scroll event is fired is now document.body, not window.

Melantha answered 1/5, 2013 at 22:24 Comment(16)
This works not just on Safari, but on Chrome, and probably Firefox as well. Thanks!Salade
Since applying this CSS will remove momentum scrolling on iOS, don't forget to add -webkit-overflow-scrolling: touch to the body to re-enable momentum scrolling on iOS.Hampstead
Works great for disabling bounce, but I'm not able to capture the scroll event on document.body, as such $(document.body).on("scroll", function() { console.log('scrolling') }); I also can't capture on the scrolling element itself. Any ideas?Manet
Hmm, have you tried inspecting which element is actually scrolling? It sounds like another element on the page is scrolling instead of the page body.Melantha
This solved the issue, though I think it's worth noting that I had to change all of my scroll event listeners to listen to document.body instead of 'window'.Peloquin
Resurrecting this... This doesn't seem to work on ios 7 (chrome or safari). The body does scroll as expected, but once on an edge html scrolling seem to take over.Revers
Hmm, I haven't tried this in newest iOS yet. Platform level changes are difficult to override.Melantha
-webkit-overflow-scrolling: touch; seems to re enable the elastic scrolling. On another note, document.body.scrollTop, window.pageYOffset and window.scrollY don't give the value for how far the page has scrolled anymore after using this solution, they stay at 0. How can I find this?Spectrochemistry
Please see @Yisela for why this is a bad hack!Sirree
On iOS 8 Safari, setting this CSS will restrict user entering the minimal-ui.Merrymerryandrew
Adding this CSS makes the fonts look all fuzzy on Chrome... weird.Sanborn
This does not work at all for me.Eligible
Is this solution still supposedly correct? I cannot get it to work on an iPhone 6S with iOS 9.3. @Aintaer, if it does still work for you, can you provide a test page?Bautram
This can not work on ios 10Pepe
best solution bram.us/2016/05/02/…Pepe
This made scrolling on mobile dreadfully slow in my scenario. When you combine all of them including @Hampstead suggestion to be html, body { height: 100%; width: 100%; overflow: auto; -webkit-overflow-scrolling: touch; } It should work on iOSKerf
F
15

If you use the overflow:hidden hack on the <body> element, to get back normal scrolling behavior, you can position a <div> absolutely inside of the element to get scrolling back with overflow:auto. I think this is the best option, and it's quite easy to implement using only css!

Or, you can try with jQuery:

$(document).bind(
'touchmove',
function(e) {
e.preventDefault();
}
);

Same in javasrcipt:

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

Last option, check ipad safari: disable scrolling, and bounce effect?

Farfamed answered 16/11, 2011 at 11:5 Comment(3)
Thanks, the css workaround works! I hoped that there may be a javascript/jquery solution, because this one isn't working for me. I think this will only work for iOS devices. Correct me if I'm wrong.Deletion
The answer is a little convoluted. To target OS X, all you need is overflow: hidden on the html and body element. There is no "touchmove" event for OS X, which is the OS that is being asked about.Receivable
If you're transitioning from jQuery, don't forget document.querySelector('div'); document.querySelectorAll('.someClass'); document.querySelector('#someID');Poppyhead
M
12

overflow:hidden;-webkit-overflow-scrolling:touch won't work well on iOS safari 8.1, as the fixed header will be out of visible area.

gif

As @Yisela says, the css should be placed on .container(the <div> below <body>). which seems no problem(at leas on safari iOS 8.1)

gif

I've place the demo on my blog: http://tech.colla.me/en/show/disable_elastic_scroll_on_iOS_safari

Microampere answered 12/7, 2015 at 16:30 Comment(1)
The link isn't active anymore :(Assignation
T
12

I had solved it on iPad. Try, if it works also on OSX.

body, html { position: fixed; }

Works only if you have content smaller then screen or you are using some layout framework (Angular Material in my case).

In Angular Material it is great, that you will disable over-scroll effect of whole page, but inner sections <md-content> can be still scrollable.

Travertine answered 12/9, 2016 at 14:21 Comment(2)
I must also add body, html { width: 100%; } for right render in IE11. :(Travertine
This worked for me also.Merriman
H
5

I made an extension to disable it on all sites. In doing so I used three techniques: pure CSS, pure JS and hybrid.

The CSS version is similar to the above solutions. The JS one goes a bit like this:

var scroll = function(e) {
    // compute state
    if (stopScrollX || stopScrollY) {
        e.preventDefault();              // this one is the key
        e.stopPropagation();
        window.scroll(scrollToX, scrollToY);
    }
}

document.addEventListener('mousewheel', scroll, false);

The CSS one works when one is using position: fixed elements and let the browser do the scrolling. The JS one is needed when some other JS depends on window (e.g events), which would get blocked by the previous CSS (since it makes the body scroll instead of the window), and works by stopping event propagation at the edges, but needs to synthesize the scrolling of the non-edge component; the downside is that it prevents some types of scrolling to happen (those do work with the CSS one). The hybrid one tries to take a mixed approach by selectively disabling directional overflow (CSS) when scrolling reaches an edge (JS), and in theory could work in both cases, but doesn't quite currently as it has some leeway at the limit.

So depending on the implementations of one's website, one needs to either take one approach or the other.

See here if one wants more details: https://github.com/lloeki/unelastic

Hairdresser answered 27/2, 2014 at 23:9 Comment(0)
I
4

None of the 'overflow' solutions worked for me. I'm coding a parallax effect with JavaScript using jQuery. In Chrome and Safari on OSX the elastic/rubber-band effect was messing up my scroll numbers, since it actually scrolls past the document's height and updates the window variables with out-of-boundary numbers. What I had to do was check if the scrolled amount was larger than the actual document's height, like so:

$(window).scroll(
    function() {
        if ($(window).scrollTop() + $(window).height() > $(document).height()) return;
        updateScroll(); // my own function to do my parallaxing stuff
    }
);
Insolent answered 7/5, 2013 at 6:40 Comment(0)
B
3

You could check if the scroll-offsets are in the bounds. If they go beyond, set them back.

var scrollX = 0;
var scrollY = 0;
var scrollMinX = 0;
var scrollMinY = 0;
var scrollMaxX = document.body.scrollWidth - window.innerWidth;
var scrollMaxY = document.body.scrollHeight - window.innerHeight;

// make sure that we work with the correct dimensions
window.addEventListener('resize', function () {
  scrollMaxX = document.body.scrollWidth - window.innerWidth;
  scrollMaxY = document.body.scrollHeight - window.innerHeight;
}, false);

// where the magic happens
window.addEventListener('scroll', function () {
  scrollX = window.scrollX;
  scrollY = window.scrollY;

  if (scrollX <= scrollMinX) scrollTo(scrollMinX, window.scrollY);
  if (scrollX >= scrollMaxX) scrollTo(scrollMaxX, window.scrollY);

  if (scrollY <= scrollMinY) scrollTo(window.scrollX, scrollMinY);
  if (scrollY >= scrollMaxY) scrollTo(window.scrollX, scrollMaxY);
}, false);

http://jsfiddle.net/yckart/3YnUM/

Banquer answered 23/3, 2014 at 17:25 Comment(4)
This causes heavy flicker on our ipad, while preventing the page to scroll up/down.Pekan
@Pekan ...and this is a reason for a downvote? Really?!Banquer
Sorry, but the solution itself is not good. It should only be applied to desktop computers. Mobile devices which are quite popular nowadays, have a problem with a method like this. So, I suggest if you can add some sort of check around the code, I guess it would be okay.Pekan
@Pekan Sorry, but... The most things here, are just there to explain how something could work. All of the most code-examples are not ready-to-use, you have to dive into the code and take the part/s that interests you. If something doesn't fit your needs, dive deeper and fix it (...and maybe, open source it?!)Banquer
T
1

There are a to of situations where the above CSS solutions do not work. For instance a transparent fixed header and a sticky footer on the same page. To prevent the top bounce in safari messing things and causing flashes on full screen sliders, you can use this.

    if (navigator.userAgent.indexOf('Safari') != -1 && navigator.userAgent.indexOf('Chrome') == -1) {

        $window.bind('mousewheel', function(e) {

            if (e.originalEvent.wheelDelta / 120 > 0) {

                if ($window.scrollTop() < 2) return false;
            } 
        });

    }
Tenishatenn answered 3/6, 2015 at 19:56 Comment(3)
userAgent snooping is grossSomber
I Agree. So maybe offer a fix in this situation?Tenishatenn
All rabbit holes, but the accepted answer is the most spacious oneSomber
C
1

None of the above solutions worked for me, however instead I wrapped my content in a div (#outer-wrap) and then used the following CSS:

body {
   overflow: hidden;
}
#outer-wrap {
    -webkit-overflow-scrolling: touch;
     height: 100vh;
     overflow: auto;
}

Obviously only works in browsers that support viewport widths/heights of course.

Cavity answered 9/12, 2015 at 3:24 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.