I am writing a web app in HTML and JavaScript for use on an iPhone. What I would like to achieve is preventing the app from elastic scrolling (scrolling past the pages extents a
The above solution was insufficient in my case. It prohibits all scrolling. It is possible to build a solution here that prevents elastic scrolling in iOS, but that still allows scrolling on children. I this took the above solution and added a check that bubbles up the DOM to determine which scrollable element "owns" the scroll event, and if it's the root element of the page, I drop it:
function overflowIsHidden(node) {
var style = getComputedStyle(node);
return style.overflow === 'hidden' || style.overflowX === 'hidden' || style.overflowY === 'hidden';
}
function findNearestScrollableParent(firstNode) {
var node = firstNode;
var scrollable = null;
while(!scrollable && node) {
if (node.scrollWidth > node.clientWidth || node.scrollHeight > node.clientHeight) {
if (!overflowIsHidden(node)) {
scrollable = node;
}
}
node = node.parentNode;
}
return scrollable;
}
document.addEventListener('DOMContentLoaded', function() {
document.body.addEventListener('touchmove', function(event) {
var owner = findNearestScrollableParent(event.target);
if (!owner || owner === document.documentElement || owner === document.body) {
event.preventDefault();
}
});
}, false);
At this point, the body is no longer scrollable or elastically scrollable in iOS, but children elements are. You can then add -webkit-overflow-scrolling: touch;
to those children so they are elastic but the document wont be. This will actually capture all scroll events even as you scroll to the bottom of the children, so the window's scroll position wont ever change erroneously. Alternatively you may also consider:
['resize', 'orientationchange', 'scroll'].forEach(function(event) {
window.addEventListener(event, function() {
window.scrollTo(0, 0);
});
});
which in addition to the first code block I shared, should really throw an axe at document scrolling in ios altogether.