SP用メニューボタン
ソレゾレブログ

技術的な事だったり日常の気になる事だったり

Webページ読み込み時にWebフォントが再描画され表示がちらつくのを制御する方法

スポンサーリンク

はじめに

Webフォントの読み込みが完了する前にページが表示されると、フォントが途中で切り替わってしまいとても格好が悪いですよね。何か良い方法が無いかと調べた結果を忘れないように残しておきます。

FOUTとFOITについて

ページ読み込み時にフォントの読み込みが完了していないと、読み込み完了まで文字が非表示になったり、システムフォント(OSに標準で備わっているフォント)が表示されたりします。この制御には正式名称があります。

名称制御の内容
FOUT(Flash of Unstyled Text)フォントの読み込みが終わるまではシステムフォントが表示される
FOIT(Flash Of InvisibleText)フォントの読み込みが終わるまでは文字を非表示にする
ブラウザフォント読み込み完了までの制御
ChromeFOIT(3秒間)の後FOUT
SafariFOIT(読み込み完了まで)
EdgeFOUT
FirefoxFOIT(3秒間)の後FOUT

以降の記事で必要な情報となりますので覚えておいてください。

Typekitの再描画制御方法

Typekitの再描画を制御する方法はAdobe公式のページに書いてあります。そちらを参考に検証結果を交えて説明していきます。公式のページは下記となります。

Typekitの読み込み設定

再描画の制御をする前に、まずはTypekit フォント自体の読み込みが完了していなければ先に進めません。「Web プロジェクトの作成」「Web プロジェクトへのフォントの追加」「コードの埋め込み」「CSSの設定」を済ませておく必要があります。また、FOIT方式の制御を行うにはダイナミックサブセットを使用している必要があります。ダイナミックサブセットについては次の章で説明していますので、まずは次章を読んでから下記のページでTypekitの読み込みを行ってください。

Typekitダイナミックサブセットの必要性

(function(d) {
  var config = {
     kitId: 'タイプキットのID',
     scriptTimeout: 3000,
     async: true
  },
  h=d.documentElement,t=setTimeout(function(){
     h.className=h.className.replace(/\bwf-loading\b/g,"")+" wf-inactive";},config.scriptTimeout),tk=d.createElement("script"),f=false,s=d.getElementsByTagName("script")[0],a;h.className+=" wf-loading";tk.src='https://use.typekit.net/'+config.kitId+'.js';tk.async=true;tk.onload=tk.onreadystatechange=function(){a=this.readyState;if(f||a&&a!="complete"&&a!="loaded")return;f=true;clearTimeout(t);try{Typekit.load(config)}catch(e){}};s.parentNode.insertBefore(tk,s)
})(document);
<link rel="stylesheet" href="https://use.typekit.net/xxxxxxx.css">

ダイナミックサブセットを使用すると、フォントの読み込み状況に応じて、フォントの読み込み状況がわかるCSSクラスがhtml要素のclass属性に動的追加されます。それを利用してフォント再描画の制御を行います。

Typekitでフォントの読み込み状況に応じてhtml要素に追加されるクラス

フォントの読み込み状態によってhtml要素に下記のCSSクラスが追加されます。それを利用することでフォント再描画の対処が可能です。

 CSS クラスhtml要素に追加されるタイミング
.wf-loadingWebフォントの読み込み中
.wf-activeWebフォントの読み込み完了
.wf-inactiveWebフォントが読み込まれていない

TypekitでCSSだけを使って再描画を制御する方法

CSSでの対処方法はとても簡単です。Webページ読み込み直後はhtml要素を非表示にし、Webフォントの読み込みが完了したらページを表示する例です。

/* ページ読み込み直後は非表示にする */
html {
  visibility: hidden;
}

/* Webフォントの読み込みが完了すると.wf-activeクラスがhtml要素に追加され「visibility: visible;」によってページが表示される */
html.wf-active {
  visibility: visible;
}

気を付ける点は、「dispaly: none;」は使わないところです。Google AdsenceやGreenSockなど、親要素の属性値(サイズや位置)で子要素の幅や表示位置を決めるプラグインがある場合、「dispaly: none;」ですとGoogle AdsenceやGreenSockのスクリプト読み込み完了時点で親要素の実体がないので、属性が何もセットされておらず、引きずられて子要素の属性も決めることが出来ずうまく動きません。「visibility」は、実体はあるが非表示になっているだけなので、親要素の属性値が決まっており子要素の属性も決めることが出来ます。

TypekitのJavascriptコードで使えるコールバックイベント

Typekitの再描画をJavascriptで制御するにはイベントを使います。3つのタイミングでコールバックイベントを設定出来ます。configに追記することで簡単に呼び出すことが出来ます。下記が設定できるタイミングとコードです。

イベントコードタイミング
loading: function() { 実行したいコード }Webフォントの読み込み中
active: function() { 実行したいコード }Webフォントの読み込み完了
inactive: function() { 実行したいコード }Webフォントが読み込まれていない

TypekitでJavascriptによって再描画を制御する方法

この例では説明がわかりやすいようにCSSに「visibility: visible;」を追加する例で示していますが、Javascriptでの設定は本来もっと複雑なことをやりたい場合に使用が想定されてい方法だと思います。単に「visibility: visible;」を追加したい場合でしたらCSSを利用した方法がおすすめです。 あくまで設定例と思って読んでください。

(function(d) {
  var config = {
     kitId: 'タイプキットのID',
     scriptTimeout: 3000,
     async: true,
     active: function() { $('html').css('visibility', 'visible'); } //これを追加。私はjQueryで書きました。
  },
  h=d.documentElement,t=setTimeout(function(){
     h.className=h.className.replace(/\bwf-loading\b/g,"")+" wf-inactive";},config.scriptTimeout),tk=d.createElement("script"),f=false,s=d.getElementsByTagName("script")[0],a;h.className+=" wf-loading";tk.src='https://use.typekit.net/'+config.kitId+'.js';tk.async=true;tk.onload=tk.onreadystatechange=function(){a=this.readyState;if(f||a&&a!="complete"&&a!="loaded")return;f=true;clearTimeout(t);try{Typekit.load(config)}catch(e){}};s.parentNode.insertBefore(tk,s)
})(document);

CSSはページ読み込み前にHTML要素を非表示にする指定だけあれば、上記Javascriptコードがフォント呼び出し完了と共にCSSを書き換えます。

/* ページ読み込み直後は非表示にする */
html {
  visibility: hidden;
}
/* Webフォントの読み込みが完了するとJavascriptによって「visibility: visible;」に書き換えてくれる */

CSSを変更するぐらいでしたらわざわざJavascriptで操作しなくてもよいですが、複雑な制御をしたいのならJavaScriptによる制御がおすすめです。

そのほかにも使えそうな設定があるので公式ページも参考にしてください。

GoogleFontsの再描画制御方法

WebFontLoaderスクリプトの利用

Typekitのスクリプトは、Web Font Loaderのライブラリを元に提供されているようです。もしかしてWeb Font LoaderでもTypekitを呼び出せるかもとWeb Font Loaderのマニュアルにあるように設定して試してみましたが、呼び出せませんでした。私の設定が悪いだけかもしれませんが。

GoogleFonts基本のコード

3行目のような、font familyのスペースに「+」を書く表記方法でも読み込めます。

WebFontConfig = {
  google: { families: ['Akronim', 'Noto Sans JP:100,300,400,500,700,900', 'Noto Serif JP:200,300,400,500,600,700,900', 'Rampart One']}
// google: { families: ['Akronim', 'Noto+Sans+JP:100,300,400,500,700,900', 'Noto+Serif+JP:200,300,400,500,600,700,900', 'Rampart+One']}
};

(function(d) {
  var wf = d.createElement('script'), s = d.scripts[0];
  wf.src = 'https://ajax.googleapis.com/ajax/libs/webfont/1.6.26/webfont.js';
  wf.async = true;
  s.parentNode.insertBefore(wf, s);
})(document);

下記のようなlink relを使ったコードは不要ですので削除してかまいません。

<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Roboto:wght@100&display=swap" rel="stylesheet">

GoogleFontsの読み込み状況に応じてhtml要素に追加されるクラス

 CSS クラスhtml要素に追加されるタイミング
.wf-loadingWebフォントの読み込み中
.wf-activeWebフォントの読み込み完了
.wf-inactiveWebフォントが読み込まれていない
.wf-<familyname>-<fvd>-loading特定のWebフォントの読み込み中
.wf-<familyname>-<fvd>-active特定のWebフォントの読み込み完了
.wf-<familyname>-<fvd>-inactive特定のWebフォントが読み込まれていない

GoogleFontsのJavascriptコードで使えるコールバックイベント

イベントコードコールバックを発動するタイミング
loading : function() { コールバックするイベント記載 }全てのフォントが読み込み中
active : function() { コールバックするイベント記載 }全てのフォントが読み込み完了
inactive : function() { コールバックするイベント記載 }全てのフォントが読み込まれていない
fontloading : function( familyName, fvd ) { コールバックするイベント記載 }特定のフォントが読み込み中
fontactive : function( familyName, fvd ) { コールバックするイベント記載 }特定のフォントが読み込み完了
fontinactive : function( familyName, fvd ) { コールバックするイベント記載 }loading特定のフォントが読み込まれていない

イベントの引数を覗いてみる

引数にどんな値が格納されているかわからなければ条件として使えません。下記のようにChromeの開発者コンソールにメッセージを表示して確認してみましょう。

WebFontConfig = {
  google: { families: ['Akronim', 'Noto Sans JP:100,300,400,500,700,900', 'Noto Serif JP:200,300,400,500,600,700,900', 'Rampart One']},
  loading: function() {
     console.log('loading');
  },
  active: function() {
    console.log('active');
  },
  inactive: function() {
    console.log('inactive');
  },
  fontloading: function(familyName, fvd) {
    console.log('fontloading', familyName, fvd);
  },
  fontactive: function(familyName, fvd) {
    console.log('fontactive', familyName, fvd);
  },
  fontinactive: function(familyName, fvd) {
    console.log('fontinactive', familyName, fvd);
  }
};

(function(d) {
  var wf = d.createElement('script'), s = d.scripts[0];
  wf.src = 'https://ajax.googleapis.com/ajax/libs/webfont/1.6.26/webfont.js';
  wf.async = true;
  s.parentNode.insertBefore(wf, s);
})(document);

このコードを実行すると、開発者コンソールに下記のように表示されます。下記は全て正常に読み込まれた例です。

Web Font Loaderのイベント引数を開発者コンソールで確認1

次は読み込まれなかったフォントがある例です。「Noto Sans JP」を「Noto Sans J」に書き換えて読み込みました。読み込まれなかったフォントだけ「fontinactive」イベントのメッセージで出力されています。

Web Font Loaderのイベント引数を開発者コンソールで確認2

引数に格納されてくる値がわかったところで、再描画の制御方法について書いていきます。

GoogleFontsでJavascriptによって再描画を制御する方法

この例では説明がわかりやすいようにCSSに「visibility: visible;」を追加する例で示していますが、Javascriptでの設定は本来もっと複雑なことをやりたい場合に使用が想定されてい方法だと思います。単に「visibility: visible;」を追加したい場合でしたらCSSを利用した方法がおすすめです。あくまで設定例と思って読んでください。

ここでは、「すべてのフォントが読み込み完了」のパターンと「特定のフォントが読み込み完了」パターンの設定例を挙げます。

【例1】すべてのフォントが読み込み完了したタイミングでページを表示するパターン

まずは、フォント読み込み完了までhtml要素が非表示となるように下記CSSを書きます。

/* ページ読み込み直後は非表示にする */
html {
  visibility: hidden;
}
/* Webフォントの読み込みが完了するとJavascriptによって「visibility: visible;」に書き換えてくれる */

次にイベントを書いていきます。フォントの読み込みが全て完了したら、html要素の「visibility」を「visible」に書き換えます。

WebFontConfig = {
  google: { families: ['Akronim', 'Noto Sans JP:100,300,400,500,700,900', 'Noto Serif JP:200,300,400,500,600,700,900', 'Rampart One']},
  active: function() {
    $('html').css('visibility', 'visible');
  }
};

(function(d) {
  var wf = d.createElement('script'), s = d.scripts[0];
  wf.src = 'https://ajax.googleapis.com/ajax/libs/webfont/1.6.26/webfont.js';
  wf.async = true;
  s.parentNode.insertBefore(wf, s);
})(document);

これでOKです。

【例2】特定のフォントが読み込み完了したタイミングでページを表示するパターン

CSSは同じです。イベントは下記のように書きます。font family「Noto Sans JP」とweight「n7」を指定した例です。「Rampart One」のようにweightの指定がひとつしかない場合は条件から「fvd」の判定を除いて指定しても良いです。尚、複数のweightがある場合にweightを省略すると、normalのn4が返されます。

WebFontConfig = {
  google: { families: ['Akronim', 'Noto Sans JP:100,300,400,500,700,900', 'Noto Serif JP:200,300,400,500,600,700,900', 'Rampart One']},
  fontloading: function(familyName, fvd) {
    if ( familyName === 'Noto Sans JP' && fvd === 'n7' ) {
       $('html').css('visibility', 'visible');
    }
  }
};

(function(d) {
  var wf = d.createElement('script'), s = d.scripts[0];
  wf.src = 'https://ajax.googleapis.com/ajax/libs/webfont/1.6.26/webfont.js';
  wf.async = true;
  s.parentNode.insertBefore(wf, s);
})(document);

これでOKです。

GoogleFontsでCSSだけで再描画を制御する方法

特定のフォントの読み込みが完了した場合、「.wf-<familyname>-<fvd>-active」クラスがhtml要素に追加されます。「familyname」と「fvd」についてですが、例えばfamilynameが「Rampart One」でfvdが「n4」だった場合は、「.wf-rampartone-n4-active」がクラス名となります。スペースとアンダースコアが削除されて、すべて小文字になります。

実際にCSSに追加する設定は下記となります。

/* ページ読み込み直後は非表示にする */
html {
  visibility: hidden;
}

/* Webフォントの読み込みが完了すると.wf-rampartone-n4-activeクラスがhtml要素に追加され「visibility: visible;」によってページが表示される */
html.wf-rampartone-n4-active {
  visibility: visible;
}