禁用仅用于html的弹性滚动,但维护具有溢出的元素:滚动

时间:2011-11-08 15:33:14

标签: javascript css webkit ios5 overflow

我正在创建一个全屏网络应用程序,它将包含一些模块/小部件,这些模块/小部件利用新的iOS 5溢出:滚动功能。 我想要的是在滚动html / body时禁用“弹性”效果(因为它是全屏)但是只在可滚动元素上保留该效果。

平滑我可滚动元素的效果:

html, body { overflow: hidden; }

.scrollable {
    overflow: scroll;
    -webkit-overflow-scrolling: touch;
}

然后是以下禁用触摸滚动效果的脚本:

$(document).bind('touchmove', function (e) { 
    if (e.target === document.documentElement) {
        e.preventDefault(); 
    }
});

虽然这似乎根本不起作用,因为当将元素滚动到最底端或顶部时,它也会滚动documentElement

有没有办法只为body html元素禁用该效果?

这是影响功能的一个很好的例子:

http://dl.dropbox.com/u/1928164/ios5/index.html

3 个答案:

答案 0 :(得分:2)

令人遗憾的是-webkit-overflow-scrolling没有更好地处理这个问题。您需要跟踪y位置才能使其正常工作。我将课程scroll放在我要在页面上滚动的任何内容上,例如<ul>元素。使用<div>在填充视口的<ul>周围包裹overflow-y: auto。不要将overflowheight放在<ul>上。 <ul>将扩展为与其内容一样高,并且它是实际进行滚动的<div>-webkit-overflow-scrolling是继承的,所以把它放在你想要的DOM上。

演示:http://jsfiddle.net/ThinkingStiff/FDqH7/

脚本:

var swipeY = 0;

function onTouchMove( event ) {

    var scroll = event.target.closestByClassName( 'scroll' );

    if ( scroll ) {

        var top = scroll.positionTop - scroll.parentNode.positionTop,
            heightDifference = ( 0 - scroll.offsetHeight + scroll.parentNode.offsetHeight );

        if( ( top >= 0 ) && ( event.touches[0].screenY > swipeY ) ) { 
            event.preventDefault(); //at top, swiping down
        } else if( ( top <= heightDifference ) && ( event.touches[0].screenY < swipeY ) ) { 
            event.preventDefault(); //at bottom, swiping up
        };

    } else {
        event.preventDefault();
    };

};

function onTouchStart( event ) {

    swipeY = event.touches[0].screenY;

};

Element.prototype.closestByClassName = function ( className ) {

    return this.className && this.className.split( ' ' ).indexOf( className ) > -1
        ? this
        : ( this.parentNode.closestByClassName && this.parentNode.closestByClassName( className ) );

};

window.Object.defineProperty( Element.prototype, 'positionTop', {

    get: function () { 
        return this.offsetTop - this.parentNode.scrollTop;
    }

} );

document.getElementById( 'viewport' ).addEventListener( 'touchmove', onTouchMove, false );
document.getElementById( 'viewport' ).addEventListener( 'touchstart', onTouchStart, false );

HTML:

<div id="viewport">
<div id="scroll-view">
    <ul class="scroll">
        <li>scroll scroll scroll scroll scroll </li>
        <li>scroll scroll scroll scroll scroll </li>
        <li>scroll scroll scroll scroll scroll </li>

        . . .

    </ul>
</div>
</div>

CSS:

#viewport {
    border: 1px solid black;
    height: 460px;
    width: 320px;
    -webkit-overflow-scrolling: touch;
}

#scroll-view {
    height: 100%;
    overflow-y: auto;
    width: 100%;
}

答案 1 :(得分:2)

这是ThinkingStiff的类似答案,除了它不强制你的html结构。它会查找溢出内容的任何元素,并仅在用户与它们交互时才启用滚动。

缺点:

  • 一旦用户点击可滚动节点的顶部或底部限制,就不会在该节点内弹跳(但如果您从限制下方/上方轻弹,它将会发生)。这可能意味着它不能满足您对刷新的要求:(

  • 在计算滚动偏移时,我在测试用例中注意到了一个奇怪的2px差异。不确定它来自何处,您可能需要调整值

CoffeeScript的:

# Vertical scrolling behavior overrides.
#
# This disables vertical scrolling on the page for touch devices, unless the user is scrolling
# within an overflowed node.  This requires some finessing of the touch events.
#
# **NOTE:** This code ends up disabling bounce behavior if the user tries to scroll on a node that
# is already at its upper or lower limit.
window$   = $(window)
initialY  = null
nodeStack = []

# When a user begins a (potential) drag, we jot down positional and node information.
#
# The assumption is that page content isn't going to move for the duration of the drag, and that
# it would also be awkward if the drag were to change/stop part way through due to DOM
# modifications.
window$.bind 'touchstart', (evt) ->
  initialY  = evt.originalEvent.pageY
  nodeStack = $(evt.target).parents().andSelf().filter(':not(body, html)').get().reverse()
  nodeStack = nodeStack.map (node) -> $(node)

window$.bind 'touchend touchcancel', (evt) ->
  initialY  = null
  nodeStack = []

# We override the `touchmove` event so that we only allow scrolls in allowable directions,
# depending on where the user first began the drag.
window$.bind 'touchmove', (evt) ->
  return evt.preventDefault() if initialY == null
  # A positive direction indicates that the user is dragging their finger down, thus wanting the
  # content to scroll up.
  direction = evt.originalEvent.pageY - initialY

  for node$ in nodeStack
    nodeHeight    = node$.height()
    # For some reason, the node's scrollHeight is off by 2 pixels in all cases.  This may require
    # tweaking depending on your DOM.  Concerning.
    scrollHeight  = node$[0].scrollHeight - 2
    nodeScrollTop = node$.scrollTop()

    # If we have a scrollable element, we want to only allow drags under certain circumstances:
    if scrollHeight > nodeHeight
      # * The user is dragging the content up, and the element is already scrolled down a bit.
      return if direction > 0 and nodeScrollTop > 0
      # * And the reverse: the user is dragging the content down, and the element is up a bit.
      return if direction < 0 and nodeScrollTop < scrollHeight - nodeHeight

  # Otherwise, the default behavior is to disable dragging.
  evt.preventDefault()

答案 2 :(得分:0)

对我而言,唯一有效的解决方案是使用joelambert/ScrollFix脚本,在没有滞后的情况下工作得非常好,事实上我已经在我的一个项目中使用它了。

您还可以在his blog post更详细地查看相关信息。 很抱歉其他用户回复了但我真的没有得到那些为我工作的答案。