本文のスクロールは禁止するが、オーバーレイのスクロールは許可する 質問する

本文のスクロールは禁止するが、オーバーレイのスクロールは許可する 質問する

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

私が再現しようとしている動作は、ピンタレスト画像をクリックすると、オーバーレイはスクロール可能です (オーバーレイ全体がページの上にページが重なるように上に移動します) が、オーバーレイの背後にある本体は固定されています。

私はこれを CSS だけで作成しようとしました (つまり、divページ全体と本文の上にオーバーレイを配置しますoverflow: hiddendiv) が、スクロールできなくなります。

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

ベストアンサー1

理論

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

オンザフライで作成されたオーバーレイ、または既にページに挿入されて を介して表示されたオーバーレイ (どちらでもかまいません)にはdisplay: block、およびがあり、、、プロパティが に設定されています。このスタイルにより、オーバーレイがビューポート全体を埋め尽くします (ただし、現在は 2022 年なので、代わりに を使用できます)。position : fixedoverflow-y: scrolltopleftrightbottom0inset: 0

divオーバーレイの内側には垂直position: staticスクロールバーがその要素に関連付けられています。これにより、スクロール可能でありながら固定されたオーバーレイが作成されます。

オーバーレイを閉じるときは、それを非表示にする必要があります ( を使用display: none)。また、JavaScript を使用してノードを削除することもできます (または、内部のコンテンツのみを削除することもでき、これはユーザー次第ですが、コンテンツの性質にも依存します)。

最後のステップは、noscroll適用されたクラスも削除することですbody(これにより、overflowプロパティは以前の値に戻ります)。


コード

Codepen の例

aria-hidden(オーバーレイの属性を変更して表示/非表示を切り替え、アクセシビリティを向上させることで機能します)。

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

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

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

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

CS

.noscroll { 
  overflow: hidden;
}

.overlay { 
   position: fixed; 
   overflow-y: scroll;
   inset: 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"]'),
    openingBtt;
    
[].forEach.call(overlayBtts, function(btt) {

  btt.addEventListener('click', function() { 
     
     /* Detect the button class name */
     var overlayOpen = this.className === 'open-overlay';
     
     /* storing a reference to the opening button */
     if (overlayOpen) {
        openingBtt = this;
     }
     
     /* 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;
     
      /* forcing focus for Assistive technologies but note:
    - if your modal has just a phrase and a button move the
      focus on the button
    - if your modal has a long text inside (e.g. a privacy
      policy) move the focus on the first heading inside 
      the modal
    - otherwise just focus the modal.

    When you close the overlay restore the focus on the 
    button that opened the modal.
    */
    if (overlayOpen) {
       overlay.focus();
    }
    else {
       openingBtt.focus();
       openingBtt = null;
    }

  }, false);

});

/* detect Escape key when the overlay is open */
document.body.addEventListener('keyup', (ev) => {
   if (ev.key === "Escape" && overlay.getAttribute('aria-hidden') === 'false') {
      overlay.setAttribute('aria-hidden', 'true');
      body.classList.toggle('noscroll', false);
      openingBtt.focus();
      openingBtt = null;
   }
})

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

Codepen の例 (フェード)

CS

.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;
     inset: 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; 
}

おすすめ記事