CDN から CSS ファイルが読み込めなかった時のフォールバック対策方法
CDN から CSS ファイルが読み込めなかった時に、自前で用意しているミラーの CSS ファイルを読み込ませるようなフォールバック (切り戻し) 対策の方法はないか調べた。
JavaScript の場合は、グローバルに対象の変数が存在するかどうかで JS ファイルを読み直す方法がよく知られている。以下のようなやり方だ。
<script src="//ajax.googleapis.com/ajax/libs/jquery/2.1.3/jquery.min.js"></script>
<script>window.jQuery || document.write('<script src="./jquery.min.js"><\/script>');</script>
1行目で CDN から正しく読み込めていたら window.jQuery
が存在するはずなので、その場合は2行目は何もしない。window.jQuery
が存在しない = CDN から読み込めていないということになるので、document.write
を使用して自サーバの jQuery を読み込ませようとしている。
これの CSS 版はないかな、というのが今回の話。
link
要素に onload
属性を付けてハンドリングする
上のサイトで紹介されていた、以下のコードが一番スッキリしてやりやすそう。
<link rel="stylesheet" onload="window.hljscss = 1;" href="//cdnjs.cloudflare.com/ajax/libs/highlight.js/8.4/styles/docco.min.css">
<script>window.hljscss || document.write('<link rel="stylesheet" href="/highlight.docco.min.css">');</script>
link
要素の onload
属性内で、その CSS ファイルを示すグローバル変数に何か値を入れておく。そして次の行で JS の場合と同じようにグローバル変数の存在チェックを行い、なければ document.write
で link
要素を生成する、というやり方だ。
onload
属性は body
要素だけでなく、img
・iframe
・link
要素などにも適用できるようだ。
ただ、この書き方だと、2行目のスクリプトが1行目の CSS の読み込みを待ってから実行されるのかどうか、微妙な気がする。
と思って調べてみると、以下の記事では window.onload
イベント内で CSS の検証をしていた。やはり window.onload
に addEventListener
でイベントを追加する方が良いかもしれない。
CSSのロードは
onload
以前に行われるもので、DOMContentLoaded
はCSSのロードを待ちませんので、onload
に仕掛ける必要があります。一方で、CSSどうしに(こっちをロードしておかないと別のがロードにも失敗する、という意味合いでの)依存関係は発生し得ないので、後から差し替えても問題なく表示できます。
CSSRule を確認してハンドリングする
CSS が読み込めているかの検証方法は、実際にその CSS ファイルで指定されそうなスタイルがページに適用されているかをチェックしたりしている他、CSSStyleSheet
が返す CSSRule
の数を数える、という方法もあるみたい。
<script type="text/javascript">
$.each(document.styleSheets, function(i, sheet) {
if(sheet.href === 'http://code.jquery.com/mobile/1.0b3/jquery.mobile-1.0b3.min.css') {
var rules = sheet.rules ? sheet.rules : sheet.cssRules;
if(rules.length == 0) {
$('<link rel="stylesheet" type="text/css" href="path/to/local/jquery.mobile-1.0b3.min.css">').appendTo('head');
}
}
});
</script>
jQuery を使っているが、Vanilla JS にも書き換えられるレベル。これまで知らなかったのだが、document.styleSheets
で、読み込んだ順に link
要素や style
要素を抱えて持っているみたい。
- 参考 : CSSStyleSheet - Web API | MDN
- 参考 : CSSRule - Web API | MDN
- 参考 : javascriptでスタイルシートを自在に操る(styleSheetsオブジェクト)入門基本編 | Unskilled?
ただ、どうも @import
しか書いていない CSS ファイルの場合は CSSRule に計上されない?らしい (未検証)。読み込む CSS ファイルの内容によっては完璧ではないかもしれないが、かなり精緻なやり方かなと思う。
フォールバックを考慮した JavaScript ライブラリを使う
YepNope や Fallback.js といったライブラリで、こうしたフォールバックを考慮した記述ができるようだ。
以下のサイトにサンプルコードがあるので、それぞれ雰囲気を掴んでみてもらいたい。
ただ、これってこのライブラリの JavaScript の読み込みを待ってから CSS の読み込みをして…って流れになるから、表示までが少し遅くなりそう?そしてこのライブラリのフォールバックも…とかなると大変かも。
こんなところ。CDN を使う時は少し考慮してみよう…。