Javascriptでのページ内スクロール

# ページ内スクロール
Webページ作成時にそれぞれのsectionにidを付与してnavのリストのaタグからページ内での遷移をスクロールで行いオシャレにしたい✨
よく見る光景なのでコードは調べたらすぐに出てきた😋

```
const smoothScrollTrigger = document.querySelectorAll('a[href^="#"]');
for (let i = 0; i < smoothScrollTrigger.length; i++){
  smoothScrollTrigger[i].addEventListener('click', (e) => {
    e.preventDefault();
    let href = smoothScrollTrigger[i].getAttribute('href');
    let targetElement = document.getElementById(href.replace('#', ''));
    const rect = targetElement.getBoundingClientRect().top;
    const target = rect + window.pageYOffset;
    window.scrollTo({
      top: target,
      behavior: 'smooth',
    });
  });
}
```

これで実装はできたがコードの理解ができていなかったため今回はコードの理解を深めることを目的として投稿する！


## コード解説

```
const smoothScrollTrigger = document.querySelectorAll('a[href^="#"]');
```
まず１行目のコードではsmoothScrollTriggerを定義しているのだがquerySelectorAllで要素を取得する際の記載が少し見慣れない。
`a[href^="#"]` の解説をする。~~`^=`これはビット演算子というもので左辺の属性で右辺の値の前方一致するものを取得するようです。~~
このように記載することで左辺の属性で右辺の値の前方一致するものを取得するそうです。
今回の場合`a[href^="#"]` href属性の中で#から始まるものを取得するということです。
* * *
```
for (let i = 0; i < smoothScrollTrigger.length; i++)
```
次にfor文で　i=0と定義し先ほど取得したsmoothScrollTriggerの数がiより多い場合ループし関数が終わり次第iを＋１する
* * *
```
smoothScrollTrigger[i].addEventListener('click', (e) => { });
```
smoothScrollTrigger[i]がi番目の時addEventListenerで第一引数にクリックされた時、第二引数で(e)の関数を渡しイベントを処理している。
（e）はイベントの略
第二引数の関数の中でスクロール処理を記載する。

* * *
``` 
e.preventDefault();
```
まず初めに`preventDefault()`メソッドではデフォルトの動作をキャンセルする役割がある。今回の場合だと`aタグ`のデフォルトのページ遷移をキャンセルして新たに定義してあげる!

```
let href = smoothScrollTrigger[i].getAttribute('href');
```
変数　hrefにsmoothScrollTrigger[i]の`href`属性の値を`getAttribute`で取得定義してあげます。`getAttribute()`メソッドは、この要素の指定された属性の値を返す。
```
let targetElement = document.getElementById(href.replace('#', ''));
```
変数　targetElementにdocumentの`getElementById`メソッドで先ほど定義したhrefの要素のオブジェクトを返している。その際にStringオブジェクトのメソッドである`replace` で１つ目に指定した文字列を２つ目の指定した文字列に置き換えて値を返している。
今回の場合`#を’’`に置き換えている。理由は補足で詳しく説明！
```
const rect = targetElement.getBoundingClientRect().top;
```
定数　rectに先ほど定義したtargetElementの座標を`getBoundingClientRect()`メソッドで取得している。
`getBoundingClientRect`メソッド指定した要素を、表示領域の左上を(0, 0)として、相対位置で示される。
`.top`はプロパティで要素の上端の Y-座標,の値の位置を返している。
```
const target = rect + window.pageYOffset;
```
まずは`window.pageYOffset`の解説。
`pageYOffset`とはWindowプロパティであり縦方向のスクロールの量をピクセル単位で返している。
定数 targetが何を定義しているか解説する。
先ほど定義したrectは要素のトップの相対的な座業を持っている。しかしこの座標は画面の左上をからの位置であり、スクロールすると値は変化してしまう。
🔔わかりやすく例えると、遷移したい要素のtopが画面上から1000位置にあるとする。これがrectの値。しかしスクロールで300進んでいたとするとrectだけの記載だと300の誤差が出てくる。その誤差を埋めるために`window.pageYOffset`を使用し画面のどの位置にいてもしっかり遷移したい場所に値がズレることなく遷移できる。

自分の制作物の場合だと、headerを固定しているので上記に記載だと要素のとheaderが重なるので
```
const target = rect + window.pageYOffset -55;
```
として55px分更に下に遷移するように指定している。
思うような場所にスクロールできない場合は好きな数値で適切に調整もできる。

```
window.scrollTo({
      top: target,
      behavior: 'smooth',
    });
```
`scrollTo`とはwindowオブジェクトのメソッドであり書内の特定の組み合わせの座標までスクロールする。
今回の場合topを先ほどスクロールしたい座標の値を格納したtargetにし、behaviorではスクロールの仕方を指定している。今回の`smooth`だと時間をかけて円滑にスクロールする。デフォルトだと`auto`であり瞬時にスクロールする。
***
## 補足
```
let targetElement = document.getElementById(href.replace('#', ''));
const rect = targetElement.getBoundingClientRect().top;
```
①`replace('#', '')`でメソッドを使って#をとる理由
②`querySelector`での置き換えについて
について解説。

①`replace`メソッドで`href`属性の#を取る理由としてはsectionにそれぞれのidを付与していてそのidの位置を取得してスクロールすることを目的をしているが、aタグの`href`属性には`#~~`のように#でidに遷移するように記載してある。そのため`replace`メソッドを使わなって#を外さないと`getElementById`によって`#~~`というidを取得しようとして結果としてtargetElementが`null`となってしまう。
idには`~~`と指定しているため！

②自分は普段`querySelector`を理解せず汎用性が高いため理解せず使用していた。今回も同様に`getElementById`を`querySelector`に置き換えた方が個人的に可読性が上がるため置き換えたところtargetElementの結果が`null`になってしまう。
```
let targetElement = document.querySelector(href);
```
解答としては`replace`メソッドを外せば問題なく動作した。
しかし、メソッドを外す意味が理解できずに困惑していた。メソッドを使用し#を外して指定したidの名称と一致させてidを取得しても問題なく動作しそうだと思っていたからである。
これは単純な問題で`querySelector`でidを取得する場合は#をつける必要があり、取り除いてしまうと取得できなくなるからである。

なぜ`getElementById`を使用してわざわざメソッドまで使用しているかというと処理速度に差があるためである。`getElementById`は`querySelector`より処理速度が７~8倍ほど早いらしく大規模な開発においては`getElementById`の方が良いらしい。
***

### 感想
今回はJavascriptの関数内をしっかり理解して使用するために勉強した😎
ネットには行いたい動作のコードが数分で手に入るがそれを理解して使用することでアレンジを加えたりするので大切なことだと感じた✨
また処理速度についても差があることを知り`querySelector`や`querySelectorAll`はものすごく便利だが処理速度は遅めであるということを知った。大規模な開発をする経験があれば処理速度についても勉強していきたいと思っている😑
正直この数文を理解するのに2時間くらいかかった🥲
脱初心者を１日でも早くするために頑張るぞ！