【スムーススクロール対応】scrollIntoViewで手軽にスライダー作成

# 前置き
以下のような表示領域に複数スライドが表示されるスライダーを作りたいとき・・・

![slider.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/269370/0e12eeab-cd53-1e79-ccc7-1fe24c02b732.png)

要素のscrollLeftやtranslateXの値を操作することが多いのではないかと思います。

しかし、リキッド対応が必要な場合、このscrollLeftやtranslateXを用いた実装方法だと、 resize イベントのたびにスライドの width を再計算し直して、移動させる位置を調整する必要があるので少し手間がかかります。

そんなときには`scrollIntoView`を使うとサクッと実装できちゃいます。

# もっと楽ができる。そう、 scrollIntoView ならね

## scrollIntoViewとは

> Element インターフェイスの scrollIntoView() メソッドは、scrollIntoView() が呼び出された要素がユーザーに見えるところまで、要素の親コンテナーをスクロールします。

>引用元: [scrollIntoView() - MDN](https://developer.mozilla.org/ja/docs/Web/API/Element/scrollIntoView)

以下のように記述すると、要素を見えるところまで持ってきてくれるメソッドです。

```javascript
element.scrollIntoView();
```

引数にboolean値を渡すことで表示要素を表示領域の上端/下端のどちらに来るようにするかを設定できます。
規定値はtrueです。

> - true の場合、要素の上端がスクロール可能な祖先の表示範囲の上端に来るようにスクロールします。 
- false の場合、要素の下端がスクロール可能祖先の表示範囲の下端に来るようにスクロールします。 

>引用元: [scrollIntoView() - MDN](https://developer.mozilla.org/ja/docs/Web/API/Element/scrollIntoView)


## スライダー実装に使う際の注意点

スライダーを作成する場合は、表示させたいスライドに対して`scrollIntoVeiw`メソッドを呼び出すだけでOK！と、言いたいところなのですが、1点注意が必要です。

`scrollIntoView();`を実行するだけだと、

- スライダーだけでなく画面全体がスクロールしてしまう
- スムーススクロールの対応ができない
- スライドを表示領域の中央に表示する、などの位置調整ができない

という問題が起こります。
以下は上記の問題が起こっている実装例です。
<p class="codepen" data-height="265" data-theme-id="dark" data-default-tab="html,result" data-user="kjkj_ongr" data-slug-hash="vYXWLNR" style="height: 265px; box-sizing: border-box; display: flex; align-items: center; justify-content: center; border: 2px solid; margin: 1em 0; padding: 1em;" data-pen-title="scrollIntoViewSlider">
  <span>See the Pen <a href="https://codepen.io/kjkj_ongr/pen/vYXWLNR">
  scrollIntoViewSlider</a> by yuki-kojima (<a href="https://codepen.io/kjkj_ongr">@kjkj_ongr</a>)
  on <a href="https://codepen.io">CodePen</a>.</span>
</p>
<script async src="https://production-assets.codepen.io/assets/embed/ei.js"></script>

これはいかん。

これを解決するためには、`scrollIntoView`の引数にオプションとして渡せる`scrollIntoViewOptions`を指定する必要があります。

## scrollIntoViewOptionsとは

IE と Safari を除くブラウザで対応している、`scrollIntoView`オプション設定です。
boolean値の代わりに、以下のように`scrollIntoViewOptions`を引数で渡すことで、対象要素を表示させる位置とスムーススクロールの有無を設定できます。

```javascript
element.scrollIntoView({
  behavior: "smooth",  // スムーススクロールを有効化: auto/smooth
  block: "end",  // 上下方向の位置: start/center/end/nearest
  inline: "start" // 水平方向の位置: start/center/end/nearest
});
```

先述した通り、引数を渡さない場合はtrueを渡した状態がセットされるのですが、`scrollIntoViewOptions`で言う所の`{block: "start", inline: "nearest"}`が適用されてしまうため、画面全体が上部にスクロールしてしまったのでした。

スライダー実装時は、`block: "nearest"`とすることで画面全体がスクロールしてしまうことを防ぎます。

以下はクリックしたボタンに応じたスライドを表示するスライダーを`scrollIntoView`を用いて実装した例です。
対象要素の水平方向の表示位置をスライダーの中央とし、スムーススクロールを適用しています。
一応リキッド対応もしております。

<p class="codepen" data-height="265" data-theme-id="dark" data-default-tab="html,result" data-user="kjkj_ongr" data-slug-hash="qBaVbPp" style="height: 265px; box-sizing: border-box; display: flex; align-items: center; justify-content: center; border: 2px solid; margin: 1em 0; padding: 1em;" data-pen-title="scrollIntoViewSlider-options">
  <span>See the Pen <a href="https://codepen.io/kjkj_ongr/pen/qBaVbPp">
  scrollIntoViewSlider-options</a> by yuki-kojima (<a href="https://codepen.io/kjkj_ongr">@kjkj_ongr</a>)
  on <a href="https://codepen.io">CodePen</a>.</span>
</p>
<script async src="https://production-assets.codepen.io/assets/embed/ei.js"></script>


これでスライダーもお手軽に実装できちゃいますね。
そう、 IE と Safari 以外なら。

# 先生、IEとSafariでもscrollIntoViewOptionsを使いたいです
そんな時におすすめなのは[scroll-into-view-if-needed](https://www.npmjs.com/package/scroll-into-view-if-needed)と[smooth-scroll-into-view-if-needed](https://www.npmjs.com/package/smooth-scroll-into-view-if-needed)。

## scroll-into-view-if-neededとは
まだ標準化されていない`scrollIntoViewIfNeeded`のPonyfill[^1]のnpmパッケージです。

`scrollIntoViewIfNeeded`は、表示したい要素が表示領域にない場合だけスクロールするという仕様のもので、要素を表示領域に持ってきてくれる、という点は基本的に`scrollIntoView`と同じです。

[scrollIntoViewIfNeeded - MDN](https://developer.mozilla.org/en-US/docs/Web/API/Element/scrollIntoViewIfNeeded)

このパッケージを使えば、`scrollIntoView`をほぼ同じOptionで表示位置・スムーススクロールの有無を設定できます。
呼び出し方についても、表示したい要素を第一引数に渡す以外は`scrollIntoView`とほぼ同じです。

```javascript
import scrollIntoView from 'scroll-into-view-if-needed'

scrollIntoView(taregtElement, {
  behavior: "smooth",  // スムーススクロールを有効化: auto/smooth
  block: "end",  // 上下方向の位置: start/center/end/nearest
  inline: "start" // 水平方向の位置: start/center/end/nearest
});
```
ただし注意したいのは、`behavior`オプションを`smooth`にしても、スムーススクロール対応のブラウザでしか有効になりません。
IEやSafariなどでもスムーススクロールを実装したい場合は、 smooth-scroll-into-view-if-needed を使います。

## smooth-scroll-into-view-if-needed とは
`scroll-into-view-if-needed`のスムーススクロールも Ponyfill してくれるパッケージです。同じ開発者によるパッケージで、利用方法も全く同じです。
全ブラウザでスムーススクロールを有効にすることが優先ならば、こちらを利用しましょう。

```javascript
import smoothScrollIntoViewIfNeeded from 'smooth-scroll-into-view-if-needed'

smoothScrollIntoViewIfNeeded(taregtElement, {
  behavior: "smooth",  // スムーススクロールを有効化: auto/smooth
  block: "end",  // 上下方向の位置: start/center/end/nearest
  inline: "start" // 水平方向の位置: start/center/end/nearest
});
```
ちなみにIE対応する場合は、`Promise`のPolyfillが必要なので適宜追加してください。

## IEとSafariでもスムースなスライダーを作ってみる

以下は smooth-scroll-into-view-if-needed を用いてスライダーを作った例です。

<p class="codepen" data-height="265" data-theme-id="dark" data-default-tab="html,result" data-user="kjkj_ongr" data-slug-hash="poEdyoe" style="height: 265px; box-sizing: border-box; display: flex; align-items: center; justify-content: center; border: 2px solid; margin: 1em 0; padding: 1em;" data-pen-title="smooth-scroll-into-view-if-needed-slider">
  <span>See the Pen <a href="https://codepen.io/kjkj_ongr/pen/poEdyoe">
  smooth-scroll-into-view-if-needed-slider</a> by yuki-kojima (<a href="https://codepen.io/kjkj_ongr">@kjkj_ongr</a>)
  on <a href="https://codepen.io">CodePen</a>.</span>
</p>
<script async src="https://production-assets.codepen.io/assets/embed/ei.js"></script>

実にスムースですね。（※個人の感想です）

# まとめ
- スライダー実装には`scrollIntoView`が便利
- どのブラウザでも`scrollIntoViewOptions`を使いたい場合は[scroll-into-view-if-needed](https://www.npmjs.com/package/scroll-into-view-if-needed)
- どのブラウザでもスムーススクロールを有効にしたい場合は[smooth-scroll-into-view-if-needed](https://www.npmjs.com/package/smooth-scroll-into-view-if-needed)

2020年ももう終わりですね。1年の思い出をスライダーにしてみてはいかがでしょうか。

[^1]: Ponyfillについては[こちら](https://github.com/sindresorhus/ponyfill)を参照ください