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

后端 未结 14 1524
南方客
南方客 2020-11-28 17:50

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

相关标签:
14条回答
  • 2020-11-28 18:13

    This solution doesn't require you to put a scrollable class on all your scrollable divs so is more general. Scrolling is allowed on all elements which are, or are children of, INPUT elements contenteditables and overflow scroll or autos.

    I use a custom selector and I also cache the result of the check in the element to improve performance. No need to check the same element every time. This may have a few issues as only just written but thought I'd share.

    $.expr[':'].scrollable = function(obj) {
        var $el = $(obj);
        var tagName = $el.prop("tagName");
        return (tagName !== 'BODY' && tagName !== 'HTML') && (tagName === 'INPUT' || $el.is("[contentEditable='true']") || $el.css("overflow").match(/auto|scroll/));
    };
    function preventBodyScroll() {
        function isScrollAllowed($target) {
            if ($target.data("isScrollAllowed") !== undefined) {
                return $target.data("isScrollAllowed");
            }
            var scrollAllowed = $target.closest(":scrollable").length > 0;
            $target.data("isScrollAllowed",scrollAllowed);
            return scrollAllowed;
        }
        $('body').bind('touchmove', function (ev) {
            if (!isScrollAllowed($(ev.target))) {
                ev.preventDefault();
            }
        });
    }
    
    0 讨论(0)
  • 2020-11-28 18:14

    While disabling all "touchmove" events might seem like a good idea, as soon as you need other scrollable elements on the page it will cause problems. On top of that, if you only disable "touchmove" events on certain elements (e.g. body if you want the page to be non-scrollable), as soon as it is enabled anywhere else, IOS will cause unstoppable propagation in Chrome when the URL bar toggles.

    While I cannot explain this behavior, it looks like the only way to prevent seems to set the body's position to fixed. The only problem doing is that you will lose the position of the document - this is especially annoying in modals for example. One way to solve it would be to use these simple VanillaJS functions:

    function disableDocumentScrolling() {
        if (document.documentElement.style.position != 'fixed') {
            // Get the top vertical offset.
            var topVerticalOffset = (typeof window.pageYOffset != 'undefined') ?
                window.pageYOffset : (document.documentElement.scrollTop ? 
                document.documentElement.scrollTop : 0);
            // Set the document to fixed position (this is the only way around IOS' overscroll "feature").
            document.documentElement.style.position = 'fixed';
            // Set back the offset position by user negative margin on the fixed document.
            document.documentElement.style.marginTop = '-' + topVerticalOffset + 'px';
        }
    }
    
    function enableDocumentScrolling() {
        if (document.documentElement.style.position == 'fixed') {
            // Remove the fixed position on the document.
            document.documentElement.style.position = null;
            // Calculate back the original position of the non-fixed document.
            var scrollPosition = -1 * parseFloat(document.documentElement.style.marginTop);
            // Remove fixed document negative margin.
            document.documentElement.style.marginTop = null;
            // Scroll to the original position of the non-fixed document.
            window.scrollTo(0, scrollPosition);
        }
    }
    

    Using this solution you can have a fixed document and any other element in your page can overflow by using simple CSS (e.g., overflow: scroll;). No need for special classes or anything else.

    0 讨论(0)
  • 2020-11-28 18:16

    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();
        }
    });
    
    0 讨论(0)
  • 2020-11-28 18:16

    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>
    
    0 讨论(0)
  • 2020-11-28 18:17

    I was looking for a way to prevent all body scrolling when there's a popup with a scrollable area (a "shopping cart" popdown that has a scrollable view of your cart).

    I wrote a far more elegant solution using minimal javascript to just toggle the class "noscroll" on your body when you have a popup or div that you'd like to scroll (and not "overscroll" the whole page body).

    while desktop browsers observe overflow:hidden -- iOS seems to ignore that unless you set the position to fixed... which causes the whole page to be a strange width, so you have to set the position and width manually as well. use this css:

    .noscroll {
        overflow: hidden;
        position: fixed;
        top: 0;
        left: 0;
        width: 100%;
    }
    

    and this jquery:

    /* fade in/out cart popup, add/remove .noscroll from body */
    $('a.cart').click(function() {
        $('nav > ul.cart').fadeToggle(100, 'linear');
        if ($('nav > ul.cart').is(":visible")) {
            $('body').toggleClass('noscroll');
        } else {
            $('body').removeClass('noscroll');
        }
    });
    
    /* close all popup menus when you click the page... */
    $('body').click(function () {
        $('nav > ul').fadeOut(100, 'linear');
        $('body').removeClass('noscroll');
    });
    
    /* ... but prevent clicks in the popup from closing the popup */
    $('nav > ul').click(function(event){
        event.stopPropagation();
    });
    
    0 讨论(0)
  • 2020-11-28 18:17

    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

    0 讨论(0)
提交回复
热议问题