Disable elastic scrolling in Safari
JavascriptJqueryCssSafariOsx LionJavascript Problem Overview
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!
Javascript Solutions
Solution 1 - Javascript
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
.
Solution 2 - Javascript
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 https://stackoverflow.com/questions/7768269/ipad-safari-disable-scrolling-and-bounce-effect
Solution 3 - Javascript
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.
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)
I've place the demo on my blog: http://tech.colla.me/en/show/disable_elastic_scroll_on_iOS_safari
Solution 4 - Javascript
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.
Solution 5 - Javascript
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
Solution 6 - Javascript
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
}
);
Solution 7 - Javascript
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);
Solution 8 - Javascript
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;
}
});
}
Solution 9 - Javascript
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.