iOSはoverflow:hidden;でスクロールを無効にできない

  • 63
    いいね
  • 0
    コメント

スクロールを無効にするCSS

html,body{
  overflow: hidden
}

しかしこれではiOSでスクロールを無効にできない.

ページ全体を無効にする方法

// スクロールを無効にする
$(window).on('touchmove.noScroll', function(e) {
    e.preventDefault();
});

// スクロール無効を解除する
$(window).off('.noScroll');

スマホの独自イベントのtouchmoveイベントを切ってスクロールを止める。
解除したい時はoffする。

イベント名に、.noScrollと名前を付けることで、他の部分でtouchmoveイベントを使っていたとしても、そのtouchmoveイベントまでイベントをoff()してしまうこともなく、.noScrollの名前のイベントのみをoff()することができる。

参考

特定のエリアのみスクロールを無効にする方法

ドロワーメニューを左側に表示するときにコンテンツ側を固定するときなど

// スクロールを無効にする
$('.content').on('touchmove.noScroll', function(e) {
    e.preventDefault();
});

// スクロール無効を解除する
$('.content').off('.noScroll');

としても、うまく動くときとうまく動かないときがあった。

しょうがないのでfixして対応した。

// スクロールを無効にする
var scrollTop = $(window).scrollTop();
$('.content').css({'position':'fixed','top':-scrollTop});

// スクロール無効を解除する
$('.content').css({'position':'static','top':'0'});
$('html,body').scrollTop(scrollTop);

私が引っかかってしまったのは、

$('.content').css({'position':'static','top':'0'}).scrollTop(scrollTop);

としてしまったこと。

おまけ

ちなみに作業していたサイトは、

  • レスポンシブでPC,スマホ対応
  • 左ドロワー、コンテンツが右にずれるタイプ
  • スワイプでも操作できる
  • ドロワーをホバーしたらスクロールバーを表示したい・慣性的な動きにしたい
  • ヘッダー固定
  • ドロワーを開いているとき、ドロワー以外をクリック(タップ)したら閉じる
  • ドロワーを開いているときコンテンツはスクロールできない

という仕様だった。(いや、最初は違ったんだ・・・)

当初、sidrというプラグインを使っていた。
途中でヘッダー固定になり、いろいろ書き加えたところ、iPad,Androidタブレットでうまく動かなくなった。
ドロワーを閉じると、Chromeは上へ、Safariは下へ、Firefoxは下へ少しずれる。。。iPhoneも発生するものと発生しないものが・・・(/-\ )
右ドロワーなら問題なかった。
それがプラグインの仕様のようだったので、違うプラグインを探した。
いろいろ検証した結果slideoutというのが一番理想に近いことができそうだったので、これで頑張ることにした。

他にも、
iOSでページトップで上にスクロールしようとするとドロワーメニューが見えてしまう。
ページが全て読み込まれる前にスクロールするとヘッダーの上に隙間ができる。(これは対応していない)
ドロワーメニューのスクロール位置が次開いたときにも引き継がれている。
IE11だけドロワーを開いたときヘッダーが右に移動しない。

などなどいろいろありました。

また、このプラグインは、touchstartがうまく動かないようで、/に遷移してしまう症状もありました。

var _touch = ('ontouchstart' in document) ? 'touchstart' : 'click';
$('.menu').on(_touch,function(){});

こんな感じの使い方はできませんでした。

最終的に、ざっくりとですがこんな感じで実装しました〜

<script src="/js/slideout.min.js"></script>

<script>
    $(function(){

        var ua = navigator.userAgent.toLowerCase(),
                isIE11 = (ua.indexOf('trident/7') > -1);

        var slideout = new Slideout({
            'panel': document.getElementById('panel'),
            'menu': document.getElementById('menu'),
            'padding': 260,
            'duration' : 200
        });

        var scrollTop = $(window).scrollTop(),
                header = $('header'),
                panel = $('#panel'),
                drawer = $('#menu'),
                close = $('.drawer-close'),
                fadeSpeed = 100;

        $('.js-slideout-toggle').on('click',function(){
            slideout.toggle();
        });

        slideout.on('beforeopen', function() {
            if(isIE11){
                header.css({'top':'0'});
                header.animate({'left':'280px'},200);
            }else{
                scrollTop = $(window).scrollTop();
                header.css({'top':scrollTop});
                panel.css({'position':'fixed','top':-scrollTop});
            }
            drawer.show();
        });

        slideout.on('open', function() {
            header.css({'top':scrollTop});
            close.css({
                'z-index':'999999',
                'display':'block'
            });
        });

        slideout.on('beforeclose', function() {
            if(isIE11) {
                header.animate({'left':'0'},200);
            }
        });

        slideout.on('close', function() {
            header.css({'top':'0'});
            panel.css({'position':'static','top':'0'});
            $('html,body').scrollTop(scrollTop);
            close.css({
                'z-index':'1',
                'display':'none'
            });
            drawer.hide();
            $('html').removeClass('slideout-open');
        });

    });
</script>
<nav id="menu" class="slideout-menu">
  <div class="js-slideout-toggle">x</div>
  <div>
    メニュー
  </div>
</nav>

<main id="panel" class="slideout-panel">
  <header>
    <div class="js-slideout-toggle">
      <span class="icon-bar"></span>
      <span class="icon-bar"></span>
      <span class="icon-bar"></span>
    </div>
    <h2>Panel</h2>
  </header>
  <div>
    コンテンツ
  </div>
  <div class="drawer-close js-slideout-toggle"></div>
</main>
.slideout-menu{
    position: fixed;
    left: 0;
    top: 0;
    bottom: 0;
    right: 0;
    z-index: 0;
    width: 280px;
    overflow-y: auto;
    -webkit-overflow-scrolling: touch;
    display: none;
}
.slideout-panel{
    position: relative;
    background-color: #fff;
    z-index: 2;
}
.slideout-open,
.slideout-open body,
.slideout-open .slideout-panel {
    overflow: hidden;
}
.slideout-open .slideout-menu {
    display: block;
}

.drawer-close {
    display: none;
    position: absolute;
    top: 0;
    left: 0;
    width: 100%;
    height: 100%;
    z-index: 1;
}

そういえば、初回アクセス時はドロワーが表示されていて、しばらくしたら閉じるという仕様だったな・・・
他にも案を出したが採用されず、cookieを使ってプラグインの仕様に合わせるためにゴリゴリしたら、やっぱりいらないってなったなぁー・・・w(でしょうねw)

他にもいろいろゴリゴリした。
なんだかんだ久しぶりにがっつりで楽しかったな〜