web-dev-qa-db-ja.com

スクロール機能を無効にせずにiOSのバウンスを防ぐ

Webページのコンテンツがビューポートよりも大きい場合、iOS用のSafariでiOSバウンス効果を防ぐソリューションを実装しようとしています。

私が取り組んでいるページは、その構造が非常に具体的で、このページに非常に似ています http://new.salt.ch/

  • 基本構造はブートストラップベースです。
  • 上部に固定navbarがあります。
  • フルスクリーンの背景スライドショーがあります。
  • スライドショーには、ビューポートの下部に固定されたオーバーレイがあります。
  • オフキャンバスをロードするフッター要素があり、コンテンツのスクロール時にのみ表示されます。
  • コンテンツはnavbarの背後でスクロールします。
  • コンテンツは、ナビゲーションバーの20ピクセル下に配置されたタイトルと、ビューポートの20ピクセル上に配置された一連のボタンで構成されます。
  • スクロールすると、ボタンとタイトルがすべて画面上に移動してフッターが表示されます。

私が抱えている問題は、ページ上の問題と同じです http://new.salt.ch/ 上にスクロールすると、画面の下部にバウンス効果が得られ、背景とオーバーレイが表示されます。

INoBounce.js、Nonbounce.js、SOで見つけたその他の提案など、さまざまなソリューションを試しました。

私は常に同じ問題を抱えています...バウンスを無効にしようとすると、すべてのスクロールが無効になります。これは、コンテンツ(フッター以外)が常にスクロールが必要ないほど十分に大きいため、スクロールが無効になり、フッターがスクロールでアクセスできなくなるためだと推測しています。

19
Ali Samii

このコードは、バウンスするHTMLタグであるため、バウンスを停止する必要があります

html {
    height  : 100%;
    overflow: hidden;
}
body {
    height  : 100%;
    overflow: auto;
}
14
James Campbell

あなたの質問を正しく解釈している場合、クロスプラットフォームモバイルウェブアプリを開発するために長年同じ問題を抱えており、各デバイスですべての異なる独自のスクロール機能を正しく動作させようとしています:Apple iOS、Google Android、Windows Phone、ラップトップChrome、ラップトップSafari、IE、ラップトップEdge。

jQuery Mobileは、フレームワークの範囲内でこれを試行し、修正し続けていますが、各デバイスメーカー/ OSメーカーからの絶え間ない更新により、モグラが強すぎます。

はい、モバイルデバイスごとにソリューションがあります。また、各デバイスのデバイス選択ページングフレームワークの開発をテストしましたが、真剣には検討していません。各デバイスを検出し、それぞれにわずかに異なるフレームワークを提示する必要があります。基本的に少なくとも3つ、実際には最大12個の異なるバージョンのコードを維持するという非常に悪い考えです。

解決策:永続ヘッダーとフッターをページフレームワークの上に置くだけで、最大の幸運が得られました。簡単にするためにインラインスタイルを使用する一般的なソリューションを次に示します。

<html>
<head>
  <title>Fixed Header and Footer on All Mobile Web Apps</title>
  <meta name="viewport" content="user-scalable=no, initial-scale=1.0, maximum-scale=1.0" />
  <style>
    html, body { height:100%; width:100%; }
  </style>
</head>
<body>
<div style="position: fixed; height:100%; width:100%; top:0; left:0;">
  <div style="height:calc(100% - 1px); width:100%; margin-top: 60px; z-index: 1; overflow-y: scroll; -webkit-overflow-scrolling: touch;">
    [Body Content That Can Scroll if it extends beyond screen...]

  </div>
  <div style="position: absolute; top:0; left:0; width:100%; height: 60px; background:#dddddd; z-index:10;">
    [Header Content...]

  </div>
  <div style="position: absolute; bottom:0; left:0; width:100%; height: 40px; background:#cccccc; z-index:11;">
    [Footer Content...]

  </div>
</div>
</body>
</html>

したがって、BodyはjQuery Mobileのページセットになります。実際、理論的には、ボディはどのフレームワークのほぼすべてのコンテンツである可能性があります。

特別な注意、高さのある行:calc(100%-1px);魔法にとって重要です。

この問題の見かけ上は無限の組み合わせまたは順列は、最も純粋で、最も単純で、最も普遍的に互換性のあるソリューションを見つけようとして、長年にわたってほとんど私たちの十字軍になりました。したがって、このトピックに恥ずかしいほどの工数を費やした後、これは私たちの最良の解決策であるだけでなく、私たちが見つけた唯一の普遍的な互換性のあるアプローチでもあり、単一のコードベースに固執することもできます。 iOS、Windows Phone、Android、ラップトップChrome、ラップトップSafari、PhoneGap、ラップトップFirefox、IE 9-11、およびWindows Edgeの最新バージョンで正常にテストされています。

タグ:モバイルアプリ、ウェブアプリ、固定ヘッダー、固定フッター、固定ヘッダー、固定フッター、スクロールの問題、iOSスクロールバウンス、Chromeスクロールバウンス、Androidスクロールバウンス、Webkitスクロールの問題、Webkitタッチスクロール、iOSタッチスクロールの問題

17
Jeremy Whitt

SOでいくつかの回答を行ったところ、このコードに出くわすまで物事は暗い感じでした。

html {
  position: fixed;
  height: 100%;
  overflow: hidden;
}

body {
  width: 100vw;
  height: 100vh;
  overflow-y: scroll;
  overflow-x: hidden;
  -webkit-overflow-scrolling: touch;
}

bodyのスタイル宣言は、スクロール機能が必要な要素に配置できます。必要に応じてoverflow-xoverflow-yを変更することもできます。私は個人的に、横にスクロールしないように必要だったので、そのように宣言しました。

2017年9月15日更新:別のプロジェクトにこの修正を使用する必要があり、htmlセレクターでこれらの宣言position: fixedおよびheight: 100%;なしで実行できました。 YMMV

2018年4月12日更新(コメントに記載):ページで固定要素を使用している場合、スクロール時にそれらの要素に視覚的な「ゆがみ」が生じることがあります。

15
Sgnl

INoBounce https://github.com/lazd/iNoBounce を使用しましたが、完全に機能します!

水平スクロールも許可する必要がある場合は、 santi6291 at https://github.com/lazd/iNoBounce/pull/36 によるプルリクエストがあります。

4
Zhong Huiwen

モバイルSafariでoverflow: autoおよびoverflow: scrollをサポートするほとんどの問題を解決できました。

  • リストの先頭をタッチしてスクロールビューをハングさせずに、下に移動してから上に移動します(その場合、モバイルSafariはページ全体に対してデフォルトのバウンス動作を実行します)
  • スクロールバーによるいオーバーラップなしの上部の固定ヘッダー/アクションバーのサポート
window.addEventListener('DOMContentLoaded', function () {
                        
  var atTop = true;
  var atBottom = false;

  var body = document.getElementById('fixedBody');
  body.addEventListener('touchmove', function(event){
    event.preventDefault();
  });

  body.addEventListener('touchstart', function(event){
    event.preventDefault();
  });

  body.addEventListener('touchend', function(event){
    event.preventDefault();
  });

  var scrollingDiv = document.getElementById('scrollDiv');
  if (scrollingDiv.scrollHeight <= scrollingDiv.clientHeight) {
    atBottom = true;
  }

  scrollingDiv.addEventListener('scroll', function(event){
    
    if (event.target.scrollTop === 0) {
      atTop = true;
    } else {
      atTop = false;
    }
    
    if (event.target.scrollHeight - event.target.scrollTop === event.target.clientHeight) {
      atBottom = true;
    } else {
      atBottom = false;
    }
  });
  
  var lastY = 0;
  var topLock = false;
  var bottomLock = false;
  
  scrollingDiv.addEventListener('touchmove', function(event){
    event.stopPropagation();
    var currentY = event.touches[0].clientY;
    if (currentY > lastY) {
      // moved down
      if (atTop) {
        event.preventDefault();
        topLock = true;
      }

      if (bottomLock) {
        bottomLock = false;
        // TODO: Emulate finger remove and touch again here
      }
    } else if(currentY < lastY){
      // moved top
      if (atBottom) {
        event.preventDefault();
        bottomLock = true;
      }

      if (topLock) {
        topLock = false;
        // TODO: Emulate finger remove and touch again here
      }
    }
     
    lastY = currentY;
  });

  scrollingDiv.addEventListener('touchstart', function(event){
    lastY = event.touches[0].clientY;
    event.stopPropagation();
  });

  scrollingDiv.addEventListener('touchend', function(event){
    event.stopPropagation();
  });

});
<html>
<head>
  <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=0"/>
</head>
<body id="fixedBody" style="overflow: hidden;">
  <div style="position: fixed; height: 64px; width:100%; top:0; left:0; background-color: green; z-index: 1;">
    Header
  </div>
  <div id="scrollDiv" style="position: absolute; width: 100%; top: 64px; bottom: 0px; overflow-y: auto; overflow-x: hidden; -webkit-overflow-scrolling: touch; background-color: white;">
    <div style="height: 150px; background-color: blue;">
      First
    </div>
    <div style="height: 150px; background-color: red;">
      Second
    </div>
    <div style="height: 150px; background-color: green;">
      Third
    </div>
    <div style="height: 150px; background-color: black;">
      Another
    </div>
  </div>
</body>
</html>

唯一の注意点は、ユーザーがタッチして下に移動してから上に移動しても何も起こらないことです(予想:リストが下にスクロールするはずです)。しかし、少なくともこのメソッドは「疑似スクロールダウン」を防ぎ、ユーザーを混乱させません。

この最後の問題を克服するには、タッチエンドをエミュレートし、方向が変わったときにタッチスタートイベントをエミュレートする必要があります(「TODO」コメントを入れました)。

更新:iOS CordovaでDisallowOverscroll = trueおよびWKWebViewを使用してJavaScriptコード修正を使用することを避けることが可能です。

0
Brian Haak