防止整页滚动iOS



在Mobile Safari下,是否有可能允许一个绝对定位的div滚动而不允许整个页面在滚动到达边缘时上下摆动(弹性滚动)?

下面是我所面临的问题的一个最小的工作示例:

<!doctype html>
<html>
<head>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width, minimum-scale=1.0, maximum-scale=1.0, user-scalable=no">
    <meta name="apple-mobile-web-app-capable" content="yes" />
    <style>
        * {
            margin: 0;
            padding: 0;
            box-sizing: border-box;
        }
        #a, #b {
            position: absolute;
            top: 0;
            left: 0;
            height: 100%;
            padding: 10px;
            overflow: auto;
        }
        #a {
            width: 80px;
            background: #f00;
        }
        #b {
            background: #00f;
            left: 80px;
            width: 100%;
        }
    </style>
    <script src="http://code.jquery.com/jquery-1.10.1.min.js"></script>
    <script>
        function pdcb(e) {
            e.preventDefault();
        }
        function npcb(e) {
            e.stopPropagation();
        }
        $(document).on('touchstart touchmove', pdcb).
                    on('touchstart touchmove', '.scrollable', npcb);
    </script>
</head>
<body>
    <div id="a" class="scrollable">
        This<br>
        should<br>
        be<br>
        scrollable<br>
        but<br>
        not<br>
        scroll<br>
        the<br>
        whole<br>
        page<br>
        This<br>
        should<br>
        be<br>
        scrollable<br>
        but<br>
        not<br>
        scroll<br>
        the<br>
        whole<br>
        page<br>
        This<br>
        should<br>
        be<br>
        scrollable<br>
        but<br>
        not<br>
        scroll<br>
        the<br>
        whole<br>
        page<br>
        This<br>
        should<br>
        be<br>
        scrollable<br>
        but<br>
        not<br>
        scroll<br>
        the<br>
        whole<br>
        page<br>
        This<br>
        should<br>
        be<br>
        scrollable<br>
        but<br>
        not<br>
        scroll<br>
        the<br>
        whole<br>
        page<br>
    </div>
    <div id="b">
        this should never scroll
    </div>
</body>
</html>

解决方案:

$(document).on('touchmove', function(e) {
    e.preventDefault();
}).ready(function() {
    $(".scrollable").on('touchstart', function(e) {
        this.allowUp = (this.scrollTop > 0);
        this.allowDown = (this.scrollTop < this.scrollHeight - this.clientHeight);
        this.prevTop = null;
        this.prevBot = null;
        this.lastY = e.originalEvent.pageY;
    }).on('touchmove', function(e) {
        var event = e.originalEvent;
        var up = (event.pageY > this.lastY), down = !up;
        this.lastY = event.pageY;
        if ((up && this.allowUp) || (down && this.allowDown))
            event.stopPropagation();
        else
            event.preventDefault();
    });
});

最初的答案非常棒,但有一些缺陷,我解决了:

  • 如果一个元素位于顶部或底部,它将不会分别向上和向下滚动。
  • 如果一个元素是动态添加的,它不会有滚动处理程序。
  • 有未使用的变量(prevTop, prevBot)

我的回答解决了这些问题。(注意,我使用.scroll-y,而不是.scrollable)

首先,添加这些CSS规则:
.scroll-y {
  overflow-y: auto;
  overflow-x: hidden;
  -webkit-overflow-scrolling: touch; /* nice webkit native scroll */
}

.scroll-y类添加到您想要滚动的任何元素。

然后,在某处添加这个JS:

// Disable scroll for the document, we'll handle it ourselves
$(document).on('touchmove', function(e) {
  e.preventDefault();
});
// Check if we should allow scrolling up or down
$(document.body).on("touchstart", ".scroll-y", function (e) {
  // If the element is scrollable (content overflows), then...
  if (this.scrollHeight !== this.clientHeight) {
    // If we're at the top, scroll down one pixel to allow scrolling up
    if (this.scrollTop === 0) {
      this.scrollTop = 1;
    }
    // If we're at the bottom, scroll up one pixel to allow scrolling down
    if (this.scrollTop === this.scrollHeight - this.clientHeight) {
      this.scrollTop = this.scrollHeight - this.clientHeight - 1;
    }
  }
  // Check if we can scroll
  this.allowUp = this.scrollTop > 0;
  this.allowDown = this.scrollTop < (this.scrollHeight - this.clientHeight);
  this.lastY = e.originalEvent.pageY;
});
$(document.body).on('touchmove', ".scroll-y", function(e) {
  var event = e.originalEvent;
  var up = event.pageY > this.lastY;
  var down = !up;
  this.lastY = event.pageY;
  if ((up && this.allowUp) || (down && this.allowDown)) {
    event.stopPropagation();
  } else {
    event.preventDefault();
  }
});

虽然您没有触及div内容的边缘,但您需要允许本地touchmove事件在该元素上工作(因此它可以滚动),但是您将希望阻止事件冒泡DOM,以便它不会触发页面主体上的滚动。

当你触及元素的边界时,你需要阻止原生动量完全滚动

我使用的代码如下(向原作者道歉,这是改编自我过去在互联网上找到的关于这个主题的教程…现在好像找不到URL了):

你elem是DOM节点

elem.addEventListener('touchstart', function(event){
    this.allowUp = (this.scrollTop > 0);
    this.allowDown = (this.scrollTop < this.scrollHeight - this.clientHeight);
    this.prevTop = null; this.prevBot = null;
    this.lastY = event.pageY;
});
elem.addEventListener('touchmove', function(event){
    var up = (event.pageY > this.lastY), down = !up;
    this.lastY = event.pageY;
    if ((up && this.allowUp) || (down && this.allowDown)) event.stopPropagation();
    else event.preventDefault();
});

我通常定义一个元素数组并遍历它们——将此代码迭代地应用于每个元素。

祝你好运,希望这对你有帮助。

Aaron Grey提供的说明很有帮助!

见链接:http://blog.christoffer.me/six-things-i-learnt-about-ios-safaris-rubber-band-scrolling/

<!doctype html>
<html lang="en">
<head>
    <meta charset="utf-8">
    <meta name="viewport" content="minimum-scale=1.0, width=device-width, maximum-scale=1.0, user-scalable=no, initial-scale=1">
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
    <style>
        .page{
            font-size: 24px;
            overflow: scroll;
        }
        .menu{
            position: fixed;
            top: 0;
            bottom: 0;
            left: 0;
            width: 80%;
            background: gray;
            z-index: 1;
            font-size: 10px;
            overflow: scroll;
            /* uncomment to get smooth momentum scroll, but also a rubber band effect */
            /*-webkit-overflow-scrolling: touch;*/
        }
        .menu-item{
            padding: 10px;
            background: darkgray;
            font-size: 24px;
        }
    </style>
</head>
<body>
<div class="menu scrollable">
    <div class="menu-item">hello world</div>
    <div class="menu-item">hello world</div>
    <div class="menu-item">hello world</div>
    <div class="menu-item">hello world</div>
    <div class="menu-item">hello world</div>
    <div class="menu-item">hello world</div>
    <div class="menu-item">hello world</div>
    <div class="menu-item">hello world</div>
    <div class="menu-item">hello world</div>
    <div class="menu-item">hello world</div>
    <div class="menu-item">hello world</div>
    <div class="menu-item">hello world</div>
    <div class="menu-item">hello world</div>
    <div class="menu-item">hello world</div>
    <div class="menu-item">hello world</div>
    <div class="menu-item">hello world</div>
    <div class="menu-item">hello world</div>
    <div class="menu-item">hello world</div>
</div>
<div class="page disable-scrolling">
    Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's
    standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make
    a type specimen book. It has survived not only five centuries, but also the leap into electronic typesetting,
    remaining essentially unchanged. It was popularised in the 1960s with the release of Letraset sheets containing
    Lorem Ipsum passages, and more recently with desktop publishing software like Aldus PageMaker including versions
    of Lorem Ipsum.
</div>
<script>

    document.ontouchmove = function ( event ) {
        var isTouchMoveAllowed = true, target = event.target;
        while ( target !== null ) {
            if ( target.classList && target.classList.contains( 'disable-scrolling' ) ) {
                isTouchMoveAllowed = false;
                break;
            }
            target = target.parentNode;
        }
        if ( !isTouchMoveAllowed ) {
            event.preventDefault();
        }
    };
    function removeIOSRubberEffect( element ) {
        element.addEventListener( "touchstart", function () {
            var top = element.scrollTop, totalScroll = element.scrollHeight, currentScroll = top + element.offsetHeight;
            if ( top === 0 ) {
                element.scrollTop = 1;
            } else if ( currentScroll === totalScroll ) {
                element.scrollTop = top - 1;
            }
        } );
    }
    removeIOSRubberEffect( document.querySelector( ".scrollable" ) );

</script>
</body>
</html>

最新更新