画像の遅延読込でアンカーリンクがズレる時の解決法

画像の遅延読み込みが原因で、アンカーリンクのスクロール位置がズレる場合の解決策の備忘録です。

ページ内リンク(アンカーリンク)が設定した箇所より上の位置で止まってしまうので、調べたところ画像に設定されているloading="lazy"が原因でした。

画像の遅延読込

loading="lazy"は、Webサイトの表示速度を高速化のためにimgタグに設定されるコードです。

画像はファイルサイズが比較的大きいため、ページを開いた時に一度に読み込むとWebサイトの表示が遅くなってしまいます。

そのため、遅延読込(lazy load)を使用して、画面外の画像をスクロールで表示が必要なタイミングで読み込むように実装することで、ページの初期表示速度を短縮することができます。

しかし、アンカーリンクでページをスクロールすると、画像が遅延読込で表示する前の位置へ移動してしまい表示された画像の高さ分ズレが生じます。

修正前のコード

下記がアンカーリンクを実装していたコードのサンプルです。

<ul>
  <li><a href="#anchor01">タイトル01</a></li>
  <li><a href="#anchor02">タイトル02</a></li>
  <li><a href="#anchor03">タイトル03</a></li>
</ul>

<h2 id="anchor01">タイトル01</h2>
<img loading="lazy" src="sample.jpg">

<h2 id="anchor02">タイトル02</h2>
<img loading="lazy" src="sample.jpg">

<h2 id="anchor03">タイトル03</h2>
<img loading="lazy" src="sample.jpg">

リンク先に設定したIDの位置へ、jQueryでスクロールさせています。

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

ページの一番上からアンカーリンク先の要素までの距離を.offset().topで取得して、リンクをクリックした時に取得した値の分だけスクロールするイベントが発生します。

このコードでは、間に遅延読込があるとアンカーリンクの移動先がズレてしまいます。

変更後のコード

遅延読込の影響を受けないように、スムーススクロール部分のjQueryを変更します。

jQuery('a[href^="#"]').click(function (e) {
  var speed = 400,
    href = $(this).attr("href"),
    target = $(href == "#" || href == "" ? 'html' : href),
    position = target.offset().top;
  $.when(
    $('html,body').animate({
      scrollTop: position
    }, speed, 'swing'),
    e.preventDefault(),
  ).done(function() {
    var diff = target.offset().top;
    if (diff === position) {
    } else {
      $('html, body').animate({
        scrollTop: diff
      }, 10, 'swing');
    }
  });
});

改修後のコードでは、.when().done()で二段階に分けてページ内遷移を実行しています。

はじめに、.when()でリンク先の要素の位置を.offset().topで取得してスムーススクロールを実行します。この時に遅延読込の画像があると、リンク先の位置にずれが生じます。

続けて、.done()で再びリンク先の要素の位置を取得して、遅延読込のズレが生じている場合再度スクロールを実行します。

遅延読込を考慮して、段階を踏むことで正しい位置へスクロールするアンカーリンクが実装できました。

Share on Twitter
関連記事
文字列を省略して末尾に「…」を追加する方法
文字列を省略して末尾に「…」を追加する方法
JavaScriptで特定の位置までスクロールした要素を固定表示する方法
JavaScriptで特定の位置までスクロールした要素を固定表示する方法
WordPressにslickでスライドショーを実装する方法
WordPressにslickでスライドショーを実装する方法