jQueryとCSSのスムーススクロールの作り方・使い方

更新2023/10/27

ほとんどのサイトにあるのではないかと思うくらい普遍的なスムーススクロールの実装メモです。
JS(jQuery)を使ったものとCSSでも実装できるパターンをご紹介します。

スムーススクロールとは

リンクやボタンなどをクリックして、ページ内の特定の場所に自動的に移動させたいときに瞬時にその場に行くのではなく、スルスルーとスクロールしている動きを見せるものをスムーススクロールと言います。

JSでもCSSでも実装できるので、用途に合わせて使えるようにしていきましょう。

CSSでのスムーススクロール

CSSでスムーススクロールをさせるには scroll-behavior: smooth; というプロパティを使用します。

IEやOpera Miniといったブラウザでは使用できませんが、それ以外のブラウザは対応しています。(対応していない古いバージョンもありますが、最新バージョンでは使用できます)

“scroll-behavior” | Can I use…

全体的にスムーススクロールをかける場合は <html> タグにCSSを指定します。

html {
  scroll-behavior: smooth; 
}

これだけでページ内リンクがスムーススクロールになります。

全体的にかけたい場合は <html> タグに指定します。 <body> タグに記述しても反映されないので記述場所に注意です。

ページ移動の際にも、https://example.com/#example といったハッシュ付きリンクの場合は始めにページの上部を表示してからその場所までスムーススクロールで移動します。

Tabキーなどによってフォーカス移動する時もスムーススクロールになります。もしフォーカスできる部分が離れている場合、どれだけ移動したかがわかりやすいので便利です。

scroll-behavior プロパティで実装した場合、URLに アンカーリンク #*** が付きます。JSでURLの状態を取得して何かをするなら、スムーススクロールもJSでの実装が無難かもしれません。

scroll-behavior: smooth; を使用した場合、ブラウザの仕様によって動作が固定されます。スクロールの速度やスクロールの動き方(イージング)の指定はできません。移動先やスクロールの動きを細かく調整したい場合でも変更ができません。

こういったことを踏まえてCSSで実装するかJSで実装するかを考えた方が良さそうです。

固定ヘッダーがある場合

固定ヘッダーがあると、スクロール場所に移動した際にヘッダーの高さ分マージンがないとかぶってしまいます。scroll-behavior: smooth; を使用してスムーススクロールにした場合、この重なりを避けるために scroll-margin-top というプロパティを使用します。

:target {
  scroll-margin-top: ヘッダーと重ならない高さ;
}

:target という疑似クラスは id を持つ要素に当てはまります。

:target – CSS: カスケーディングスタイルシート | MDN

jQueryでのスムーススクロール

jQueryでの実装のメリットはやはりイージングやスクロール速度が調整できる点でしょう。この調整が必須だったり、scroll-behavior に対応していないブラウザでも動作させることが必須ならJSで実装をします。

function smoothScroll() {
  $('a[href^="#"]').click(function(){
    var speed = 500;
    var href = $(this).attr("href");
    var target = $(href == "#" || href == "" ? 'html' : href);
    var position = target.offset().top;
    $("html, body").animate({scrollTop:position}, speed, "swing");
    return false;
  });
}

$(function() {
  smoothScroll();
});

var href = $(this).attr("href"); でクリックした要素の href属性の値を、変数 href に入れています。

var target = $(href == "#" || href == "" ? 'html' : href); は「href」が # もしくは何もない場合は html、それ以外は href の値を、変数 target に入れています。

$("html, body").animate({scrollTop:position}, speed, "swing"); では、animateメソッドを使って移動する位置・アニメーションの実行時間・イージング(速度の緩急)を指定します。

アニメーションの実行時間は指定しない場合は400ミリ秒(0.4秒)で、数字が大きくなるほど遅くなります。数字以外に fast(200ミリ秒) や slow(600ミリ秒)といった値も指定できます。
イージングは swing(最初と最後がゆっくりで途中が早い) もしくは linear(最初から最後まで一定) が指定できます。デフォルトが swing なので swing の動作でよければ省略してもOK。

【参考】.animate() | jQuery API Documentation

はじめに html と body を指定するのはブラウザによって認識されるタグが異なるようで、幅広い動作環境に対応できるように両方を指定します。

jQuery でもページ移動後にスムーススクロールさせたい!という人はこちらの記事が参考になります。
【jQuery】ページ遷移後にスムーススクロールする

固定ヘッダーと重ならないようにする

上部に固定した要素がある場合、上記のコードでは重なってしまうことも。
その場合、移動させる位置の調整が必要です。

target.offset().top のところに固定されている要素の高さ分ずらします。

var position = target.offset().top - 100;

// 変数に入れる場合
var buffer = 100;
var position = target.offset().top - buffer;

jQueryのスムーススクロールが動作しない?

以前 jQueryでスムーススクロールを実装した際に、コードを書いている・jsファイルの読み込みもちゃんとされているのにも関わらず動かないということがありました。

デベロッパーツールを見ると「Uncaught Error: Syntax error, unrecognized expression: a[href^=#]」というエラーになっていたのです。
これは「シンタックスエラーだよ。この書き方→a[href^=#]は認識できない」と言われています。

この時に $('a[href^=#]').click(function() という形で書いていましたが、これはjQuery1.12以降だとエラーになります。# にクオーテーションが必要なのです。
正しくは $('a[href^="#"]').click(function() と記述します。

古いコードをコピペしてそのまま使用していたので、jQueryのバージョンを変更した際に起こってしまったものです。
今でももし同じようなエラーに遭遇してしまった方は修正してみてください。

カテゴリー : HTML-CSS

TOPへ