サイトのCLSがJavaScriptで後から挿入するコンテンツによって上昇するのを防ぐにはどうするか 結城永人 - 2023年2月25日 (土) サイトのHTMLに最初から記載されてないコンテンツを後からJavaScriptで挿入するとそのときにページが新しいスペースを取られて動いてしまう。初回画面だと既存のコンテンツが押し下げられるのが、一瞬、見えたりもするけど、これがサイトのCLS(Cumulative Layout Shift/累積的な配置の変動)を上げて使い難くなる。 指標|PageSpeed Insights|Google Developers 良くあるのが押そうとしたボタンがサイトの読み込み中に動いて押せなくなってしまうことだ。 訪問者に良い印象を与えないのはもちろんだけど、さらにGoogleのサイトの検索状況のSearch Consoleや性能計測のPageSpeed Insightsでも警告が出されるので、検索エンジンからアクセスアップを望む上でもCLSの上昇に注意しなくてはならない。 普通の回避策としては新しいコンテンツのscriptタグを挿入する位置かその後の近くに置いて直ぐに開始するようにしておくのが良いと思う。表示の時間差があるからCLSの上昇を完全に免れることはできないにせよ、ページが明らかに動くと分かったり、Googleから警告を出されるような害は相当に減らせるという方法になる。 CLSの上昇を完全に免れるには三つの方法が考えられる。 次のコンテンツを上の外側の余白で最初から下げておく挿入するコンテンツのスペースを最初から確保しておく挿入するコンテンツと他のコンテンツを一緒に表示する 三つの方法についてどのように行うかを取り上げる。 次のコンテンツを上の外側の余白で最初から下げておく 挿入されるコンテンツの次のコンテンツの上の外側の余白を十分に取って予め下げておくと後からページが動かされることはない。 次のコンテンツにCSSで、「margin-top:上の外側の余白」を付けておく。 上の外側の余白は挿入されるコンテンツの高さが分かっていればそれで足りるけど、しかし可変的で一律でないとか分からなければ「100vh」で画面の高さになるから最も確実にCLSの上昇は防げる。CLSの上昇は画面内のパフォーマンスの問題だから画面内に押し下げられるコンテンツがなければ大丈夫なんだ。 p.nextの上の外側の余白を画面の高さで指定するならば p.next {margin-top:100vh} そのままにしておくと挿入されたコンテンツと次のコンテンツの間に余分なスペースが残ってしまうから消すためのプログラムを目次が挿入された後に追加する。 JavaScriptでCSSを書き換える方法は幾つかあるけれども個別のstyleプロパティの上の外側の余白の「style.marginTop」を使うと、直接、CSSのインラインstyleを上書きするから他のところの指定の影響を余り受けないから楽に使えると思う。 p.nextの上の外側の余白を元に戻すならば document.querySelector("p.next").style.marginTop = ""; その他に上の外側の余白を指定したclassをCSSに用意して次のコンテンツに付けておいてclassListプロパティのremove()メソッドで外すこともできる。 document.querySelector("p.next").classList.remove("次のコンテンツの上の外側の余白のために付けたクラス名"); 次のコンテンツの上の外側の余白を付けて新しいコンテンツの高さが不明ならば挿入された時点で消すという方法がCLSの上昇を防ぐには欠点も手間も少ないと思う。 関連ページCSSはどう使えば良いのか|タグ自体とidとclassの指定先とstyleタグとインラインの記述法 挿入するコンテンツのスペースを最初から確保しておく 挿入されるコンテンツの高さを親ボックスで必要なスペースとして指定して次のコンテンツを予め下げておくと後からページが動かされることはない。 挿入するコンテンツの位置にdivタグなどの親ボックスを配置した上で、CSSで、「height:高さ」を付けておく。 親ボックスの高さは挿入するコンテンツの高さが分かっていればそれで足りるけど、しかし可変的で一律でないとか分からなければ「100vh」で画面の高さになるから最も確実にCLSの上昇は防げる。CLSの上昇は画面内のパフォーマンスの問題だから画面内に押し下げられるコンテンツがなければ大丈夫なんだ。 div.boxの高さを画面の高さで指定するならば div.box {height:100vh} 確保したスペースが挿入されるコンテンツの高さと一致していなければそのままにしておくと次のコンテンツとの間に余分なスペースが残ってしまうから消すためのプログラムを目次が挿入された後に追加する。 JavaScriptでCSSを書き換える方法は幾つかあるけれども個別のstyleプロパティの高さの「style.height」を使うと、直接、CSSのインラインstyleを上書きするから他のところの指定の影響を余り受けずに楽に使えると思う。 div.boxの高さを元に戻すならば document.querySelector("div.box").style.height = ""; その他に上の外側の余白を指定したclassをCSSに用意して次のコンテンツに付けておいてclassListプロパティのremove()メソッドで外すこともできる。 document.querySelector("div.box").classList.remove("親ボックス高さのための付けたクラス名"); 挿入するコンテンツの親ボックスでスペースを取って新しいコンテンツの高さが不明ならば入った時点で消すという方法は親ボックスをHTMLで記載する必要があるから手間が増えるけれども最初から親ボックスに入れるような仕方で表示するならば逆にぴったりだろう。 関連ページCSSはどう使えば良いのか|タグ自体とidとclassの指定先とstyleタグとインラインの記述法 挿入するコンテンツと他のコンテンツを一緒に表示する ページに新しいコンテンツを後から挿入しなければ既存のコンテンツが余計に動かされることはないので、変更部分のコンテンツを、一旦、止めておいて挿入するコンテンツと他のコンテンツを一緒に表示しても良いと思う。 僕がBloggerの投稿画像の遅延読み込みや軽量化で、使った方法だけれども既存のコンテンツをnoscriptタグで、一旦、止めておいて新しいコンテンツを挿入したらnoscriptタグの中身だけ取り出すようにする。 noscript内を編集して表示するプログラムの流れ // noscript要素の親ボックス(div.box)を取得する const pn = document.querySelector("div.box"); // noscript要素(noscript.content)を取得する const ns = document.querySelector("noscript.content"); // 取得したnoscript要素の親要素の中身以外を複製する const nns = pn.cloneNode(false); // 新しい親ボックス(複製)にnoscript要素の中身だけ入れる nns.insertAdjacentHTML("afterend", ns.innerText); // 新しい親ボックス(複製)に新しいコンテンツを挿入する nns.insertAdjacentHTML("afterend","新しいコンテンツ"); // 新しい親ボックスを元の親ボックスと入れ替えて全てのコンテンツを表示する pn.parentElement.replaceChild(nns, ns); noscript内のコンテンツはそのままではHTML要素として扱えないので、複製した親ボックスに新しく入れ替えたり、操作が普通よりも少し増えるものの不可能ではない。 挿入するコンテンツと既存のコンテンツを一緒に表示するという方法はCLSの上昇をサイトのデザインを全く崩さずに回避できるところが優れているけど、ただし変更部分のコンテンツを、一旦、止めるからページの表示速度が僅かでも下がるのは間違いない。 通常はやり過ぎみたいな感じなので、考える必要もないとはいえ、余程の利点が他に加わったりすれば敢えてやってみても面白い。 CSSの二つの回避策、挿入するコンテンツの次のコンテンツを最初から下げておく方法が一般的に使い易いけれども方法としての難点は場合分けができないことだ。 CLSの上昇を防ぎたいのは画面内のコンテンツだけだとしてもそうした条件を付けて指定できるCSSはない。ある要素の全ての場合が含まれざるを得ないから不要ならば無駄になるというか、保険をかけるような感じで、使わなくてはならない。 コメント 新しい投稿 前の投稿
コメント