リンクをクリックしてサイト内をジャンプするとき、キュルキュルーって動きながら移動するのを見ます。
スムーズスクロールといいます。CSSの1行追加でできるようになりました。
いま一番かんたんな実装方法です。ただひとこと言いたいのは『頼むぜ。Apple!』。
これまで、スムーズスクロールはJavaScriptで実装していました。それがCSSでできちゃうなんて。
ただ未対応ブラウザがあるので、JSがまったくいらないことにはなりません。
CSSでスムーズスクロール
かんたんなので、いきなりCSSコードから。
html {
scroll-behavior: smooth;
}
@media (prefers-reduced-motion: reduce) {
html {
scroll-behavior: auto;
}
}
サイト全体で使うためにhtmlタグに当ててますが、ピンポイントに当てることもできます。
また最初の3行だけでいいのですが、OSのアニメーションの設定で、最小化や禁止になっているときの設定も入れました。
(メディアクエリのprefers-reduced-motion)
最近のWebサイトは動きのあるリッチなものが増えました。でも逆に、派手な動きをつけるものが増えて、画面酔い、アニメーション酔いが問題になりました。
それを抑えるための設定がOSにはあります。
対応できないブラウザ
しかしscroll-behaviorプロパティは、いまだ対応していないブラウザがあります。(2021年5月30日 現在)
IE
Safari
Opera
SafariはMacOS, iPhoneの標準ブラウザです。Mac, iPhoneが人気の日本ではキツイですね?
(IEはいずれ消える。Operaはそこまでシェアがない。)
そこで、未対応ブラウザはJavaScriptを使います。
JavaScriptのスムーズスクロール
対応できないものはJavaScriptでやっちゃいましょう。2つのコードを作成します。
ひとつは、ブラウザ判別処理、もう一つはスムーズスクロールです。
/**
* ブラウザ判定
*/
function isEdge() {
// Chrome, Safariが入っている。
const agent = window.navigator.userAgent.toLowerCase();
return agent.indexOf("edge") !== -1;
}
function isChrome() {
// Safariが入っている。
if (isEdge()) return false;
if (isSafari()) return false;
const agent = window.navigator.userAgent.toLowerCase();
return agent.indexOf("chrome") !== -1;
}
function isSafari() {
const agent = window.navigator.userAgent.toLowerCase();
const judge1 = agent.indexOf("chrome") !== -1;
if (judge1) return false;
return agent.indexOf("safari") !== -1;
}
function isFirefox() {
const agent = window.navigator.userAgent.toLowerCase();
return agent.indexOf("firefox") !== -1;
}
function isIE11() {
const agent = window.navigator.userAgent.toLowerCase();
return agent.indexOf("trident") !== -1;
}
function isIEold() {
const agent = window.navigator.userAgent.toLowerCase();
return agent.indexOf("msie") !== -1;
}
function isIE() {
const old = isIEold();
const ie11 = isIE11();
if (!old && !ie11) {
return false;
} else {
return true;
}
}
function isOpera() {
const agent = window.navigator.userAgent.toLowerCase();
return agent.indexOf("opera") !== -1;
}
export {
isChrome,
isSafari,
isEdge,
isFirefox,
isIE11,
isIEold,
isIE,
isOpera,
};
import { isSafari, isOpera } from "./_browser";
document.addEventListener("DOMContentLoaded", () => {
if (isOpera()) {
console.log("js smooth");
smooth();
} else {
console.log("css smooth");
}
});
function smooth() {
// 内部リンクだけ
const targetElement = document.querySelectorAll('a[href^="#"]');
if (targetElement.length == 0) return;
targetElement.forEach((alink) => {
alink.addEventListener("click", (event) => {
event.preventDefault();
const id = event.target.hash;
let hash = id;
let top = -1; // 移動先
if (id.length > 0) {
// 遷移先のエレメント
const target = document.querySelector(id);
// 絶対値で移動。
// 現在のビューポート(画面に表示されているところ)から遷移先までの距離
const rectTop = target.getBoundingClientRect().top;
// 現在のスクロール位置
const offsetTop = window.pageYOffset;
top = rectTop + offsetTop;
} else {
top = 0;
hash = "#";
}
// スクロール開始
window.scrollTo({
top: top,
behavior: "smooth",
});
});
});
}
ひとつのjsファイルに書いてもいいですが、いまはwebpackなどのビルドツールを使って分割して書いていくのが主流なのでそれに合わせました。
JavaScriptは、ページ内リンクの<a>のクリックイベントにスムーズスクロールを追加しています。
そして、ブラウザ OperaのときはJavaScript、それ以外はCSSでスクロールするように分けました。
IE, Safariはもうひと工夫が必要
IE, Safariはwindow.scrollTo()のパラメータにオブジェクトが使えません。
ということでIE, Safariはスムーズスクロールしません。捨てました。
(MacOS, iOSを捨てる。CSSかscrollTo()で対応するの待つ。)
かたくなに『許さない。必ず実装しろ!』とまで言われるのは稀でしょうから。
一応、説明はしておきましょう。『CSSもJavaScriptも未対応だから、実装は想像以上に大変ですよ?』って。
(表向き。polyfillを使えばそこまで大変じゃない。jsライブラリもある? 知らないけど。)
トランスパイル(transpile)
プログラムを、べつのバージョンや、べつの言語のプログラムに変換すること。
一種のコンパイラ。トランスコンパイラともいう。
JavaScriptは最新の仕様で開発して、任意のバージョンにトランスパイルする手法が一般的になっている。
(新しい仕様のほうがシンプルな記述で読み書きしやすいため。)
@babelが有名。
トランスパイルは文法を変換するが新しいAPI(関数やオブジェクト)までは対応できない。
古いもので確実に動かすためにはポリフィルも必要。
ポリフィル(Polyfill)
プログラムのバージョン違いでAPIが不足するところを補うこと。
プログラム言語はバージョンが上がると新しいAPIを追加する。APIは関数やクラス・オブジェクトのこと。
古いバージョンで動かすときは新しいAPIと同じ機能がないと動かない。それを作るのがポリフィル。
ポリフィルは、新しいAPIを移植するのではなく、古いバージョンのプログラムで実装できるAPIに作り直して提供する。
jQueryのmigrateはjQueryのポリフィル・パッケージ。
トランスパイルで有名な@babelにもポリフィルの拡張機能がある。
ただ、個人的にはそこまでしない方向にもっていきたい。
(jsファイルの増大は避けたいので。)