ウェブフォントを遅延読み込みする簡単な実装

ページの初回読み込み時から @font-face でウェブフォントを読み込もうとしていると、どうもレンダリングが遅くなる。font-display: swap もなんだか効いてない気がする。そこで、いさぎよく window.onload 以降にウェブフォントを遅延読み込みすることにした。

ココでは、OS 問わず日本語対応の等幅フォントを表示するため、Noto Sans Mono CJK JP を遅延読み込みすることにする。

サンプル

以下で実装サンプルを確認できる。

サンプルでは、ページ読み込みから3秒後、初めて Web フォントを読み込むよう遅延させている。

「Remove」と「Add」ボタンは、遅延読み込み時に html 要素に付与しているクラス名を付けたり外したりできる。スワップの挙動を手動で再現できる。

@font-face で定義したウェブフォントはいつロードされるか

ところで、@font-face で定義したウェブフォントは、どのタイミングでロードされるのか。自分が調べたところ、

  1. ただ @font-face を定義しただけでは、ウェブフォントは読み込まれない
  2. HTML 中に適用する対象がないセレクタでウェブフォントを指定していても、ウェブフォントは読み込まれない
    • 例えばページ中に div 要素が一つもない状態で、div { font-family: "My Web Font"; } という CSS 指定があっても、そのウェブフォントはロードされない
  3. HTML 中に適用対象となる要素が存在すると、初めてウェブフォントが読み込まれる

つまり、ウェブフォントを使うような指定が CSS として記述してあっても、それが使われている HTML 要素がページ中になければ、その CSS の記述が無視されて、ウェブフォントも読み込まれないワケだ。

遅延読み込みを前提とした CSS を書く

ということなので、予め次のような CSS を用意しておく分には、ウェブフォントはロードされない。

@font-face {
  font-family: "Noto Sans Mono CJK JP Web";
  src: local("NotoSansMonoCJKjp-Regular"),
       local("Noto Sans Mono CJK JP Regular"),
       local("Noto Sans Mono CJK JP"),
       url("https://cdn.jsdelivr.net/npm/@neos21/japanese-monospaced-fonts@1.0.2/NotoSansMonoCJKjp-Regular.woff2")              format("woff2"),
       url("https://unpkg.com/@neos21/japanese-monospaced-fonts@1.0.2/NotoSansMonoCJKjp-Regular.woff2")                         format("woff2"),
       url("https://cdn.jsdelivr.net/npm/@neos21/japanese-monospaced-fonts@1.0.2/NotoSansMonoCJKjp-Regular.woff")               format("woff"),
       url("https://unpkg.com/@neos21/japanese-monospaced-fonts@1.0.2/NotoSansMonoCJKjp-Regular.woff")                          format("woff"),
       url("https://cdn.jsdelivr.net/npm/@neos21/japanese-monospaced-fonts@1.0.2/NotoSansMonoCJKjp-Regular.otf")                format("opentype"),
       url("https://unpkg.com/@neos21/japanese-monospaced-fonts@1.0.2/NotoSansMonoCJKjp-Regular.otf")                           format("opentype"),
       url("https://cdn.jsdelivr.net/npm/@japanese-monospaced-fonts/noto-sans-mono-cjk-jp@1.0.1/NotoSansMonoCJKJP-Regular.otf") format("opentype"),
       url("https://unpkg.com/@japanese-monospaced-fonts/noto-sans-mono-cjk-jp@1.0.1/NotoSansMonoCJKJP-Regular.otf")            format("opentype");
  font-display: swap;
}

/* 通常のスタイル : ウェブフォントを読み込まない */
html { font-family: monospace; }

/* html 要素に class が振られた場合 : 通常 .loaded クラスを振らなければ、この指定はどこにも使われず、ウェブフォントはロードされない */
html.loaded { font-family: "Noto Sans Mono CJK JP Web", monospace; }

遅延読み込みのためのスクリプト

先程用意した html.loaded というセレクタを有効にするため、window.onload のタイミング以降で html 要素に CSS クラスを振る、簡単な JS を書く。

window.addEventListener('load', () => {
  setTimeout(() => {
    document.querySelector('html').classList.add('loaded');
  }, 1);
});

コレだけで OK。この CSS クラスが振られた瞬間からウェブフォントが読み込まれ、読み込みが完了したら画面に適用される。

Noto Sans Mono CJK JP はファイルサイズが大きいので、ロードに10秒程度かかることもある。ページへの反映がかなり遅れるかもしれないが、ページの初回読み込みは一切ブロックしないので、一長一短。