cssと少しのJavaScriptで、ヘッダー追従要素あっても、それが被らないでスムーズスクロールを実装する

# まずはじめに
ヘッダー部分に追従要素がある時、ページ内スクロールをすると、その追従要素が重なってくることがあると思います。
それをcssと少しのJavaScriptで解決しようという話です。

# どう解決するか
cssだけでスムーズスクロールとスクロールした時の余白を設定できるようになりました。

scroll-behavior
https://developer.mozilla.org/ja/docs/Web/CSS/scroll-behavior

scroll-padding-top
https://developer.mozilla.org/ja/docs/Web/CSS/scroll-padding-top

これらを使って解決しようと思います。

```html
<div id="content" class="content">
  <nav id="nav" class="nav">
    <a href="#target1" class="nav_link">link1</a>
    <a href="#target2" class="nav_link">link2</a>
    <a href="#target3" class="nav_link">link3</a>
    <a href="#target4" class="nav_link">link4</a>
  </nav>
  <div class="body">
    <p>Lorem ipsum dolor sit amet consectetur adipisicing elit. Eos adipisci pariatur ut vel harum fugit esse, beatae blanditiis consequuntur velit quisquam magni facere incidunt voluptatem natus. Dolores rerum voluptas aliquam!</p>
    <p>Lorem ipsum dolor sit amet consectetur adipisicing elit. Natus libero assumenda quibusdam rem, neque ullam consectetur expedita exercitationem aliquid accusamus iusto dolore tempore deleniti non cupiditate officia! Esse, non fuga!</p>
    <p>Lorem ipsum, dolor sit amet consectetur adipisicing elit. Voluptas ipsum itaque illum dolore hic atque, ducimus quisquam tempora nobis neque, nostrum voluptate eius! Modi, vitae? Quas quaerat unde praesentium neque.</p>
    <p>Lorem ipsum dolor sit, amet consectetur adipisicing elit. Sit similique, nobis iste corrupti illo voluptate obcaecati illum praesentium doloremque eaque maiores magnam earum, fuga doloribus adipisci beatae excepturi. Ducimus, ea?</p>
    <p id="target1">Anchor Target1</p>
    <p>Lorem ipsum, dolor sit amet consectetur adipisicing elit. Molestiae veniam quae enim harum magni cupiditate ipsum atque porro, fugit corporis ab id architecto dolorem quis. Officiis non animi numquam magni!</p>
    <p>Lorem ipsum dolor sit amet consectetur adipisicing elit. Voluptatem error, explicabo minus officia vero facere officiis voluptatibus et rerum fugit, quia est consectetur, illum cupiditate. In at voluptate voluptatem repudiandae!</p>
    <p>Lorem ipsum dolor sit amet consectetur adipisicing elit. Eos adipisci pariatur ut vel harum fugit esse, beatae blanditiis consequuntur velit quisquam magni facere incidunt voluptatem natus. Dolores rerum voluptas aliquam!</p>
    <p>Lorem ipsum dolor sit amet consectetur adipisicing elit. Natus libero assumenda quibusdam rem, neque ullam consectetur expedita exercitationem aliquid accusamus iusto dolore tempore deleniti non cupiditate officia! Esse, non fuga!</p>
    <p>Lorem ipsum, dolor sit amet consectetur adipisicing elit. Voluptas ipsum itaque illum dolore hic atque, ducimus quisquam tempora nobis neque, nostrum voluptate eius! Modi, vitae? Quas quaerat unde praesentium neque.</p>
    <p>Lorem ipsum dolor sit, amet consectetur adipisicing elit. Sit similique, nobis iste corrupti illo voluptate obcaecati illum praesentium doloremque eaque maiores magnam earum, fuga doloribus adipisci beatae excepturi. Ducimus, ea?</p>
    <p id="target2">Anchor Target2</p>
    <p>Lorem ipsum, dolor sit amet consectetur adipisicing elit. Molestiae veniam quae enim harum magni cupiditate ipsum atque porro, fugit corporis ab id architecto dolorem quis. Officiis non animi numquam magni!</p>
    <p>Lorem ipsum dolor sit amet consectetur adipisicing elit. Voluptatem error, explicabo minus officia vero facere officiis voluptatibus et rerum fugit, quia est consectetur, illum cupiditate. In at voluptate voluptatem repudiandae!</p>
    <p>Lorem ipsum dolor sit amet consectetur adipisicing elit. Natus libero assumenda quibusdam rem, neque ullam consectetur expedita exercitationem aliquid accusamus iusto dolore tempore deleniti non cupiditate officia! Esse, non fuga!</p>
    <p>Lorem ipsum, dolor sit amet consectetur adipisicing elit. Voluptas ipsum itaque illum dolore hic atque, ducimus quisquam tempora nobis neque, nostrum voluptate eius! Modi, vitae? Quas quaerat unde praesentium neque.</p>
    <p>Lorem ipsum dolor sit, amet consectetur adipisicing elit. Sit similique, nobis iste corrupti illo voluptate obcaecati illum praesentium doloremque eaque maiores magnam earum, fuga doloribus adipisci beatae excepturi. Ducimus, ea?</p>
    <p id="target3">Anchor Target3</p>
    <p>Lorem ipsum, dolor sit amet consectetur adipisicing elit. Molestiae veniam quae enim harum magni cupiditate ipsum atque porro, fugit corporis ab id architecto dolorem quis. Officiis non animi numquam magni!</p>
    <p>Lorem ipsum dolor sit amet consectetur adipisicing elit. Voluptatem error, explicabo minus officia vero facere officiis voluptatibus et rerum fugit, quia est consectetur, illum cupiditate. In at voluptate voluptatem repudiandae!</p>
    <p>Lorem ipsum dolor sit amet consectetur adipisicing elit. Natus libero assumenda quibusdam rem, neque ullam consectetur expedita exercitationem aliquid accusamus iusto dolore tempore deleniti non cupiditate officia! Esse, non fuga!</p>
    <p>Lorem ipsum, dolor sit amet consectetur adipisicing elit. Voluptas ipsum itaque illum dolore hic atque, ducimus quisquam tempora nobis neque, nostrum voluptate eius! Modi, vitae? Quas quaerat unde praesentium neque.</p>
    <p>Lorem ipsum dolor sit, amet consectetur adipisicing elit. Sit similique, nobis iste corrupti illo voluptate obcaecati illum praesentium doloremque eaque maiores magnam earum, fuga doloribus adipisci beatae excepturi. Ducimus, ea?</p>
    <p id="target4">Anchor Target4</p>
    <p>Lorem ipsum, dolor sit amet consectetur adipisicing elit. Molestiae veniam quae enim harum magni cupiditate ipsum atque porro, fugit corporis ab id architecto dolorem quis. Officiis non animi numquam magni!</p>
    <p>Lorem ipsum dolor sit amet consectetur adipisicing elit. Voluptatem error, explicabo minus officia vero facere officiis voluptatibus et rerum fugit, quia est consectetur, illum cupiditate. In at voluptate voluptatem repudiandae!</p>
</div>
```

```css
.content {
  width: 300px;
  height: 200px;
  overflow: hidden auto;
  scroll-behavior: smooth;
  scroll-padding-top: 40px; // .navの高さが40pxであると想定して決め打ち
}

.nav {
  box-sizing: border-box;
  display: flex;
  align-items: stretch;
  justify-content: space-between;
  position: sticky;
  top: 0;
  left: 0;
  width: 100%;
  padding: 8px;
  background-color: #fff;
  box-shadow: 0 0 6px 0 rgba(0,0,0,0.5);
}

.nav_link {
  color: #06c;
}
```

上記コードで作ったサンプルです。
※埋め込んだCodepenだとうまく追従してなさそうなので、リンクも貼っておきます。
https://codepen.io/yamabero/full/KwKGwbO

<p class="codepen" data-height="400" data-default-tab="html,result" data-slug-hash="KwKGwbO" data-pen-title="scroll in overflow with fixedHeader(css only)" data-user="yamabero" style="height: 400px; box-sizing: border-box; display: flex; align-items: center; justify-content: center; border: 2px solid; margin: 1em 0; padding: 1em;">
  <span>See the Pen <a href="https://codepen.io/yamabero/pen/KwKGwbO">
  scroll in overflow with fixedHeader(css only)</a> by yamabero (<a href="https://codepen.io/yamabero">@yamabero</a>)
  on <a href="https://codepen.io">CodePen</a>.</span>
</p>
<script async src="https://public.codepenassets.com/embed/index.js"></script>

この状態でも、ほぼやりたいことはできてます。
しかしcssだと固定値しか入れられないので、もし追従要素の高さが変わった場合に対応できなくなります。

# css変数とJavaScriptで値を上書きする
`scroll-padding-top`に入れる値をcss変数に変更します。
```css
.content {
  --scrollPadding: 40px;
  width: 300px;
  height: 200px;
  overflow: hidden auto;
  scroll-behavior: smooth;
  scroll-padding-top: var(--scrollPadding);
}

.nav {
  box-sizing: border-box;
  display: flex;
  align-items: stretch;
  justify-content: space-between;
  position: sticky;
  top: 0;
  left: 0;
  width: 100%;
  padding: 8px;
  background-color: #fff;
  box-shadow: 0 0 6px 0 rgba(0,0,0,0.5);
}

.nav_link {
  color: #06c;
}
```

その後、JavaScriptの`setProperty`を使って、追従要素の高さで上書きします。

```JavaScript
// 追従要素
const nav = document.getElementById('nav');

// スクロール要素
const content = document.getElementById('content');

// 追従要素の高さを取得
const navHeight = nav.offsetHeight;

// スクロール要素のcss変数`--scrollPadding`を追従要素の高さでセット
content.style.setProperty('--scrollPadding', navHeight + 'px' );

```
こうすることで、もし追従要素内の高さが変わったとしても対応できました。

上記コードで作ったサンプルです。
※埋め込んだCodepenだとうまく追従してなさそうなので、リンクも貼っておきます。
https://codepen.io/yamabero/full/dPygPPX

<p class="codepen" data-height="400" data-default-tab="html,result" data-slug-hash="dPygPPX" data-pen-title="scroll in overflow with fixedHeader" data-user="yamabero" style="height: 400px; box-sizing: border-box; display: flex; align-items: center; justify-content: center; border: 2px solid; margin: 1em 0; padding: 1em;">
  <span>See the Pen <a href="https://codepen.io/yamabero/pen/dPygPPX">
  scroll in overflow with fixedHeader</a> by yamabero (<a href="https://codepen.io/yamabero">@yamabero</a>)
  on <a href="https://codepen.io">CodePen</a>.</span>
</p>
<script async src="https://public.codepenassets.com/embed/index.js"></script>

このJavaScriptのコードだと、描画時にしか対応できてないので、何かイベントを実行した時に再取得するようにすれば、応用が効くかなと思います。