ページ内スクロール
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^="#"]');
まず1行目のコードではsmoothScrollTriggerを定義しているのだがquerySelectorAllで要素を取得する際の記載が少し見慣れない。
a[href^="#"]
の解説をする。^=
これはビット演算子というもので左辺の属性で右辺の値の前方一致するものを取得するようです。
このように記載することで左辺の属性で右辺の値の前方一致するものを取得するそうです。
今回の場合a[href^="#"]
href属性の中で#から始まるものを取得するということです。
for (let i = 0; i < smoothScrollTrigger.length; i++)
次にfor文で i=0と定義し先ほど取得したsmoothScrollTriggerの数がiより多い場合ループし関数が終わり次第iを+1する
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
で1つ目に指定した文字列を2つ目の指定した文字列に置き換えて値を返している。
今回の場合#を’’
に置き換えている。理由は補足で詳しく説明!
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
より処理速度が7~8倍ほど早いらしく大規模な開発においてはgetElementById
の方が良いらしい。
感想
今回はJavascriptの関数内をしっかり理解して使用するために勉強した😎
ネットには行いたい動作のコードが数分で手に入るがそれを理解して使用することでアレンジを加えたりするので大切なことだと感じた✨
また処理速度についても差があることを知りquerySelector
やquerySelectorAll
はものすごく便利だが処理速度は遅めであるということを知った。大規模な開発をする経験があれば処理速度についても勉強していきたいと思っている😑
正直この数文を理解するのに2時間くらいかかった🥲
脱初心者を1日でも早くするために頑張るぞ!