2019/12/10
意外と簡単!jQueryを使わずにJavaScriptだけでスムーススクロールを実装する(IE11以上版)
スムーススクロールはこれまで以下のようなjQueryを使った方法で実装してきました。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 | var userAgent = window.navigator.userAgent.toLowerCase(); var appVersion = window.navigator.appVersion.toLowerCase(); var scrollBody = (userAgent.indexOf('msie') > -1 || userAgent.indexOf('trident') > -1 || userAgent.indexOf('firefox') > -1 ) ? 'html' : 'body'; if (userAgent.indexOf('chrome') > -1 && userAgent.indexOf('edge') == -1) { let chromeVersion = window.appVersion.match(/chrome\/.* /) chromeVersion = chromeVersion[0].replace('chrome/', '').replace(' ', '') chromeVersion = Number(chromeVersion.split('.')[0]) if (chromeVersion >= 61) { window.g_scrollBody = 'html' } } var duration = 500; $(function () { $('a[href^="#"]').on('click', function (e) { var $targetElement = $('#' + $(this).attr('href')); if ($targetElement.length) { e.preventDefault(); e.stopPropagation(); var targetPosition = $targetElement.offset().top - 115 $(scrollBody).animate({ scrollTop: position }, duration, 'swing'); } }); }); |
スムーススクロールの記述自体はシンプルですが、この方法では、クロスブラウザの対応のためにユーザーエージェントをもとにブラウザごとのスクロール対象の振り分けが必要でした。つい最近もSafariでは追加の振り分けの対応が必要になっています。
この機会にjQueryに依存しない方法を使おうと思い、JavaScriptだけでスムーススクロールを実装する方法を調べてみました。
↓作ってみたもの
DEMO
2021.4.12追記)IE11で使えない記述が混じってましたので、修正しました
方法
実際やってみると、確かにコードは長くなりますが、思ったほど複雑ではなく、
クロスブラウザ対応に至ってはjQueryを使った方法よりも簡潔でした。
ブラウザの標準化が進んできているおかげですね。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 | var Ease = { easeInOut: function (t) { return t<.5 ? 4*t*t*t : (t-1)*(2*t-2)*(2*t-2)+1; } } var duration = 500; window.addEventListener('DOMContentLoaded', function () { var smoothScrollTriggers = [].slice.call(document.querySelectorAll('a[href^="#"]')); smoothScrollTriggers.forEach(function (smoothScrollTrigger) { smoothScrollTrigger.addEventListener('click', function (e) { var href = smoothScrollTrigger.getAttribute('href'); var currentPostion = document.documentElement.scrollTop || document.body.scrollTop; var targetElement = document.getElementById(href.replace('#', '')); if (targetElement) { e.preventDefault(); e.stopPropagation(); var targetPosition = window.pageYOffset + targetElement.getBoundingClientRect().top - 115; var startTime = performance.now(); var loop = function (nowTime) { var time = nowTime - startTime; var normalizedTime = time / duration; if (normalizedTime < 1) { window.scrollTo(0, currentPostion + ((targetPosition - currentPostion) * Ease.easeInOut(normalizedTime))); requestAnimationFrame(loop); } else { window.scrollTo(0, targetPosition); } } requestAnimationFrame(loop); } }); }); }); |
コードの解説つき
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 | // イージング関数 var Ease = { easeInOut: function (t) { return t<.5 ? 4*t*t*t : (t-1)*(2*t-2)*(2*t-2)+1; } } // アニメーションの Duration の設定 var duration = 500; window.addEventListener('DOMContentLoaded', function () { // スムーススクロールのトリガーを取得(IE11はNodeListでforEachが使えないので、[].slice.call()により配列に変換) var smoothScrollTriggers = [].slice.call(document.querySelectorAll('a[href^="#"]')); smoothScrollTriggers.forEach(function (smoothScrollTrigger) { // トリガーをクリックした時に実行 smoothScrollTrigger.addEventListener('click', function (e) { // href属性の値を取得 var href = smoothScrollTrigger.getAttribute('href'); // 現在のスクロール位置を取得(クロスブラウザに対応) var currentPostion = document.documentElement.scrollTop || document.body.scrollTop; // スクロール先の要素を取得 var targetElement = document.getElementById(href.replace('#', '')); // スクロール先の要素が存在する場合はスムーススクロールを実行 if (targetElement) { // デフォルトのイベントアクションをキャンセル e.preventDefault(); e.stopPropagation(); // スクロール先の要素の位置を取得 var targetPosition = window.pageYOffset + targetElement.getBoundingClientRect().top - 115; // headerと余白の分だけずらす // スタート時点の時間を取得 var startTime = performance.now(); // アニメーションのループを定義 var loop = function (nowTime) { // スタートからの経過時間を取得 var time = nowTime - startTime; // duration を1とした場合の経過時間を計算 var normalizedTime = time / duration; // duration に経過時間が達していない場合はアニメーションを実行 if (normalizedTime < 1) { // 経過時間とイージングに応じてスクロール位置を変更 window.scrollTo(0, currentPostion + ((targetPosition - currentPostion) * Ease.easeInOut(normalizedTime))); // アニメーションを継続 requestAnimationFrame(loop); // duration に経過時間が達したら、アニメーションを終了 } else { window.scrollTo(0, targetPosition); } } // アニメーションをスタート requestAnimationFrame(loop); } }); }); }); |
今後はこちらで実装していきたいと思います。
参考にさせていただいたサイト
新しいChromeでスクロールが取れない? scrollingElement? | Ginpen.com
Simple Easing Functions in Javascript – see https://github.com/gre/bezier-easing
Author Profile
NINOMIYA
Webデザイナー兼コーダー出身のフロントエンド開発者です。 UXデザインやチーム開発の効率化など、勉強中です。
SHARE