アンカーリンクの smooth scroll の実装

# 概要
smooth scroll（ページ内のリンク先に、スルスルとスクロールする動き）の実装についてのメモ。



# jQueryで実装

jQeryの場合は、`animate`メソッドで、実装できる。
`animate`の第一引数で動作、第2引数でduration（動作にかかる時間）、第3引数にeasing を指定する。

## サンプル

```index.html
<p><a class="js-scroll" href="#hoge">ページ内リンク</a></p>

```

```index.js
$(function() {
  $('.js-scroll').on('click',(e) => {
    e.preventDefault();
    let $target = $($(e.currentTarget).attr('href'));
    if ($target.length > 0) {
      $('html, body').animate({
        scrollTop: $target.offset().top
      }, 500, 'swing');
    }
  })
})
```

# JavaScriptのみで実装（IE非対応）

`scrollIntoView()`は、ターゲットとなる要素が見えるまで、親要素をスクロールさせるメソッド。
optionで、`behavior: "smooth"`を指定すると、スルスルの実装が簡単にできる。（IEは非対応。。）


## サンプル

```index.js
document.addEventListener('DOMContentLoaded', () => {
  let elms = document.querySelectorAll('.js-scroll');
  if (elms) {
    elms.forEach((elm) => { 
      elm.addEventListener('click', (e) => {
        e.preventDefault();
        const target = document.querySelector(e.currentTarget.getAttribute('href'));
        if (target) {
          target.scrollIntoView({
            behavior: "smooth",
          });
        }
      })
    })
  }
})
```

## 参考
scrollIntoView: https://developer.mozilla.org/ja/docs/Web/API/Element/scrollIntoView


# JavaScriptのみで実装（IE対応）

`setTimeout`を駆使して実装。
`setTImeout`の第1引数に所定の時間がすぎた後に実行する関数を指定し、第2引数で`delay`を指定する。第3引数以降は、第1引数で指定した関数に渡す引数。下の例だと、500msecを100分割した5msecを`interval`として定義し、5msec毎に、`doScroll`関数を実行している。

## サンプル

```index.js
document.addEventListener('DOMContentLoaded', () => {
  var elms = document.querySelectorAll('.js-scroll');
  var speed = 500;    //スクロール時間
  var divisor = 100;  //分割数
  var tolerance = 5;  //誤差
  var headerHeight = 0;  //ヘッダなどのオフセットを必要に応じ指定
  var interval = speed / divisor;  // speedを指定したかずで分割

  Array.prototype.forEach.call(elms, function(elm) { 
    elm.addEventListener('click', function(e) {
      e.preventDefault();
      var nowY = window.scrollY;  //Y方向の現在のスクロール量
      var href = e.currentTarget.getAttribute('href');
      var target = document.querySelector(href);
      if (target) {
        var targetRectTop = target.getBoundingClientRect().top;
        var targetY = targetRectTop + nowY - headerHeight;  //ターゲットの位置
        var minY = Math.abs((targetY - nowY) / divisor);  //現在の位置からターゲットまでのスクロール量を分割
        doScroll(minY, nowY, targetY, tolerance, interval);
      }
    })
  })
  var doScroll = function (minY, nowY, targetY, tolerance, interval) {
    var toY;
    if (targetY < nowY) {
      toY = nowY - minY;
    } else {
      toY = nowY + minY;
    }
    window.scrollTo(0, toY);
    if (targetY - tolerance > toY || toY > targetY + tolerance) {
      window.setTimeout(
        doScroll,
        interval,
        minY,
        toY,
        targetY,
        tolerance,
        interval
      );
    } else {
      return false;
    }
  };

}
```


# DEMO

上記、3パターンのDEMOです。

<p class="codepen" data-height="300" data-default-tab="js,result" data-slug-hash="oNooZze" data-user="iiishokoiii" style="height: 300px; 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/iiishokoiii/pen/oNooZze">
  Sample Smooth Scroll</a> by iiishokoiii (<a href="https://codepen.io/iiishokoiii">@iiishokoiii</a>)
  on <a href="https://codepen.io">CodePen</a>.</span>
</p>
<script async src="https://cpwebassets.codepen.io/assets/embed/ei.js"></script>