JavaScriptだけで実装する、ページ内リンクのスムーズスクロール(はてなブログ仕様)
前回、jQuery で脚注もスクロールで移動するようにするスクリプトを紹介したのですが、この動作だけで jQuery を使うのはちょっと…、などと思うことがあるかもしれません。
わたしもどちらかと言えばそんな方です。なので、いろいろ調べてみてJavaScriptだけで作ってみました。
まあ、ページ内リンクのスムーズスクロールとなると、jQuery のほうが断然簡単に書けて楽なのですけどね(笑)
でもまあ、何事もチャレンジ。なので、作ってみたスクリプトを紹介したいと思います。
JavaScriptだけで「はてなブログ」の脚注にも対応した、ページ内リンクのスクロール移動
最近では JavaScript も jQuery のように書けるものが増え、以前に比べたら非常にシンプルに書けるようになりましたね。
ちょっとだけ長いですが、書いたコードはこちらです。あ、ちなみに対応ブラウザは日本でメジャーなブラウザのそれぞれの最新版を想定しています。
//移動時間(1 = 1000分の1秒(1ミリ秒))
const move_time = 500;
//繰り返す回数(アニメーションの回数)の計算
const end_time = move_time / 1000 * 60;
//イージング計算式( easeInOutQua )
const easing = function ( t, b, c, d) {
if ((t/=d/2) < 1) return c/2*t*t + b;
return -c/2 * ((--t)*(t-2) - 1) + b;
};
//移動関数(アニメーション実行関数)
function pageScroll( pos ){
let xPos = window.pageXOffset
, yPos = window.pageYOffset
, moved = pos - yPos
, time = 1;
(function scrollMoved() {
window.scrollTo( xPos, easing( time, yPos, moved, end_time) );
time ++ ;
if ( time <= end_time ) {
window.requestAnimationFrame( scrollMoved );
}
})();
};
//ハッシュからエレメントを取得する関数
function getElm( h ){
let deCode = decodeURI( h );
return document.querySelector( deCode + ',a[name="' + deCode.substr(1) + '"]');
}
//移動先の位置を取得する関数
function getPos( t ){
let tPos = t.getBoundingClientRect()
return tPos.top + window.pageYOffset;
};
//ページ内リンクを取得
const entryPageLinks = document.querySelectorAll( 'a[href^="#"]' );
//ページ内リンクにイベント登録
if ( entryPageLinks.length ) {
for( var i = 0; entryPageLinks.length > i; i++ ) {
entryPageLinks[i].addEventListener( 'click', function(e){
let href = e.target.hash;
if ( ( href != '' ) && ( href != '#') ) {
let targetElm = getElm( href );
if ( targetElm ) {
( e.preventDefault ) ? e.preventDefault(): e.returnValue = false;
pageScroll( getPos( targetElm ) );
return false;
}
}
});
}
}
そのページの「トップに戻る」以外のページ内リンクを取得して、スームズスクロールで移動します。「トップに戻る」やページ内リンクのリンク先が無いものではプログラムは動かずデフォルトの動きをします。
以下、簡単な解説などを書いておきます。
解説など
プログラムの内容的には、調べれば出てくるコードをいくつかくっつけたようなものです。
ただ、はてなブログに使うために、ちょびっとだけ工夫したりしてますので、それらも含めて少し解説。
繰り返す回数(アニメーションの回数)
スムーズスクロールにするにあたり、繰り返し実行するアニメーション処理は当然必要。
繰り返し処理を行う方法は JavaScript では3通りほど*1ありますが、今回はrequestAnimationFrame
にしました。
このメソッドは基本的に1秒間に60フレーム相当の動きをするそうなので、それに合わせて移動時間分の繰り返し回数を、下のように計算しています。
詳しいことは調べていただいたら分かりますが、このrequestAnimationFrame
の場合、ブラウザとかのパフォーマンスによっては、繰り返す間隔の時間がマチマチになることがあるそうです(その分、低負荷)。
ただ、ページ内リンクのスムーズスクロールは長くても一秒にも満たない短い時間。これなら移動時間に多少差があっても、それほど気にならないかなか?と、今回はrequestAnimationFrame
にしています。
イージング計算式
このようなスクロールをするときは、やっぱイージングでしょうということで、イージング( easing )を導入しています。
イージングに関しては、ここで紹介するなんかより詳しく分かりやすい解説ががネットの中にはたくさんあります。
参考になるサイトは、以下のようなものでしょうか。
- http://easings.net/ja
- Robert Penner's Easing Functions
- イージングの仕組み - イージングとはなにか | CodeGrid
- tweenについて、easingについて – nackpan Blog
今回のプログラムには、イージングでも分かりやすい「 easeInOutQuad 」というものにしています。他の動きにするときは、「イージング計算式」関数の中の計算式を、このリンク先とかにあるイージングの計算式だけを入れ替えれば、そのイージングの動きになります。
ただ、これらの場合、 jQuery の「 swing 」とまったく同じ動きのものは多分無いと思います。たぶん計算式から見て1番近いか同じなのは「easeInOutSine」だと思うのですが…。(間違っていたらゴメンナサイ)
リンク先の取得
ページ内リンクのリンク先取得は、通常の id で、id 名も半角英数で設定していたら、これといって難しくも、ややこしくもありません。
ですが、はてなブログでは「脚注」は name 属性、「目次」は id ですが日本語です。日本語の id 名だとメソッドで検索しても、ひっかかったりしない場合も多々あります。
なので、リンク元から得たハッシュは、decodeURI
でエンコードした後、querySelector(
で id と name の複数指定で検索しています。
正直、id で統一してくれるだけでも大分スッキリするのですけど、システム上なかなか難しいようですね。
簡単な解説は以上です。
複雑なことはあまりしていませんが、していないだけに重くはないと思います。
今回もサンプルページを用意していますので、ご参照下さい。まあ、動作的には前回のサンプルページとほぼ変わりはないですが(笑)
リンク:サンプルページ
以上、何かのお役に立てれば幸いです。
*1:他は setIntervalと setTimeout 。