ペラサイトではメニューにアンカーリンクを配置して、ページ内スクロールでナビゲーションします。また、ブログなどでページ先頭に目次を配置してページ内ジャンプさせることも良く行われています。
アンカージャンプは便利なのですが、ページが長くなるとLazyloadの影響でアンカージャンプ先がズレる現象が起きたりしました。いわゆるCLSというやつですね。
そのような問題も、WordPressがNative Lazyloadに対応した際に、imgタグにwidthとheightを自動的に追加するようになってから無くなったと思ったのですが、2021年が終わりに近づく中、アンカージャンプのとび先がずれてしまう現象に出くわし、解析するのにとても苦労したので、まとめておきたいと思います。
アンカージャンプでのトラブルは別記事にもありますが、今回はそれとは別の内容になります。
Lazyload前の1pxのplaceholderがズレを引き起こす
アンカージャンプのとび先がズレる原因は飛び先を計算した時に間違うからです。
その計算はスムーズスクロールのJavascriptで計算しています。
なぜ飛び先の計算を間違えるかというと、アンカージャンプしている間にLazyloadで画像が読み込まれるので、計算した時とページの長さが変わるからです。
今回も原因はソレなのですが、想像の斜め上の事象でした。
今回使用したプラグインはa3 lazyloadです。最近はあまり使っていませんでしたがテーマとの相性問題が起きにくいので昔のサイトではよく使っていました。

このプラグインは、Lazyload前の画像として、1pxのplaceholderをsrcにセットしてくれます。
ところが、そのplaceholderの画像は、imgタグのオリジナル画像のwidthとheightを無視して、オリジナル画像と同じ横幅の正方形になってしまうのです。
レスポンシブ対応でimgタグに以下の様なCSSが当たっているためです。height:autoだとアスペクト比は画像のアスペクト比に従って決定されますので、placeholderの画像は正方形として扱われます。
img{
width:100%;
height:auto;
}
つまり、オリジナル画像とアスペクト比が同じplaceholder画像を使わない限り、オリジナル画像と高さが合わなくなり、飛び先の計算を間違えることになります。
- オリジナルが横長の画像であれば、placeholderの高さははオリジナルよりも高くなり、ページ全体の長さが伸びますので、飛び先が遠くにズレます。
- オリジナルが縦長の画像であれば、 placeholderの高さははオリジナルよりも低くなりますので、ページ全体の長さが短くなり、飛び先が近くにズレます。
じゃぁ、placeholderをオリジナル画像と同じアスペクト比にする良い方法がありそうなものですが、画像ごとにアスペクト比が異なるので実は簡単ではなく、中々良い方法が見つかりませんでした。
プラグインのサポートでも問題になっていますが、解決しないまま放置されています。

アスペクト比がオリジナルと同じsvg画像にplaceholderを置き換えると解決する
アスペクト比がオリジナルと同じplaceholderの画像を用意すればいいのですが、アスペクト比は画像ごとに違いますので、結構大変だと申しました。
幸い、最近のwordpressのimgタグにはwidthとheightが定義されているので、jQueryでplaceholderを置き換えることができます。
imgタグのsrcにplaceholder画像があったら、アスペクト比が正しい画像に置き換える処理をしているだけです。
スムーズスクロールの計算前に処理すればいいので、フッターで読み込んでいます。
<?php
add_action('wp_footer',function(){
?>
<script>
(function($){
$('[src="//yourdomain.com/wp-content/plugins/a3-lazy-load/assets/images/lazy_placeholder.gif"]').each(function(i,j){
var h = $(j).attr( 'height' );
var w = $(j).attr( 'width' );
var at = `data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 ${w} ${h}'%3E%3C/svg%3E`;
$(j).attr('src', at);
});
})(jQuery);
</script>
<?php
});
参考:https://stackoverflow.com/questions/23416880/lazy-loading-with-responsive-images-unknown-height
Navtive Lazyloadを使っている場合は問題が起きない
今回のPlaceholder画像のアスペクト比がオリジナルと異なることに起因するCLS問題は、実はplaceholderをimgタグのsrcに設定しているために起こります。
固定画像のplaceholderを設定してくれるLazyload全般で発生する問題です。
一方で、Native Lazyloadでは、srcにオリジナル画像が設定されるので問題が発生しません。その点、Native Lazyloadはよく考えられた仕組みだと思いました。
Native Lazyloadとフォールバックする仕組みを組み合わせたLazyloadプラグインを使うのが一番スマートなのかもしれません。

まとめ
アンカージャンプはスムーズスクロールが絡むので、javascript起因の問題が結構おきます。
今回は、placeholder画像のアスペクト比がオリジナルと異なることで発生する問題でした。
a3 lazyloadを使っている人は、参考になるかもしれません。