アンカーリンク(aタグ)を使って、メニューや目次からページ内の特定の場所にジャンプさせることってよくありますよね。
ところがそれにloading=”lazy”などの遅延読み込みを組み合わせると、ジャンプ先がずれてしまいます。
ランディングページでどうしても両者を共存させたかったので、色々調べて解決した方法をご紹介します。
スムーススクロール用のjQueryの記述を変えよう
まずHTML側は、ざっくりこんな形だとします。
<ul>
<li><a href="#link01">リスト01</a>></li>
<li><a href="#link02">リスト02</a></li>
<li><a href="#link03">リスト03</a></li>
</ul>
<div id="link01">
<p>ブロック01</p>
<img loading="lazy" src="demo.jpg">
</div>
<div id="link02">
<p>ブロック02</p>
<img loading="lazy" src="demo.jpg">
</div>
<div id="link03">
<p>ブロック03</p>
<img loading="lazy" src="demo.jpg">
</div>
解決したjQueryのソースコード
$('a[href^="#"]').click(function(e) {
var href = $(this).attr("href");
var target = $(href == "#" || href == "" ? 'html' : href);
var position = target.offset().top;
$.when(
$("html, body").animate({
scrollTop: position
}, 400, "swing"),
e.preventDefault(),
).done(function() {
var diff = target.offset().top;
if (diff === position) {
} else {
$("html, body").animate({
scrollTop: diff
}, 10, "swing");
}
});
});
私と同じようにloading=”lazy”を採用したことで、いつも使っていたページ内リンクへのスムーススクロールが上手く動かなくなった…という方の記事を見つけて参考にさせていただきました。
参考:「loading=”lazy”とページ内リンクへのスムーススクロール(URLの#以降を表示させたくない)」
ソースコードの解説もしていきます。
ずれる理由と、解決方法の解説
そもそもなんでずれるの?というところから。
画像の遅延読み込みをしていなかった時、ページ内リンクへのスムーススクロールは下記のようにしていました。
WEB制作会社で働いていた頃、しょっちゅう使ってましたね。
$('a[href^="#"]').click(function(){
var speed = 400;
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;
});
ページ読み込み時に、ページの一番上からアンカーリンク先の要素までの距離を、.offset().topで取得。
そしてメニューなどをクリックしたら、取得した値の分だけスクロールするイベントが発生します。
ただ、そのリンク先に飛ぶまでに配置されているimgタグに遅延読み込みが設定されていると・・・
imgの高さ分の値が取得されず、要素までの距離がおかしくなってしまうんですね。
そこで、.when()と.done()で二段階に分けてページ内遷移をしています。
①遅延読み込みされる画像はひとまず無視して、リンク先の要素の位置を.offset().topで取得(Xの値)
↓
②スムーススクロール中、画像が次々と遅延読み込みされ、ずれた所で停止
↓
③もう一回、リンク先の要素の位置を.offset().topで取得(Yの値)
※スクロールしてimgが読み込まれた後なので、正しい要素の位置が取得できる
↓
④XとYの値が違うならYの位置にスクロールする
↓
⑤正しい位置へ移動
という感じです。
さらに、固定ヘッダーの高さ分ずらしたい場合
position:fixedでヘッダーをページ上部に固定している場合は、ヘッダーの高さ分ずらしてあげましょう。
$(function () {
var headerHight = $("header").height();
$('a[href^="#"]').click(function(e) {
var href = $(this).attr("href");
var target = $(href == "#" || href == "" ? 'html' : href);
var position = target.offset().top - headerHight;
$.when(
$("html, body").animate({
scrollTop: position
}, 400, "swing"),
e.preventDefault(),
).done(function() {
var diff = target.offset().top - headerHight;
if (diff === position) {
} else {
$("html, body").animate({
scrollTop: diff
}, 10, "swing");
}
});
});
});