Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

iOS Safari – How to disable overscroll but allow scrollable divs to scroll normally?

I'm working on an iPad-based web app, and need to prevent overscrolling so that it seems less like a web page. I'm currently using this to freeze the viewport and disable overscroll:

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

This works great to disable overscroll but my app has several scrollable divs, and the above code prevents them from scrolling.

I'm targeting iOS 5 and above only so I've avoided hacky solutions like iScroll. Instead I'm using this CSS for my scrollable divs:

.scrollable {     -webkit-overflow-scrolling: touch;     overflow-y:auto; } 

This works without the document overscroll script, but doesn't solve the div scrolling problem.

Without a jQuery plugin, is there any way to use the overscroll fix but exempt my $('.scrollable') divs?

EDIT:

I found something that's a decent solution:

 // Disable overscroll / viewport moving on everything but scrollable divs  $('body').on('touchmove', function (e) {          if (!$('.scrollable').has($(e.target)).length) e.preventDefault();  }); 

The viewport still moves when you scroll past the beginning or end of the div. I'd like to find a way to disable that as well.

like image 540
Jeff Avatar asked Apr 19 '12 23:04

Jeff


People also ask

How to prevent overscroll on iOS?

Handling iOS Safari 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.

Can I use Overscroll Behaviour?

You can use overscroll-behavior to get rid of unwanted scroll chaining and the browser's Facebook/Twitter app-inspired "pull to refresh"-type behavior. Note that this property only applies to scroll containers.

How do I stop extra scrolling?

To do this (in Chrome, Firefox, and Edge), we can add the CSS property overscroll-behavior: contain to the overflow: auto element. This will prevent the "scroll chaining" behavior, which will, in turn, keep the mouse-wheel active within the target element.


2 Answers

This solves the issue when you scroll past the beginning or end of the div

var selScrollable = '.scrollable'; // Uses document because document will be topmost level in bubbling $(document).on('touchmove',function(e){   e.preventDefault(); }); // Uses body because jQuery on events are called off of the element they are // added to, so bubbling would not work if we used document instead. $('body').on('touchstart', selScrollable, function(e) {   if (e.currentTarget.scrollTop === 0) {     e.currentTarget.scrollTop = 1;   } else if (e.currentTarget.scrollHeight === e.currentTarget.scrollTop + e.currentTarget.offsetHeight) {     e.currentTarget.scrollTop -= 1;   } }); // Stops preventDefault from being called on document if it sees a scrollable div $('body').on('touchmove', selScrollable, function(e) {   e.stopPropagation(); }); 

Note that this won't work if you want to block whole page scrolling when a div does not have overflow. To block that, use the following event handler instead of the one immediately above (adapted from this question):

$('body').on('touchmove', selScrollable, function(e) {     // Only block default if internal div contents are large enough to scroll     // Warning: scrollHeight support is not universal. (https://stackoverflow.com/a/15033226/40352)     if($(this)[0].scrollHeight > $(this).innerHeight()) {         e.stopPropagation();     } }); 
like image 68
Tyler Dodge Avatar answered Oct 20 '22 00:10

Tyler Dodge


Using Tyler Dodge's excellent answer kept lagging on my iPad, so I added some throttling code, now it's quite smooth. There is some minimal skipping sometimes while scrolling.

// Uses document because document will be topmost level in bubbling $(document).on('touchmove',function(e){   e.preventDefault(); });  var scrolling = false;  // Uses body because jquery on events are called off of the element they are // added to, so bubbling would not work if we used document instead. $('body').on('touchstart','.scrollable',function(e) {      // Only execute the below code once at a time     if (!scrolling) {         scrolling = true;            if (e.currentTarget.scrollTop === 0) {           e.currentTarget.scrollTop = 1;         } else if (e.currentTarget.scrollHeight === e.currentTarget.scrollTop + e.currentTarget.offsetHeight) {           e.currentTarget.scrollTop -= 1;         }         scrolling = false;     } });  // Prevents preventDefault from being called on document if it sees a scrollable div $('body').on('touchmove','.scrollable',function(e) {   e.stopPropagation(); }); 

Also, adding the following CSS fixes some rendering glitches (source):

.scrollable {     overflow: auto;     overflow-x: hidden;     -webkit-overflow-scrolling: touch; } .scrollable * {     -webkit-transform: translate3d(0,0,0); } 
like image 44
Kuba Holuj Avatar answered Oct 20 '22 00:10

Kuba Holuj