CSS Font Loading APIでウェブフォントを読み込む

ファイルサイズが巨大で読み込みに時間のかかる日本語のウェブフォントは難しいなぁと思いつつCSS Font Loading API (CSS Font Loading Module Level 3?)も一応触ってみないとダメだよねということで最も簡単なところだけ。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
<!doctype html>
<html lang="ja">
<head>
  <meta charset="utf-8">
中略
  <script>
    var font = new FontFace("Noto Serif JP", "url(/font/NotoSans-Regular.otf)", {
      style: 'normal',
      weight:'400'
    });
    font.load().then(function() {
      document.fonts.add(font);
      document.getElementById('hoge').style.fontFamily = "'Noto Sans JP', sans-serif";
    });
  </script>
</head>
<body>
  <div id="hoge">ウェブフォントを適用したい部分</div>
</body>
</html>

Javascriptに慣れてれば書くまでもない基本的なことだけど、上の13行目のウェブフォント適用先の指定方法。(下2つ)

<body></body>の中すべてに適用する場合は
document.body.style.fontFamily = "'Noto Sans JP', sans-serif";

特定のクラス、例えば<div class="hoge"></div>に適用したいならdocument.getElementById('hoge').style.fontFamilyをdocument.getElementByClassName('hoge').style.fontFamilyに変えれば良いかというと、もちろんそんなわけなくて
1
2
3
4
  var hoges = document.getElementsByClassName('hoge');
  for(var i = 0; i < hoges.length; i++) {
    hoges[i].style.fontFamily = "'Noto Serif JP', sans-serif";
  }
こんな風にする。これが最適じゃないかもだけど。

または、<div class="hoge"></div>が「1つしかない」という限定条件であれば、
  document.querySelector('.hoge').style.fontFamily = "'Noto Serif JP', sans-serif";
でも、これは後々意図しない表示になる元なので勧めない。

querySelectorではなくquerySelectorAllを使えば複数にヒットさせられるけど使い方はgetElementByClassNameと殆ど変わらないかも。
1
2
3
4
  var hoges = document.querySelectorAll('.hoge');
  for(var i = 0; i < hoges.length; i++) {
    hoges[i].style.fontFamily = "'Noto Serif JP', sans-serif";
  }
こんな感じ?

ブラウザのCSS Font Loading API対応状況
Chrome, Firefoxの新しいのは対応している。SafariやOperaは周りに無いので未確認だが対応しているらしい。Microsoftのゴミはどうでもいい。気になるのはマイナーなモバイルブラウザたち。非対応だとウェブフォントが適用されないのでウェブ製作者の意図に反した表示になる。そこが難点。

ウェブフォント表示方法比較

前回、ウェブフォントの表示方法で記事の題名に反してGoogle Fontsを使う場合をあまり考慮していなかったのでもう一度。

  1. ウェブフォントを読まない場合
  2. Google Fonts推奨の方法
  3. Google Fonts推奨の方法でCSSをPreloadで非同期読み込み
  4. サーバにウェブフォントを置いて普通にCSSで指定
  5. サーバにウェブフォントを置いてCSSで遅延指定で読み込み
  6. Google FontsのCSSを改変して遅延指定で読み込み
  7. Google FontsのCSSを改変して遅延指定で読み込み+CSSをPreloadで非同期読み込み

この7つの場合をPageSpeed Insightsで計測して比較してみた。
せっかくなのでそれぞれサンプルページ悪いインターネットに作った。

6,7番用に改変したCSSは以下2つ。

Noto Sans JP (ゴシック)
Noto Serif JP (明朝)

サンプルの目次ページにも書いたが結論として5番と6番が正解っぽい。
特にGoogle FontsのNoto系フォントを使うなら6番の方法はChromeでの文字入れ待ちが解消するので良さそう。
絶対NGなのは3番と7番、あまりオススメできないのは2番と4番。

ウェブフォント Noto Sans JPを使う 2019年新春

自分のウェブサイトの見た目を一定させるためにウェブフォントを使いたいということはある筈。 ただ、日本語のフォントはファイルサイズが大きいこともあって転送量や表示に時間がかかるなど困ることも。 以前にウェブフォントのサブセット化をやったことがあるが、それだと転送量の面しか良さがない。

これまでもGoogleが配布しているNoto Sans系のフォントを愛用していたのだが、サブセット版のフォントファイルを自分のウェブサイトに置くものどうかなという考えに変わったので変更することにした。

良くなかった対応 1

Google Fontsによると最近はウェブフォント用のCSSを用意しているのでそれを使えということみたい。

HTMLヘッダに追加
1
2
3
4
5
6
7
8
<link href="https://fonts.googleapis.com/css?family=Noto+Sans+JP&subset=japanese" rel="stylesheet">

または

<style>
  @import url('https://fonts.googleapis.com/css?family=Noto+Sans+JP');
</style>
(こちらはCSSファイルに書くもアリだけどどちらにしろ@importはしたくないよね)

CSSで使う
font-family: 'Noto Sans JP', sans-serif;

簡単ね。
そして、このCSSファイルを見てみると解るがunicode-rangeで120程度にスライスされてぺージ表示に必要な分だけ読むようにしているみたい。そもそも対応する字数も少ないのでサブセットっぽい。
Noto Sans JPフォントファイル全体をダウンロードすると転送量が多いし時間もかかるので、節約志向なら良さげではある。ただし、(含まれる字の異なる)複数のページを閲覧したらその利点は減るんだけど・・
そして、このGoogleの用意したCSSを「そのまま」読み込む最大の問題はページ表示がとにかく遅くなること。ウェブサイト管理者・作成者のページ表示高速化の努力はこれだけで完全に台無しになる。

良くなかった対応 2

日本語のコンテンツの本文に使うフォント以外であれば、Javascriptと組み合わせて遅延(というか非同期)させてウェブフォントを読み込ませて適用するという方法を採用したいところであるが、日本語のコンテンツ本文のフォントをこのやり方で読み込ませると最初に通常フォントで表示されたあとに数秒間コンテンツの文字が非表示になってその後に指定したウェブフォントが表示されるという苛立たしい状態となる。途中の非表示というのは1秒であっても閲覧者は嫌うと思うのでこれはダメだと思われる。
今回は上のGoogle提供のCSSを非同期読み込みさせたが、実際にやってみてページの大部分を占めるメインのウェブフォントでは採用すべきではないと思った。

記号フォントのFont Awesomeを遅延読み込みでHTMLヘッダに追加した例
1
2
3
4
5
6
7
<link href="/fonts/awesome/css/font-awesome.min.css" id="awesome" />
<script>
  (function () {
    var awesome = document.getElementById('awesome');
    awesome.rel = 'stylesheet';
  })();
</script>
もっと簡単にしたら、1行で下。(ただし、現在preloadはChromeでしか機能しないので実質使えない方法)
<link rel="preload" as="style" href="/fonts/awesome/css/font-awesome.min.css" onload="this.rel='stylesheet'">

テキトーじゃなくちゃんとFont Loading API, Web Font Loaderを使えということだったらスンマセン。

正解だった対応

結局、昔からの正攻法でCSSの表示指定を行って、おまけでfont-display: swap;を指定すればコンテンツの表示が高速(代替フォント)で且つ指定フォントへの切り替わりがマヌケにならないスマートな遅延表示が可能となる。(Microsoftのブラウザや一部のモバイル用ブラウザは非対応だがそういうゴミは無視で)
ついでに端末ローカルに使用したいフォントがインストールしてあればそれを使うようにsrc:にlocalで使用フォントを指定しておくともっと良いかと。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
@font-face {
  font-family: 'Noto Sans JP';
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: local("Noto Sans CJK JP"),
    local("Noto Sans JP"),
    url(//fonts.gstatic.com/ea/notosansjp/v5/NotoSansJP-Regular.woff2) format('woff2'),
    url(//fonts.gstatic.com/ea/notosansjp/v5/NotoSansJP-Regular.woff) format('woff'),
    url(//fonts.gstatic.com/ea/notosansjp/v5/NotoSansJP-Regular.otf) format('opentype');
}
CSSで使う
1
2
3
font-family: 'Noto Sans JP', sans-serif;
/* ↓ついでに文字詰めも指定 */
font-feature-settings : "palt"