画像の遅延読込でページ内リンクがずれる時の解決法【jQuery】

コーディング

アンカーリンク(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");
         }
       });
     });
  });