対応ブラウザをかなり拡げた場合の最適なCSSの読み込み方法、web-doc-baseのブートシーケンスについて
「main.js から CSS を読み込む」に、document.write()
での CSS の読み込みについて追記。(2023/08/23)
<noscript>
の .textContent
から CSS の URL を取得することが出来ないブラウザに AOSP 3未満を追加し、記事にも反映しました。(2023/01/05)
<noscript>
の .textContent
から CSS の URL を取得することが出来ないブラウザを修正し、記事にも反映しました。Chrome 2~3 を除外していたのは誤りでした。(2022/05/01)
ブラウザ毎の CSS の分け方とファイル名の変更を反映。(2022/04/28)
<noscript>
の .textContent
から CSS の URL を取得することが出来ないブラウザに Chrome 4~7 を加えました。(2022/03/18)
「9~9.2x でも一部の非対応なモードのスタイルが適用
」は誤りでした。(2022/03/01)
「javascript を有効にするか Web ブラウザの変更を促す警告メッセージ」の HTML タグに </p>
を追記。(2020/03/01)
「javascript を有効にするか Web ブラウザの変更を促す警告メッセージ」の HTML タグを大幅に修正しました。github(2022/02/23)
<noscript>
の .textContent
から CSS の URL を取得することが出来ないブラウザについて調査と実装を終えて、追記しました。(2022/02/09)
「javascript を有効にするか Web ブラウザの変更を促す警告メッセージ」の HTML タグを追記しました。github(2022/02/03)
記事中でグレースフルデグラデーションとしている部分ですが、レグレッシブ・エンハンスメントと読み替えた方が良いかもしれません。(2021/12/16)
CSS を一本化しているにも係わらず CSS の読み込み経路が複数になり、やや複雑になってきたので記事化しておくこととしました。
1. はじめに
僕の Web 文書用ベースプロジェクト web-doc-base では、新旧を問わず広範なブラウザで閲覧性を良好にすべく開発を続けています。
この内、プログレッシブエンハンスメントを諦めざるを得ない一部のレガシーブラウザに対しては、各ブラウザ用の CSS を用意して Javascript で読み込むようになりました。javascript が無効の場合は、モダンブラウザ用の CSS が読み込まれてしまうことになります。つまり、グレースフルデグラデーションでの対応です。
併せてモダンブラウザについても Javascript が有効な場合は、Javascript で CSS を読み込むこととなりました。これは、レガシーブラウザが無用なモダンブラウザ用の CSS をロードしない為に、<style>
を <noscript>
で囲んだことによる変更です。
1. 1. プログレッシブエンハンスメントを一部で諦めた件について
長らく javascript の無効なレガシーブラウザについてもある程度の閲覧性を確保したい、と考えてきましたが、Opera 9.5 未満の @media
クエリがヤバすぎるので諦めました。以下の記述は 8.5x 以下についてですが、9~9.2x でも一部の非対応なモードのスタイルが適用されました。(この部分は誤りでした。2022/03/01)
中でも Opera 8 以下には
@media
規則に致命的な不備がありました。ダークモードやハイコントラストモード用のスタイルが問答無用に適用されていく様を見て、Javascript に依存しない CSS ハックでの対処を放棄せざるを得ませんでした。
2. CSS読み込みのチェックポイント
2. 1. ファーストビューのインライン化
ここ何年かで Web 文書のパフォーマンスチェックツールが推奨してくる、ファーストビュー部分の CSS のインライン化は未着手です。
2. 2. document.write('<link>')
について
モダンブラウザでは document.write()
での <link rel="stylesheet">
の追加はページのパフォーマンスの観点から推奨されません。この件については web.dev による解説文の該当箇所を次に引用します。
一方で一部のレガシーブラウザでは document.write('<link>')
でないとスタイルの適用が不安定なものがあります。僕が確認しているのは、Opera 9.0 未満と Gecko 1.0 未満です。
Using
document.write()
can delay the display of page content by tens of seconds and is particularly problematic for users on slow connections. Chrome therefore blocks the execution ofdocument.write()
in many cases, meaning you can't rely on it.
document.write()
を使用すると、ページコンテンツの表示が数十秒遅れることがあり、低速回線のユーザーにとっては特に問題となります。そのため、Chrome は多くの場合、document.write()
の実行をブロックしており、document.write()
に依存できないようになっています。
2. 3. なるべく外部 javascript からメインの CSS を読み込まない
クロスブラウザ DOM 操作ライブラリ等の組み込まれた、main.js から <link rel="stylesheet">
を追加していたところ、一度だけですが、この main.js がネットワーク側の理由で読み込まれないケースに遭遇しました。つまり CSS も読み込まれません。
このレアケースに対処する為に、殆んどのブラウザについてはインラインの javascript で <link>
を追加することにしました。ライブラリのサポートが必要等の一部のブラウザでのみ、main.js から <link>
要素を追加しています。
3. ブラウザ毎の CSS を読み込む経路の解説
ブラウザ毎に異なる CSS を読み込む経路について解説します。フロー図の1~4の各経路番号について詳しく記述していきます。
css | js enabled | js disabled | |
---|---|---|---|
IE 5, 5.5, 6, 7, 8, 9 | ie5win.css, ie55.css, ie6.css, ie7.css, ie8.css, ie9.css | 2 | 2 |
IE10≦, Opera 9.5≦, Gecko 1.9.1≦, Safari 4.0.5≦, Chrome 8≦, AOSP 3≦, !IE && window.addEventListener | modern.css | 3 | 1 |
Gecko 1.5~1.9.0 | gck19.css | ||
Mac IE5 | ie5mac.css | ||
Safari ≦4.0.5, Chrome < 8, AOSP < 3, !IE && !Opera && !Gecko && !window.addEventListener | modern.css | 4 | 1 |
Opera 9.5 未満 | opr70.css, opr72.css, opr8.css, opr9.css | ||
Gecko 1.5 未満 | gck07.css, gck08.css, gck09.css, gck12.css, gck13.css, gck19.css | ||
IE4 | 未対応 |
3. 1. 条件付きコメント非対応ブラウザで javascript が無効の場合
IE 以外のブラウザ用または Mac IE5 用の CSS が読み込まれます。
このケースでは Gecko 1.9.1 未満、Opera 9.5 未満にも modern.css
が読み込まれてしまいます。デザインが崩れるに留まらず、最悪の場合ではブラウザがクラッシュします。
次の HTML タグで「javascript を有効にするか Web ブラウザの変更を促す警告メッセージ」を表示して閲覧者に対処を促します。幸いにクラッシュしなかった場合は、閲覧者側の対処を期待できます。
メッセージはテキストでは無く content
で表示しています。これは CSS が無効の場合、IE5~9 以外のブラウザの全てでメッセージが表示されてしまうのを避ける為です。
<!--[if !IE]><!-->
<noscript>
<div id="-o-"><!-- 旧いブラウザには noscript p セレクタが効かない! -->
<style>/*<![CDATA[*/
/* common */
#-o- p{border:double 5px #f66;padding:1em;background:#300;color:#fff}
/* Opera 7.20~9.27 */
@media all and(-o-:0),not all and(-o-:0){
html:first-child #-o-{display:block}
:_{top:0} /* Opera ~7.11 skip next rule */
}
/* common */
#-o-{display:none}
#-o- p:after{content:attr(nojs) attr(opr)}
/* Opera 用ルールを先に、Gecko 用を後に書く */
/* Gecko ~1.8 */
@media \0 all{
#-o-{display:block}
#-o- p:after{content:attr(nojs) attr(gck)}
}
@-moz-document url-prefix(){
/* Gecko 1.8~1.9.2 */
_:not(),_:-moz-loading,#-o-{display:block}
_:not(),_:-moz-loading,#-o- p:after{content:attr(nojs) attr(gck)}
/* Gecko 1.9.1~1.9.2 */
_:not(),_:-moz-handler-blocked,#-o-{display:none}
}
/*]]>*/</style>
<p nojs="Please enabled javascript or use new version of browser. At least " opr="Opera 9.5+." gck="Firefox 3.5+."><!-- inline 要素は不可 --></p>
</div>
</noscript>
<!--<![endif]-->
2022年2月23日に修正済。2022年3月1日に </p>
を追記。<noscript>
下の p
要素の閉じタグを省略できない。
短縮版
<!--[if !IE]><!--><noscript><div id="-o-"><style>/*<![CDATA[*/#-o- p{border:double 5px #f66;padding:1em;background:#300;color:#fff}@media all and(-o-:0),not all and(-o-:0){html:first-child #-o-{display:block}:_{top:0}}#-o-{display:none}#-o- p:after{content:attr(nojs) attr(opr)}@media \0 all{#-o-{display:block}#-o- p:after{content:attr(nojs) attr(gck)}}@-moz-document url-prefix(){_:not(),_:-moz-loading,#-o-{display:block}_:not(),_:-moz-loading,#-o- p:after{content:attr(nojs) attr(gck)}_:not(),_:-moz-handler-blocked,#-o-{display:none}}/*]]>*/</style><p nojs="Please enabled javascript or use new version of browser. At least " opr="Opera 9.5+." gck="Firefox 3.5+."></p></div></noscript><!--<![endif]-->
2022年2月23日に修正済。2022年3月1日に </p>
を追記。<noscript>
下の p
要素の閉じタグを省略できない。
3. 2. IE5~9 の場合
Javascript の死活にかかわらず、条件付きコメントで各ブラウザ用の CSS を読み込みます。
3. 3. インライン javascript から CSS を読み込む
殆んどの IE 以外のブラウザ(addEventListener
をサポートするもの)と Mac IE 5 用の CSS を読み込ます。
main.js のロードに失敗するケースに備えて、ごく一部のブラウザを除いてインライン javascript で CSS を読み込みます。
load
または DOMContentLoaded
イベントコールバック内で <link>
要素を追加します。CSS のパスは <noscript>
の textContent
から求めます。
3. 4. main.js から CSS を読み込む
Presto < 9.5, Gecko < 1.5, Safari < 4.0.5, Chrome < 8, AOSP < 3, IE 以外の addEventListener
をサポートしないブラウザは常にここで CSS を読み込みます。
インライン javascript の CSS ローダーを使わないビルド設定の場合、全ての IE 以外のブラウザと Mac IE 5 もここから CSS を読み込みます。
<script>
の src
からアセットディレクトリを取得します。Gecko < 1, Presto < 9 は、CSS 周りのバグを回避する為に document.write()
で <link>
要素を追加します。(そもそも Gecko 0.6~0.9.0は動的外部スタイルに非対応の為。Presto はスタイルの適用が document.write()
でないと不安定だった為。2023/08/23 追記)
Safari < 3 には load
イベントがいない為、このフォールバックのある main.js から CSS を読み込みます。
9 ≦ Presto < 9.5, 1 ≦ Gecko < 1.5, 3 ≦ Safari < 4.0.5, Chrome < 8, AOSP < 3 は <noscript>
の .textContent
にアクセスして CSS のパスを取得することが出来なかった為、この経路になりました。(2022/02/09, 2022/03/18 追記, 2022/05/01 修正)