деактивиране на подскачащо превъртане само за html, но поддържане за елементи с overflow:scroll

Създавам уеб приложение на цял екран, което ще има някои модули/джаджи, които използват новите функции за препълване: превъртане на 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.

Има ли някакъв начин да деактивирате този ефект само за html елемента body?

Ето добър пример как това се отразява на функционалността:

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


person zanona    schedule 08.11.2011    source източник
comment
Не мисля, че вашият манипулатор на събития някога ще удари preventDefault(), защото target е елементът, който е задействал събитието, което никога не може да бъде document.documentElement (‹html›). Така че виждате същото поведение, както ако нямате манипулатора на събития.   -  person ThinkingStiff    schedule 11.11.2011
comment
това би било currentTarget страхувам се. target може да върне всичко вътре в него, може да искате да опитате;)   -  person zanona    schedule 11.11.2011
comment
currentTarget връща елемента със слушателя, а не елемента, който е хвърлил събитието.   -  person ThinkingStiff    schedule 11.11.2011
comment
разбирам мисълта ти, това е правилно. Опитах и ​​с тялото без успех. Ще си поиграя с кода на вашия отговор и ще ви уведомя как върви, благодаря, човече   -  person zanona    schedule 11.11.2011
comment
Тъкмо пишех тази демонстрация преди последния ви коментар: jsfiddle.net/ThinkingStiff/3TkuF   -  person ThinkingStiff    schedule 11.11.2011


Отговори (3)


Жалко е, че -webkit-overflow-scrolling не се справя по-добре с това. Трябва да проследите позицията y, за да работи. Поставям класа scroll на всичко, което искам да превъртя на моята страница, като например <ul> елементи. Увийте <div> около <ul>, което запълва прозореца за изглед с overflow-y: auto. Не поставяйте overflow или height на <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%;
}
person ThinkingStiff    schedule 10.11.2011
comment
Опитах вашия код с #viewport като aboslute позициониран div с overflow: auto и много lorem ipsun вътре и това, което се случва в момента е, че скриптът деактивира превъртането на viewport, като същевременно остави подскачащото превъртане в тялото . - person zanona; 11.11.2011
comment
Поставете div вътре в #viewport и поставете текста вътре в него. Не задавайте препълване на вътрешния div, но поставете превъртане на клас върху него, ако използвате горния код. Настройките на обиколката за #viewport са правилни за преливане и позиция. - person ThinkingStiff; 12.11.2011
comment
@ludicco Актуализирах отговора си и създадох демонстрация, за да го направя по-ясен. Използвам това успешно от няколко месеца. - person ThinkingStiff; 27.12.2011

Ето отговор, подобен на този на 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()
person Nevir    schedule 18.02.2012
comment
CoffeeScript (компилира се до JavaScript - просто е много по-удобен за писане): coffeescript.org - person Nevir; 19.02.2012
comment
Решение на проблема с 2px -- функцията jquery height() не взема предвид подложките. Променете nodeHeight = node$.height() на nodeHeight = node$.outerHeight() и проблемът ви е решен :) - person Yuval Karmi; 26.05.2012
comment
проклятия! Благодаря за бакшиша :) - person Nevir; 26.05.2012

За мен се оказа, че единственото ефективно решение беше да използвам joelambert/ScrollFix скрипт, работеше много добре без закъснения, всъщност вече го използвам в един от моите проекти.

Можете също да проверите за това с повече подробности на публикацията му в блог. Съжалявам за другите потребители, които отговориха, но наистина не получих тези отговори, за да работят за мен.

person zanona    schedule 26.08.2012