クリックで指定要素が画面中央に来るようにスクロールする

#概要
クリックで指定の要素が画面中央に来るようにスクロールする動きを実装したときのメモです。

今回はアンケートなどの「ラジオボタンでたくさん選択するようなフォーム」で、回答すると次の質問が画面中央にスクロールされていく、というものを作りました。

▼こんなかんじ
![2c6f4e446e97d3d19f14aa5aef8c66a0.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/655954/d2e31c86-d0c1-d6f4-66f6-47b9d0d905cf.gif)


#実装
HTMLは以下のようになっています。

`<li class="qs_col">～</li>`が1つの質問のブロックになっているので、
**「質問1のラジオボタンを選択すると、スクロールして質問2が画面中央に表示される」**
ようにしてみます。

```index.html
<form method="POST" action="">
  <li class="qs_col">
    <h2>質問1.～～～～～～～～～～ですか？</h2>
    <input type="radio" id="qs01_01" name="qs01" value="0">
    <label for="qs01_01">はい</label>
    <input type="radio" id="qs01_02" name="qs01" value="1">
    <label for="qs01_02">いいえ</label>
  </li>
  <li class="qs_col">
    <h2>質問2.～～～～～～～～～～ですか？</h2>
    <input type="radio" id="qs02_01" name="qs02" value="0">
    <label for="qs02_01">はい</label>
    <input type="radio" id="qs02_02" name="qs02" value="1">
    <label for="qs02_02">いいえ</label>
  </li>
  <li class="qs_col">
    <h2>質問3.～～～～～～～～～～ですか？</h2>
    <input type="radio" id="qs03_01" name="qs03" value="0">
    <label for="qs03_01">はい</label>
    <input type="radio" id="qs03_02" name="qs03" value="1">
    <label for="qs03_02">いいえ</label>
  </li>
  <div class="f_btn">
    <button type="submit">送信</button>
  </div>
</form>

<script src="./scripts/scroll.js"></script>
```
　
スクロールを実装します。
フォームはラジオボタンのみなので、クリックを検知してINPUTタグであれば処理を実行します。

functionを作ってinput側に`onclick`を記述するのも考えましたが、実際にはラジオが50個以上あり、全てに入れるのが嫌だったので`addEventListener`でのイベント検知にしています。

```scroll.js

window.addEventListener('click', (event) => {
    // クリックした要素のタグを取得
    var tag = event.target.tagName;

    if (tag == 'INPUT') {
        // クリックした質問の次のli要素を取得
        var id = event.target.id;
        var element = document.getElementById(id).closest('li.qs_col').nextElementSibling;

        // 最後の質問は次のliが無いので送信ボタンエリアを取得
        if (element == null) {
            element = document.querySelector('.f_btn');
        }
        // 要素が画面中央に来るようにスクロール
        element.scrollIntoView({behavior: 'smooth', block: 'center'});
    }
});
```

これで、ラジオボタンで回答をクリックすると次の質問ブロックにスクロールします。

#解説
今回はスクロールに[Element.scrollIntoView()](https://developer.mozilla.org/ja/docs/Web/API/Element/scrollIntoView)というメソッドを使いました。
scrollIntoView()が呼び出された要素がユーザーに見えるところまで、要素の親コンテナーをスクロールする、というものです。

最初は次のスクロール位置を`offset().top`などで取得して`scrollTop()`を使おうとしたのですが、「要素が画面中央に来るようにスクロール」するためには位置計算などが必要になります。

Element.scrollIntoView()ではそのような計算を使わず、簡単に位置指定することができます。

##Element.scrollIntoView()
Element.scrollIntoView()には3つのオプションがあります。

- behavior：アニメーションの設定
- block：（通常は）垂直方向のスクロール位置設定
- inline：（通常は）水平方向のスクロール位置設定

詳細な設定については[こちら](https://qiita.com/amamamaou/items/728d571d508347b2bc82)で詳しく解説されています。

```javascript
// オプションの設定
element.scrollIntoView({behavior: "smooth", block: "end", inline: "nearest"});

// 今回の場合（アニメーションあり、中央配置）
element.scrollIntoView({behavior: 'smooth', block: 'center'});
```

今回は画面中央でしたが、上端・下端に合わせることも可能です。

##各ブラウザ対応
`scrollIntoView`のオプションはSafariやIEなど一部のブラウザでは対応していません。
そのため、`behavior: 'smooth'`を指定してもスムーズスクロールが適用されません。
上記を有効にするためにポリフィルを使う必要があります。

###ポリフィルの利用
ポリフィルとは「対応していないブラウザーで、その機能を使えるようにするためのコード」です。
Javascriptなどを古いブラウザや非対応ブラウザなどでも動くようにしてくれるものになります。

- [Github - Smooth Scroll behavior polyfill](https://github.com/iamdustan/smoothscroll)

npmなどを使う方法も記述されていますが、ソースダウンロードでも実装できます。
[こちら](https://unpkg.com/smoothscroll-polyfill@0.4.4/dist/smoothscroll.min.js)からポリフィルをダウンロードして、スクロールのjsより先に読み込めばOKです。

```index.html
<script src="./scripts/smoothscroll.min.js"></script>
<script src="./scripts/scroll.js"></script>
```
　
これで実装は以上です！

#参考
**【メソッドについて】**

- [Element.scrollIntoView()](https://developer.mozilla.org/ja/docs/Web/API/Element/scrollIntoView)
- [あまり知られてなさそうなメソッド element.scrollIntoView()](https://qiita.com/amamamaou/items/728d571d508347b2bc82)

　
**【ポリフィルについて】**

- [Polyfill (ポリフィル)](https://developer.mozilla.org/ja/docs/Glossary/Polyfill)
- [【jQueryなし】素のJSでトップに戻るスムーススクロールボタンを実装する方法](https://web-dev.tech/front-end/javascript/smooth-scroll-top-button/#index_id4)