ボディのスクロールを防止しますが、オーバーレイのスクロールを許可します


445

これを可能にする「ライトボックス」タイプのソリューションを探していましたが、まだ見つかりません(ご存知の場合は、提案してください)。

私が再現しようとしている動作は、Pinterestで画像をクリックしたときに表示される動作と同じです。オーバーレイはスクロール可能ですが(オーバーレイ全体がページの上のページのように上に移動するため)、オーバーレイの後ろの本体は固定されています。

私はこれをCSSだけで作成しようとしました(つまり、divページ全体の上にオーバーレイし、で本文を重ねますoverflow: hidden)が、divスクロール可能になるのを妨げません。

本文/ページがスクロールしないようにしながら、フルスクリーンコンテナー内でスクロールし続けるにはどうすればよいですか?


これは、overflow-y scrollで修正された位置を使用して、hundretsのような「オーバーレイ」プラグインではありませんか?
ggzone

3
Pinterestへのリンクは役に立ちません。そのコンテンツはログインウォールの背後にあるためです。
2540625

4
2017更新: Pinterestにログインした場合でも、OPで説明されているオーバーレイ効果が存在しないことがわかります。代わりに、画像をクリックすると、画像の大きなバージョンが表示されている通常のページに移動します。
アナベル

回答:


623

理論

pinterestサイトの現在の実装(将来変更される可能性があります)を見ると、オーバーレイを開くと、noscrollクラスがbody要素に適用されてoverflow: hidden設定されているため、bodyスクロールできなくなります。

オーバーレイ(オンザフライまたはすでにページ内の作成を介して見えるようになるdisplay: block、それは違いはありませんが、)ありposition : fixedoverflow-y: scroll、とtopleftrightおよびbottomに設定されたプロパティ0:このスタイルは、全体のビューポートを埋めオーバーレイます。

divオーバーレイの内側は、代わりにposition: static、表示される垂直スクロールバーがその要素に関連しています。その結果、コンテンツはスクロール可能ですが、オーバーレイは固定されたままです。

ズームを閉じると、オーバーレイが(を介してdisplay: none)非表示になり、JavaScriptを介して完全に削除することもできます(または、内部のコンテンツだけを挿入する方法は、あなた次第です)。

最後のステップとして、noscrollクラスも削除する必要がありますbody(そのため、overflowプロパティは初期値に戻ります)。


コード

コードペンの例

aria-hiddenオーバーレイの表示と非表示、およびアクセシビリティを高めるためにオーバーレイの属性を変更することで機能します)。

マークアップ
(開くボタン)

<button type="button" class="open-overlay">OPEN LAYER</button>

(オーバーレイして閉じるボタン)

<section class="overlay" aria-hidden="true">
  <div>
    <h2>Hello, I'm the overlayer</h2>
    ...   
    <button type="button" class="close-overlay">CLOSE LAYER</button>
  </div>
</section>

CSS

.noscroll { 
  overflow: hidden;
}

.overlay { 
   position: fixed; 
   overflow-y: scroll;
   top: 0; right: 0; bottom: 0; left: 0; }

[aria-hidden="true"]  { display: none; }
[aria-hidden="false"] { display: block; }

Javascript (バニラ-JS)

var body = document.body,
    overlay = document.querySelector('.overlay'),
    overlayBtts = document.querySelectorAll('button[class$="overlay"]');

[].forEach.call(overlayBtts, function(btt) {

  btt.addEventListener('click', function() { 

     /* Detect the button class name */
     var overlayOpen = this.className === 'open-overlay';

     /* Toggle the aria-hidden state on the overlay and the 
        no-scroll class on the body */
     overlay.setAttribute('aria-hidden', !overlayOpen);
     body.classList.toggle('noscroll', overlayOpen);

     /* On some mobile browser when the overlay was previously
        opened and scrolled, if you open it again it doesn't 
        reset its scrollTop property */
     overlay.scrollTop = 0;

  }, false);

});

最後transitionに、opacityプロパティに適用されたCSSによるフェードイン効果でオーバーレイが開く別の例を次に示します。またpadding-right、スクロールバーが消えたときに下にあるテキストのリフローを回避するために適用されます。

コードペンの例(フェード)

CSS

.noscroll { overflow: hidden; }

@media (min-device-width: 1025px) {
    /* not strictly necessary, just an experiment for 
       this specific example and couldn't be necessary 
       at all on some browser */
    .noscroll { 
        padding-right: 15px;
    }
}

.overlay { 
     position: fixed; 
     overflow-y: scroll;
     top: 0; left: 0; right: 0; bottom: 0;
}

[aria-hidden="true"] {    
    transition: opacity 1s, z-index 0s 1s;
    width: 100vw;
    z-index: -1; 
    opacity: 0;  
}

[aria-hidden="false"] {  
    transition: opacity 1s;
    width: 100%;
    z-index: 1;  
    opacity: 1; 
}

6
これが正解です。正しいので、誰かにチェックマークを付けてください。
スクータP

64
これは完全に正しいですが、モバイルサファリでは機能しません。背景はスクロールし続け、オーバーレイは修正されます。
a10s

20
正常に動作します。スクロール可能なビューポートコンテナにdivはCSSスタイルが必要position:fixedで、垂直方向のオーバーフロースクロールが可能でなければなりません。私はうまく使いoverflow-y:auto;、iOSの勢い/慣性スクロールの-webkit-overflow-scrolling:touch;ために、CSS に追加しました。私が使用しdisplay:block;width:100%;、およびheight:100%;CSSは、フルページビューポートを持っています。
2014年

10
すみません理解不能です。本体をオーバーフローに設定:非表示にしても、iPad iOS7の弾性本体のスクロールが無効になりません。だから私は私のjs内に追加する必要がありました:document.body.addEventListener( 'touchmove'、function(event){event.preventDefault();}、false); どのSADLYもすべての要素がスクロール可能になるのを無効にします。(余分なプラグインなし)これまでのいずれかの解決策を見つけていない
ガラヴァーニ

22
オーバーレイがアクティブ化されているときにユーザーが既に本文を下にスクロールしている場合、overflowを設定すると本文が上にジャンプします。これを回避する方法はありますか?
ビョルンアンダーソン

75

iOSでのオーバースクロールを防ぎたい場合は、.noscrollクラスに固定位置を追加できます

body.noscroll{
    position:fixed;
    overflow:hidden;
}

106
このソリューションでは、位置が固定されているため、本文はコンテンツの上部に自動的にスクロールされます。これは、ユーザーに迷惑をかける可能性があります。
tzi 14

5
使用に関する別の問題position:fixedは、それが私の本体のサイズを変更していたことです。多分それは他のCSSと競合していたかもしれませんが、overflow:hidden必要なのはそれだけです
Dex

3
これは、入力フィールドをタブで移動するときにフォーカスジャンプを壊します
Atav32

@ Atav32別の問題のように聞こえますが、位置とオーバーフローはタブオーダーに影響を与えません。
am80l

ときのiOSのSafari(上のフィールドを通して、あなたのタブタブ順序の作品が見つかりますが、画面の揺れ:@ am80l申し訳ありませんが、私の以前のコメントはスーパー漠然としたstackoverflow.com/questions/31126330/...
Atav32

44

使用しないでくださいoverflow: hidden;body。すべてを自動的に最上部にスクロールします。JavaScriptも必要ありません。を活用してくださいoverflow: auto;。このソリューションは、モバイルSafariでも機能します。

HTMLの構造

<div class="overlay">
    <div class="overlay-content"></div>
</div>

<div class="background-content">
    lengthy content here
</div>

スタイリング

.overlay{
    position: fixed;
    top: 0px;
    left: 0px;
    right: 0px;
    bottom: 0px;
    background-color: rgba(0, 0, 0, 0.8);

    .overlay-content {
        height: 100%;
        overflow: scroll;
    }
}

.background-content{
    height: 100%;
    overflow: auto;
}

こちらのデモとこちらのソースコードをご覧ください

更新:

キーボードのスペースバーが必要な場合は、ページを上下に動かしてください:オーバーレイにフォーカスする必要があります。たとえば、オーバーレイをクリックするか、JSのこの部分がdivキーボードに応答する前に、JSを手動でフォーカスする必要があります。オーバーレイを横に移動するだけなので、オーバーレイを「オフ」にした場合も同じです。それ以外の場合、ブラウザでは、これらは通常divの2つにすぎず、なぜそれらのいずれかに焦点を当てるべきかわかりません。


5
クールなソリューションですが、コンテンツのすべてをdivでラップする必要があります。これを行うつもりはありませんでした...
Jacob Raccuia

2
これは理想的なソリューションです。誰かがこれで問題を抱えている場合html, body { height: 100%; }、これを正しく動作させるために(デモのように)追加する必要があるかもしれません。
John

4
@ user18490角度/材料部分は、このソリューションが機能しているという事実とは何の関係もありません。
Lucia

10
これにより、モバイルデバイスのUXがさまざまな方法で壊れます(URLバーの非表示、オーバースクロールアフォーダンスなど)。デスクトップでスクロールするスペースバーも含まれます
2015

2
これはより堅牢なオプションですが、少し複雑になります。たとえば、PageUpとPageDownは更新時に機能しません。.offset()計算に値を使用するものはすべて混乱する、など...
BlackPanther '27 / 07/16

42

overscroll-behavior cssプロパティにより、コンテンツの上部/下部に到達したときのブラウザのデフォルトのオーバーフロースクロール動作をオーバーライドできます。

以下のスタイルをオーバーレイに追加するだけです。

.overlay {
   overscroll-behavior: contain;
   ...
}

Codepenデモ

現在、Chrome、Firefox、IEで動作します(caniuse

詳しくは、Google Developersの記事をご覧ください。


4
私はこれが最新のChrome、Mozilla、Operaで動作していることを伝えるためにずっと来ました。素晴らしい時を過ごして!
ワナビーJavaGeek

3
誰かがこれに報奨金を加えるべきです。これが正しい解決策です。JavaScriptは必要ありません。:)
joseph.l.hunsaker 2018

9
このソリューションの問題は、オーバーレイがスクロール可能な場合にのみ機能することです。モーダルがあり、すべてが画面に収まるため、内部にスクロールがない場合、本体のスクロールは停止しません。
それと

1
私はこれとoverflow-y: scroll(あなたのCodepenデモから)を組み合わせて解決しました。@pokrishkaによって提起されたポイントは有効ですが、私の場合は問題ではありません。いずれにせよ、ブラウザーの実装がこの詳細を将来カバーするのではないかと思います。Firefoxで、モーダルが画面に合わないようにブラウザのサイズを変更し、フルサイズにサイズ変更した後でも、このプロパティが機能するように(つまり、スクロールが含まれるように)再度サイズ変更することがわかりました-ページがリロードされるまで、 少なくとも。
Marc.2377

33

ほとんどのソリューションにはスクロール位置が保持されないという問題があるため、Facebookがどのようにスクロールするかを調べました。アンダーレイのコンテンツをposition: fixedそれらに設定することに加えて、スクロール位置を保持するためにトップを動的に設定します:

scrollPosition = window.pageYOffset;
mainEl.style.top = -scrollPosition + 'px';

次に、オーバーレイを再度削除するときに、スクロール位置をリセットする必要があります。

window.scrollTo(0, scrollPosition);

このソリューションを示すために小さな例を作成しました

let overlayShown = false;
let scrollPosition = 0;

document.querySelector('.toggle').addEventListener('click', function() {
  if (overlayShown) {
		showOverlay();
  } else {
    removeOverlay();
  }
  overlayShown = !overlayShown;
});

function showOverlay() {
    scrollPosition = window.pageYOffset;
  	const mainEl = document.querySelector('.main-content');
    mainEl.style.top = -scrollPosition + 'px';
  	document.body.classList.add('show-overlay');
}

function removeOverlay() {
		document.body.classList.remove('show-overlay');
  	window.scrollTo(0, scrollPosition);
    const mainEl = document.querySelector('.main-content');
    mainEl.style.top = 0;
}
.main-content {
  background-image: repeating-linear-gradient( lime, blue 103px);
  width: 100%;
  height: 200vh;
}

.show-overlay .main-content {
  position: fixed;
  left: 0;
  right: 0;
  overflow-y: scroll; /* render disabled scroll bar to keep the same width */
}

.overlay {
  display: none;
  position: fixed;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
  background: rgba(0, 0, 0, 0.3);
  overflow: auto;
}

.show-overlay .overlay {
  display: block;
}

.overlay-content {
  margin: 50px;
  background-image: repeating-linear-gradient( grey, grey 20px, black 20px, black 40px);
  height: 120vh;
}

.toggle {
  position: fixed;
  top: 5px;
  left: 15px;
  padding: 10px;
  background: red;
}

/* reset CSS */
body {
  margin: 0;
}
<main class="main-content"></main>

  <div class="overlay">
    <div class="overlay-content"></div>
  </div>
  
  <button class="toggle">Overlay</button>


これは私にとって最もエレガントな解決策のようです。Googleでは、ページをスクロールしたり要素を移動したりするのと同じ手法を採用していることを知っています。
forallepsilon

これは、私にとって100%正確に機能した唯一のソリューションです。この答えを早く見つけたかったのですが。
user0103

31

bodyタグに "overflow:hidden"を追加しても機能しない場合があることに注意してください。そのような場合は、htmlタグにもプロパティを追加する必要があります。

html, body {
    overflow: hidden;
}

iOSの場合、次の設定も必要ですwidth: 100%; height: 100%;
Gavin

2
これは、この質問の他の多くのソリューションでも見られる問題には対処していません。モーダルが開いているときにウィンドウがページの上部以外の場所にスクロールされると、ページの上部にスクロールします。私はフィリップMittererの解決策を見つけ、ここでほとんどのケースをカバーして最良の選択肢であることを。
CLL 2018

1
私は、あなたが話しているこの問題を経験したことはありません(Webまたはモバイル)。フィドルやjsbinで動作しない完全なコード(DOMを使用)を共有できますか?いずれにせよ、私はこの問題に取り組むためにJavaScriptの回避策を使用することに警戒しています。
Francisco Hodge

7

一般的に、子(この場合はオーバーレイ)がスクロールしたときに親(この場合は本体)がスクロールしないようにする場合は、子を親の兄弟にして、スクロールイベントが発生しないようにします。親。親が本体である場合、これには追加の折り返し要素が必要です。

<div id="content">
</div>
<div id="overlay">
</div>

参照してください。ブラウザのメインスクロールバーでスクロール特定のDIVの内容をその作業を参照するには。


1
最良の解決策、真の「箱から出して」という考え方。
マーク

本文がスクロールする要素ではない場合、ページにスクロールダウンしてもモバイルではトップバーが上にスライドしません。
Seph Reed

7

選択した答えは正しいですが、いくつかの制限があります。

  • 指で超硬い「フリング」をしても<body>バックグラウンドでスクロールします
  • <input>モーダルでをタップして仮想キーボードを開くと、以降のすべてのスクロールが<body>

最初の問題は修正していませんが、2番目の問題に光を当てたかったのです。紛らわしいことに、以前Bootstrapはキーボードの問題を文書化していましたが、修正の例としてhttp://output.jsbin.com/cacido/quietを引用して修正されたと主張しました。

実際、この例は私のテストでiOSで正常に動作します。ただし、最新のブートストラップ(v4)にアップグレードすると破損します。

それらの違いを理解するために、Bootstrapに依存しないようにテストケースを縮小しました(http://codepen.io/WestonThayer/pen/bgZxBG)

決定要因は奇妙です。キーボードの問題を回避するにbackground-color <div>、モーダル含むルートにが設定されておらず、モーダルのコンテンツ<div>background-color設定されている可能性がある別のにネストされている必要があります。

これをテストするには、Codepenの例で以下の行のコメントを外します。

.modal {
  position: fixed;
  top: 0;
  right: 0;
  bottom: 0;
  left: 0;
  z-index: 2;
  display: none;
  overflow: hidden;
  -webkit-overflow-scrolling: touch;
  /* UNCOMMENT TO BREAK */
/*   background-color: white; */
}

4

タッチデバイスの場合101vhは、オーバーレイのラッパーに1px幅、最小高さの透明なdivを追加してみてください。次に-webkit-overflow-scrolling:touch; overflow-y: auto;、ラッパーに追加します。これにより、モバイルサファリは、オーバーレイがスクロール可能であると見なし、ボディからのタッチイベントをインターセプトします。

これがサンプルページです。モバイルサファリで開きますhttp : //www.originalfunction.com/overlay.html

https://gist.github.com/YarGnawh/90e0647f21b5fa78d2f678909673507f


うん、それはトリックをした。-webkit-overflow-scrolling:touch;タッチイベントをインターセプトするために、スクロール可能なコンテナごとに追加する必要がありました。
マティ2016

私はまだiPhoneでスクロールできます
Simone Zandara 2017

@SeniorSimoneZandara私は違います。これは、バックグラウンドスクロールを防ぐためにうまく機能します
godblessstrawberry 2018

3

防止したい動作は、スクロールチェーンと呼ばれます。無効にするには、

overscroll-behavior: contain;

CSSのオーバーレイに。


3

IpadとIphoneで自分のページにあった問題を解決しようとしているこの質問が見つかりました-画像付きの固定divをポップアップとして表示しているときに本体がスクロールしていました。

いくつかの回答は良いですが、どれも私の問題を解決しませんでした。Christoffer Petterssonによる次のブログ投稿を見つけました。そこで提示された解決策は、私がiOSデバイスで抱えていた問題を助け、それが私のスクロール背景の問題を助けました。

iOS Safariのラバーバンドスクロールについて学んだ6つのこと

リンクが古くなった場合に備えて、ブログ投稿の主なポイントを記載しました。

「ユーザーが「メニューが開いている」間に背景ページをスクロールできないようにするために、JavaScriptとCSSクラスを適用することにより、スクロールを許可するかどうかを制御できます。

このStackoverflowの回答に基づいて、タッチムーブイベントがトリガーされたときに、無効化スクロールを持つ要素がデフォルトのスクロールアクションを実行しないように制御できます。」

 document.ontouchmove = function ( event ) {

    var isTouchMoveAllowed = true, target = event.target;

    while ( target !== null ) {
        if ( target.classList && target.classList.contains( 'disable-scrolling' ) ) {
            isTouchMoveAllowed = false;
            break;
        }
        target = target.parentNode;
    }

    if ( !isTouchMoveAllowed ) {
        event.preventDefault();
    }
};

次に、ページdivにdisable-scrollingクラスを配置します。

<div class="page disable-scrolling">

2

これは、「新しい」CSSとJQueryを使用して簡単に行うことができます。

最初は、body {... overflow:auto;} JQueryを使用して、「オーバーレイ」と「ボディ」を動的に切り替えることができます。「body」の場合は、

body {
   position: static;
   overflow: auto;
}

「オーバーレイ」使用時

body {
   position: sticky;
   overflow: hidden;
}

スイッチのjQuery( 'body'-> 'overlay'):

$("body").css({"position": "sticky", "overflow": "hidden"});

スイッチのjQuery( 'overlay'-> 'body'):

$("body").css({"position": "static", "overflow": "auto"});

1

以前の回答に追加したいと思います。ボディをposition:fixedに切り替えると、一部のレイアウトが壊れました。それを回避するために、ボディの高さも100%に設定する必要がありました。

function onMouseOverOverlay(over){
    document.getElementsByTagName("body")[0].style.overflowY = (over?"hidden":"scroll");
    document.getElementsByTagName("html")[0].style.position = (over?"fixed":"static");
    document.getElementsByTagName("html")[0].style.height = (over?"100%":"auto");
}

1

次のHTMLを使用します。

<body>
  <div class="page">Page content here</div>
  <div class="overlay"></div>
</body>

次にJavaScriptをインターセプトしてスクロールを停止します。

$(".page").on("touchmove", function(event) {
  event.preventDefault()
});

次に、物事を通常に戻すには:

$(".page").off("touchmove");


1

意図がモバイル/タッチデバイスで無効にすることである場合、それを行う最も簡単な方法はを使用することですtouch-action: none;

例:

const app = document.getElementById('app');
const overlay = document.getElementById('overlay');

let body = '';

for (let index = 0; index < 500; index++) {
  body += index + '<br />';
}

app.innerHTML = body;
app.scrollTop = 200;

overlay.innerHTML = body;
* {
  margin: 0;
  padding: 0;
}

html,
body {
  height: 100%;
}

#app {
  background: #f00;
  position: absolute;
  height: 100%;
  width: 100%;
  overflow-y: scroll;
  line-height: 20px;
}

#overlay {
  background: rgba(0,0,0,.5);
  position: fixed;
  top: 0;
  left: 0;
  right: 0;
  height: 100%;
  padding: 0 0 0 100px;
  overflow: scroll;
}
<div id='app'></div>
<div id='overlay'></div>

(この例はスタックオーバーフローのコンテキストでは機能しません。スタンドアロンページで再作成する必要があります。)

#appコンテナのスクロールを無効にするには、を追加しtouch-action: none;ます。


1
タッチアクションがiOSで実際に機能した場合、それはそうです。その場合、「アプリ」全体を個別のdivでラップする必要はありません。
powerbuoy 2017

0

これを試して

var mywindow = $('body'), navbarCollap = $('.navbar-collapse');    
navbarCollap.on('show.bs.collapse', function(x) {
                mywindow.css({visibility: 'hidden'});
                $('body').attr("scroll","no").attr("style", "overflow: hidden");
            });
            navbarCollap.on('hide.bs.collapse', function(x) {
                mywindow.css({visibility: 'visible'});
                $('body').attr("scroll","yes").attr("style", "");
            });

0

body / htmlスクロールを停止したい場合は、次のように追加します

CSS

    html, body {
        height: 100%;
    }

    .overlay{
        position: fixed;
        top: 0px;
        left: 0px;
        right: 0px;
        bottom: 0px;
        background-color: rgba(0, 0, 0, 0.8);

        .overlay-content {
            height: 100%;
            overflow: scroll;
        }
    }

    .background-content{
        height: 100%;
        overflow: auto;
    }

HTML

    <div class="overlay">
        <div class="overlay-content"></div>
    </div>

    <div class="background-content">
        lengthy content here
    </div>

基本的に、JSなしでそれを行うことができます。

主なアイデアは、高さ:100%、オーバーフロー:自動でhtml / bodyを追加することです。オーバーレイ内では、要件に基づいてスクロールを有効または無効にできます。

お役に立てれば!



-2

スクロールバーを無効および有効にするには、以下のコードを使用してください。

Scroll = (
    function(){
          var x,y;
         function hndlr(){
            window.scrollTo(x,y);
            //return;
          }  
          return {

               disable : function(x1,y1){
                    x = x1;
                    y = y1;
                   if(window.addEventListener){
                       window.addEventListener("scroll",hndlr);
                   } 
                   else{
                        window.attachEvent("onscroll", hndlr);
                   }     

               },
               enable: function(){
                      if(window.removeEventListener){
                         window.removeEventListener("scroll",hndlr);
                      }
                      else{
                        window.detachEvent("onscroll", hndlr);
                      }
               } 

          }
    })();
 //for disabled scroll bar.
Scroll.disable(0,document.body.scrollTop);
//for enabled scroll bar.
Scroll.enable();
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.