Help us understand the problem. What is going on with this article?

スクロールに合わせてヘッダーが積み重なっていく UI を作る

More than 1 year has passed since last update.

position: sticky;display: contents; を使い、「スクロールに合わせて各コンテンツのヘッダーがページ上部に積み重なっていく UI 」を作ってみました。おまけで「ヘッダーをクリックすると、該当するコンテンツへスクロールする」機能も付けています。つまりこういうことです。
stacked-headers.gif
動くデモはこちら。
この UI 、何と呼ぶかわからないので、 Stacked Headers と勝手に呼んでいます。

Pros/Cons

先に言っておくと、使わない方が良いです(素直)。

Pros

  • あまり見かけないデザインのため、印象に残りやすい(かもしれない)
  • 通過した各コンテンツの先頭へ飛べる

Cons

  • 下へ行くほど読みにくくなる
  • ヘッダーが重なりすぎるとコンテンツがヘッダーで見えなくなってしまう
  • ブラウザサポートが心配
    • position: sticky;何とか使える状況
      • そもそもサポートされていなくても可読性に問題はないはず
    • display: contents;極めて厳しい状況 だが、 DOM を変更することで対応可能(ただし valid な構造ではなくなるはず)
  • ヘッダーをクリックしてスクロールする機能は JavaScript に頼らざるを得ない

説明

面倒なので詳細はコード読んでください(ぶん投げ)。
GitHub でも読めます。

<meta charset="utf-8">
<meta name="viewport" content="width=device-width,initial-scale=1.0">
<style>
  * {
    margin: 0;
    padding: 0;
  }
  body {
    font-family: sans-serif;
    font-size: 20px;
  }
    .page__header {
      padding: 16px;
    }
      .page__header h1 {
        text-align: center;
      }
      .content_container {

        /*
         * コンテンツヘッダーの高さ
         * WANT: JavaScript と値を共有する
         * WANT: 高さを可変にする(またまたご冗談を)
         */
        --content__header_height: 80px;

        position: relative;
      }
        .content {

          /*
           * この要素を存在しないものとして扱う
           * 結果、 .content__header の position: sticky; が、
           * .content ではなく、 .content_container に紐づく
           */
          display: contents;

        }
          .content__header {
            cursor: pointer;
            display: block;
            position: -webkit-sticky; /* Safari 用 */
            position: sticky;
          }

          /* 積み重なる位置の指定 */
          .content:nth-child(1) .content__header {
            top: 0;
          }
          .content:nth-child(2) .content__header {
            top: var(--content__header_height);
          }
          .content:nth-child(3) .content__header {
            top: calc(var(--content__header_height) * 2);
          }
          .content:nth-child(4) .content__header {
            top: calc(var(--content__header_height) * 3);
          }
          .content:nth-child(5) .content__header {
            top: calc(var(--content__header_height) * 4);
          }

            .content__header h2 {
              color: #ffffff;
              line-height: var(--content__header_height);
              padding: 0 32px;
              height: var(--content__header_height);
            }
          .content__body {
            color: #ffffff;
            padding: 0 16px 16px;
          }
          .content:nth-child(1) .content__header,
          .content:nth-child(1) .content__body {
            background-color: #c04040;
          }
          .content:nth-child(2) .content__header,
          .content:nth-child(2) .content__body {
            background-color: #c0c040;
          }
          .content:nth-child(3) .content__header,
          .content:nth-child(3) .content__body {
            background-color: #40c040;
          }
          .content:nth-child(4) .content__header,
          .content:nth-child(4) .content__body {
            background-color: #40c0c0;
          }
          .content:nth-child(5) .content__header,
          .content:nth-child(5) .content__body {
            background-color: #4040c0;
          }
    .page__footer {
      padding: 64px 0;
    }
      .page__footer small {
        display: block;
        text-align: center;
      }
</style>
<header class="page__header">
  <h1>Stacked Headers</h1>
</header>
<div class="content_container">
  <section class="content">
    <a class="content__header" onclick="scrollToContent(1)">
      <h2>Content 1</h2>
    </a>
    <p id="content__body--1" class="content__body">Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.</p>
  </section>
  <section class="content">
    <a class="content__header" onclick="scrollToContent(2)">
      <h2>Content 2</h2>
    </a>
    <p id="content__body--2" class="content__body">Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.</p>
  </section>
  <section class="content">
    <a class="content__header" onclick="scrollToContent(3)">
      <h2>Content 3</h2>
    </a>
    <p id="content__body--3" class="content__body">Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.</p>
  </section>
  <section class="content">
    <a class="content__header" onclick="scrollToContent(4)">
      <h2>Content 4</h2>
    </a>
    <p id="content__body--4" class="content__body">Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.</p>
  </section>
  <section class="content">
    <a class="content__header" onclick="scrollToContent(5)">
      <h2>Content 5</h2>
    </a>
    <p id="content__body--5" class="content__body">Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.</p>
  </section>
</div>
<footer class="page__footer">
  <small>&copy; 2019 mimonelu</small>
</footer>
<script>
  const CONTENT_HEADER_HEIGHT = 80 // WANT: CSS と値を共有する
  const scrollToContent = (index) => {
    window.scrollBy({
      top: document.querySelector(`#content__body--${index}`).getBoundingClientRect().top - (index * CONTENT_HEADER_HEIGHT),
      behavior: 'smooth'
    })
  }
</script>

Sass とか Vue とか使えば、ギュッと圧縮できますね。

おわりに

ヘッダーをクリックしてスクロールする機能が不要だったり、 HTML が invalid でも良かったりするのであれば、よりシンプルなコードになるはずです。
さらに言うと、積み重ならなくても良いのであればもっとシンプルなコードになるし、その方が UX 的にもよろしいので、この記事は position: sticky;display: contents; の勉強程度に考えてください。

Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした