deshabilitar el desplazamiento hinchable solo para html pero mantener los elementos con desbordamiento: scroll

Estoy creando una aplicación web de pantalla completa que tendrá algunos módulos / widgets que hacen uso del nuevo desbordamiento de iOS 5: funciones de desplazamiento. Lo que quiero es deshabilitar ese efecto de 'rebote' al desplazar el html / body (ya que es pantalla completa) pero mantener ese efecto solo en los elementos desplazables.

para suavizar los efectos de los elementos desplazables tengo:

html, body { overflow: hidden; }

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

y luego el siguiente script que deshabilita el efecto de desplazamiento táctil:

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

aunque esto no parece funcionar en absoluto, porque cuando se desplaza un elemento hasta el extremo inferior o superior, también desplaza el documentElement.

¿Hay alguna forma de deshabilitar solo ese efecto para el elemento html del cuerpo?

Aquí hay un buen ejemplo de cómo esto afecta la funcionalidad:

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

preguntado el 08 de noviembre de 11 a las 15:11

No creo que su controlador de eventos llegue a preventDefault () porque target es el elemento que desencadenó el evento, que nunca puede ser document.documentElement ( ). Entonces, está viendo el mismo comportamiento que si no tuviera el controlador de eventos. -

eso sería currentTarget Me temo que. el objetivo puede devolver cualquier cosa dentro de él, es posible que desee intentarlo;) -

currentTarget devuelve el elemento con el oyente, no el elemento que lanzó el evento. -

Ah, veo tu punto, eso es correcto. Lo intenté con el cuerpo también sin éxito. Jugaré con el código de tu respuesta y te haré saber cómo va, gracias tío.

Estaba escribiendo esta demostración antes de tu último comentario: jsfiddle.net/ThinkingStiff/3TkuF -

3 Respuestas

Es una pena que -webkit-overflow-scrolling no maneja esto mejor. Necesitas rastrear el y posición para que funcione. Pongo la clase scroll en cualquier cosa que quiera desplazarme en mi página, como <ul> elementos. Envuelva un <div> alrededor del <ul> que llena la ventana con overflow-y: auto. No pongas overflow or height al <ul>. El <ul> se expandirá tan alto como su contenido y es el <div> que en realidad está haciendo el desplazamiento. -webkit-overflow-scrolling se hereda, así que colóquelo tan arriba en el DOM como desee.

Demostración: http://jsfiddle.net/ThinkingStiff/FDqH7/

Guión:

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%;
}

Respondido el 27 de diciembre de 11 a las 13:12

Probé tu código con #viewport como un div posicionado aboslute con overflow: auto y muchos lorem ipsun por dentro y lo que está sucediendo actualmente es que el script deshabilitó el desplazamiento de viewport mientras deja el desplazamiento hinchable en el cuerpo. - Zanona

Ponga un div dentro de #viewport y ponga el texto dentro de eso. No establezca overflow en el div interno, pero coloque un "scroll" de clase en él si está usando el código anterior. La configuración del recorrido para # viewport es correcta para el desbordamiento y la posición. - Pensando rígido

@ludicco Actualicé mi respuesta y creé una demostración para dejarla más clara. He estado usando esto con éxito durante algunos meses. - Pensando rígido

Aquí hay una respuesta similar a ThinkingStiff, excepto que no exige su estructura html. Busca cualquier elemento que tenga contenido desbordado y permite el desplazamiento solo cuando el usuario está interactuando con ellos.

Desventajas:

  • Una vez que el usuario alcanza el límite superior o inferior del nodo desplazable, no se producirá el rebote dentro de ese nodo (pero lo hará si se desplaza desde abajo o por encima de los límites). Esto probablemente significa que no satisface sus requisitos de extracción para actualizar :(

  • Hay una extraña diferencia de 2px que noté en mis casos de prueba al calcular las compensaciones de desplazamiento. No estoy seguro de dónde vino eso, y es posible que deba modificar el valor

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()

Respondido 18 Feb 12, 09:02

CoffeeScript (se compila en JavaScript, es mucho más conveniente de escribir): coffeescript.org - nieve

Solución al problema de 2px - the jquery height() La función no tiene en cuenta el relleno. Cambio nodeHeight = node$.height() a nodeHeight = node$.outerHeight() y tu problema está resuelto :) - Yuval Karmi

Maldiciones! Gracias por el consejo :) - nieve

Para mí resulta que la única solución eficaz era utilizar joelambert / ScrollFix script, funcionó muy bien sin retrasos, de hecho ya lo estoy usando en uno de mis proyectos.

También puede consultarlo en más detalles en su entrada de blog. Lo siento por los otros usuarios que respondieron, pero realmente no obtuve esas respuestas para mí.

Respondido 26 ago 12, 11:08

No es la respuesta que estás buscando? Examinar otras preguntas etiquetadas or haz tu propia pregunta.