【CSS / JS】スムーススクロールの不具合とその対応
【CSS / JS】 今後のスムーススクロールについての続きです。
「便利になったなぁ」と満足感を感じながら実案件で前回のコードを使ってみたのですが、ブラウザチェックの際に不具合がみつかりました。
原因の調査と対応策を考えました。
不具合とは
不具合の詳細
今回の不具合は、Mac の Safariで発生しました。
内容は目的の場所に移動しないことです。
原因の調査
$('a[href^="#"]').on('click', function () {
const speed = 500;
const easing = 'swing';
const href = $(this).attr('href');
const target = $(href == '#' || href == '' ? 'html' : href);
const spt = parseInt($('html').css('scrollPaddingTop')) || 0;
const smt = parseInt($(target).css('scrollMarginTop')) || 0;
// スクロール先
const position = target.offset().top - (spt + smt);
$('html, body').animate({ scrollTop: position }, speed, easing);
return false;
});
上記は前回の最終コードです。
何がよくなかったのでしょうか。
$('a[href^="#"]').on('click', function () {
const speed = 500;
const easing = 'swing';
const href = $(this).attr('href');
const target = $(href == '#' || href == '' ? 'html' : href);
// スクロール先
const position = target.offset().top;
$('html, body').animate({ scrollTop: position }, speed, easing);
return false;
});
元々使えていたので当たり前ですが前回追加した scroll-padding-top などを削った場合、移動はうまくいきました。
html{
scroll-padding-top: 80rem;
}
今回 scroll-magin-top は使っていないので、scroll-padding-top がうまくいってない事がわかります。
Safariではこのプロパティに不具合があるのでしょうか。
html{
scroll-padding-top: 80px;
}
試しにrem を px に変更してみたところうまく動きました。
scroll-padding-top 自体は使えるようですが rem を使うとおかしくなるようです。
次に単位を rem に戻し、コンソールから scroll-padding-top を出力してみました。
80rem で 1280px
1280 / 80 = 16
1rem = 16px。
16pxといえばブラウザのデフォルトの値。なぜかSafariではこれを参照してしまっている可能性があります。
しかしスタイル自体に崩れは有りませんでした。
html の font-size はちゃんと設定されているようです。
しかしそれなら 0.700694px * 80 = 56.05552px となるはず。
scroll-padding-top の rem はだめで、px では正常。
font-size は正常。
htmlのプロパティをremで設定することで不具合が出るのではないかという予想が立ちます。
html , body の background-position-x の値を rem で指定してみて出力される値を見てみます。
html{
background-position-x: 10rem;
}
body {
background-position-x: 10rem;
}
予想通りの結果が出ました。
どうやらSafari では html のプロパティに対して rem でスタイルをつけると、基準が 16px に固定される事がわかりました。
※Safari バージョン17.2.1 (17617.1.17.11.12, 17617)
不具合への対応
html に rem で指定できないのであれば、rem と同じ値を変数に入れておいて、それを使って計算すればよいのではないでしょうか。
まず html の基準を設定している部分に変数で同じ数値を持たせておきます。
@media(750px < width){
html{
font-size: 0.069444444444444vw;
--font-size: 0.069444444444444vw;
}
}
@media(width <= 750px){
html{
font-size: 0.133333333333333vw;
--font-size: 0.133333333333333vw;
}
}
scroll-padding-top を変数から計算します。
html{
//scroll-padding-top: 80rem;
scroll-padding-top: calc(var(--font-size)*80);
}
コンソールで値を取ってみます。
成功です。他のブラウザでもチェックします。
有効桁数や切り捨て切り上げなんかの仕様が微妙に違ってるようですが同じような数値がとれました。
scroll-padding-top を rem で使いたいときは変数から計算してやればよさそうです。
尚、ソリッドレイアウトであれば以前のコードでも対応可能なので、シンプルに書ける分そのほうが良いかもしれません。
今回の最終的なコード
// js
$('a[href^="#"]').on('click', function () {
const speed = 500;
const easing = 'swing';
const href = $(this).attr('href');
const target = $(href == '#' || href == '' ? 'html' : href);
const spt = parseInt($('html').css('scrollPaddingTop')) || 0;
const smt = parseInt($(target).css('scrollMarginTop')) || 0;
// スクロール先
const position = target.offset().top - (spt + smt);
$('html, body').animate({ scrollTop: position }, speed, easing);
return false;
});
※jsに変更なし。
// css
@media(750px < width){
html{
font-size: 0.069444444444444vw;
--font-size: 0.069444444444444vw;
}
}
@media(width <= 750px){
html{
font-size: 0.133333333333333vw;
--font-size: 0.133333333333333vw;
}
}
html {
//scroll-padding-top: 80rem;
scroll-padding-top: calc(var(--font-size)*80);
}
.header {
height: 80rem;
}
今回の不具合は、html のスタイルを rem で設定すると起こる Safari の仕様が原因でした。
一昔前にIE が退場してからというもの Safari が問題児筆頭になってるような気がします。
今後の Safari のアップデートで、この謎仕様が改善されることを祈りつつ、今回の記事を締めたいと思います。
■お仕事のご依頼やご相談はお気軽に
エイトビーは、Web制作会社としてテクノロジーとクリエイティブの融合で最適なコミュニケーションを提案・創造し、お客さまやユーザーの皆さま、すべての人が幸せになれることを目指しています。
■採用募集はこちら
エイトビーでは、新しい仲間を募集しています。