iOS Safari – How to disable overscroll but allow s

2019-01-05 07:24发布

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.

14条回答
Lonely孤独者°
2楼-- · 2019-01-05 07:40

this one works for me (plain javascript)

var fixScroll = function (className, border) {  // className = class of scrollElement(s), border: borderTop + borderBottom, due to offsetHeight
var reg = new RegExp(className,"i"); var off = +border + 1;
function _testClass(e) { var o = e.target; while (!reg.test(o.className)) if (!o || o==document) return false; else o = o.parentNode; return o;}
document.ontouchmove  = function(e) { var o = _testClass(e); if (o) { e.stopPropagation(); if (o.scrollTop == 0) { o.scrollTop += 1; e.preventDefault();}}}
document.ontouchstart = function(e) { var o = _testClass(e); if (o && o.scrollHeight >= o.scrollTop + o.offsetHeight - off) o.scrollTop -= off;}
}

fixScroll("fixscroll",2); // assuming I have a 1px border in my DIV

html:

<div class="fixscroll" style="border:1px gray solid">content</div>
查看更多
smile是对你的礼貌
3楼-- · 2019-01-05 07:48

First prevent default actions on your entire document as usual:

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

Then stop your class of elements from propagating to the document level. This stops it from reaching the function above and thus e.preventDefault() is not initiated:

$('.scrollable').bind('touchmove', function(e){
  e.stopPropagation();
});

This system seems to be more natural and less intensive than calculating the class on all touch moves. Use .on() rather than .bind() for dynamically generated elements.

Also consider these meta tags to prevent unfortunate things from happening while using your scrollable div:

<meta content='True' name='HandheldFriendly' />
<meta content='width=device-width, initial-scale=1, maximum-scale=1, user-scalable=0' name='viewport' />
<meta name="viewport" content="width=device-width" />
查看更多
放我归山
4楼-- · 2019-01-05 07:49

Here's a zepto compatible solution

    if (!$(e.target).hasClass('scrollable') && !$(e.target).closest('.scrollable').length > 0) {
       console.log('prevented scroll');
       e.preventDefault();
       window.scroll(0,0);
       return false;
    }
查看更多
甜甜的少女心
5楼-- · 2019-01-05 07:52

Best solution to this is css/html: Make a div to wrap your elements in, if you dont have it already And set it to position fixed and overflow hidden. Optional, set height and width to 100% if you want it to fill the whole screen and nothing but the whole screen

#wrapper{
  height: 100%;
  width: 100%;
  position: fixed;
  overflow: hidden;
}
<div id="wrapper">
  <p>All</p>
  <p>Your</p>
  <p>Elements</p>
</div>

查看更多
Juvenile、少年°
6楼-- · 2019-01-05 07:53

I've work a little workarround without jquery. Not perfert but works fine (especially if you have a scroll-x in a scoll-y) https://github.com/pinadesign/overscroll/

Fell free to participate and improve it

查看更多
Juvenile、少年°
7楼-- · 2019-01-05 07:55

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();
    }
});
查看更多
登录 后发表回答