web-dev-qa-db-ja.com

iOSでのオーバーフロー/ラバーバンドスクロールの防止

SOでのオーバーフロー/ゴムバンドのスクロールのトピックに関する複数の質問が既にありますが、

  1. いずれも、iOS 9.3.2のすべてのケースで機能するソリューションを提供しません
  2. それらのどれも問題自体に関する広範囲で完全な情報を提供しません

それが、私がこの記事を知識体系として作成した理由です。


問題:

他の投稿で決して言及されなかったことは、iOSオーバーフロースクロールは実際には2つの部分の動作であるということです。

1. overflow: auto/scrollを使用したコンテンツのスクロールオーバーフロー

これは、-webkit-overflow-scrolling: touchを持つ要素の一般的に知られ、最も望まれている動作であり、連続/運動量のスクロール動作が要素コンテナを通過して、スクロールされたコンテンツをスムーズに遅くします。

運動量スクロールがスクロールされたコンテンツの長さを超えるのに十分高い運動量を持つ要素のコンテンツをスクロールすると発生します。

この動作により、element.scrollTopプロパティは要素のスクロール位置に応じて変化し、0未満または最大スクロール(element.scrollHeight - element.offsetHeight)より大きくなります。

2. <body>のオーバーフロースクロール

この動作は、既に最小/最大スクロール位置にある要素(上にある要素または下にある要素)をさらにスクロールしようとすると発生します。次に、スクロールは<body>タグまで「バブルアップ」しているように見え、ビューポート全体がスクロールされます。

上記とは異なり、element.scrollTopプロパティは変わりませんが、document.body.scrollTopプロパティは変わります。

フォーカスロックと動作間の切り替え(1.5秒の遅延)

このコンテキストで最も苛立たしいのは、上記の2つのタイプの切り替えがすぐに切り替わらないことです。

両方のいずれかを入力した後、他の要素(スクロール可能な要素、ボタン、リンクなど)にフォーカスを切り替えることはできません。そのため、スクロール動作も変更されません。

たとえば:既に最上部にある要素を上にスクロールする場合は、overflow scrolling type 2と入力します。ユーザーにとって最も自然な反応は、下にスクロールしてみることです。フォーカスはoverflow scrolling type 1に移動する代わりにボディスクロールにロックされているため、type 2に留まり、ボディ全体が下にスクロールされます。その後、一般的なユーザーは、type 2から抜け出すことなく、頻繁に上下に頻繁にスクロールを開始します。

フォーカスの切り替え、したがってスクロール動作の変更は、オーバーフローアニメーションが終了し、要素が静止した後にのみ発生します(それよりも少し長い[約0.5秒])。

したがって、前の例に戻ると、ユーザーの正しい反応は、約1秒から1.5秒の間画面へのタッチを停止し、再び下にスクロールしようとすることです。

16
Aides

ソリューション:

タイプ1:

要素自体のオーバーフロースクロールを防ぐ最も基本的な解決策は、タッチイベントのデフォルトを防ぐことです。

document.body.addEventListener('touchmove', function(e) { 
    e.preventDefault(); 
});

ただし、このメソッドはブラウザーのネイティブのモーメンタムスクロールを無効にするため、ほとんどのアプリケーションには適していません。ただし、いくつかの改良を加えて(上にスクロールする場合、または下にスクロールする場合にのみ防止)、この方法はほとんどの問題を修正します。多くの可能な実装は this SO post にあります。

タイプ2:

ただし、本文のオーバーフロースクロールは、上記の方法で防止されません。

合理的と思われる解決策の1つは、 前述の質問 の最良の解決策として説明されているように、要素が最上部または最下部の位置にならないようにすることです。

anElement.addEventListener('touchstart', function( event ){
    if( this.scrollTop === 0 ) {
        this.scrollTop += 1;
    } else if( this.scrollTop + this.offsetHeight >= this.scrollHeight ) {
        this.scrollTop -= 1;
    }
}

ただし、これはiOS 9.3.2では確実に機能しませんでした。

しかし、うまくいったのはposition: fixed要素に<body>を設定して、ボディが動かないようにすることです。 ただし注意してくださいこれはまだtype 2の発生を完全に停止するわけではありません。そのため、バックグラウンドでフォーカスロック付きのtype2がまだ発生しているため(停止した後)しばらく画面に触れると、期待どおりに動作します)。

これはまだ最適な解決策とはほど遠いですが、現時点で話すことができる最善の方法のようです。

編集:position: fixed<body>要素に配置しても安全かどうかはわかりません。考えられる問題を追跡するために、作成した following SO post 。ボディ要素の子としてラッパー要素を作成し、ズームを避けるためにその要素をposition: fixedに設定する方が良いようです問題。


編集2:明確なソリューション

スクリプト iNoBounce は驚異的に動作します。それをページにロードして、バウンスのないWebアプリケーションを体験してください。これまでのところ、私はこの解決策で問題を発見していません。

13
Aides