以下斜め読んだ内容

pseudo translation of useful posts, book reviews, remarks,etc. twitter: feeddict

FOUC問題まとめ

2008.11.18更新

  • Surfin’ Safariの記事を再読してみてわかったことを反映

はじめに

旬な話題ではないが、自分がブラウザの読込とかレンダリングを勉強するときに言及されてたので勉強したことをまとめる

FOUC is...

  • Flash of Unstyled Content、のこと
  • Flash of unstyled content - Wikipedia
  • CSSが適用されていないコンテンツが一瞬表示されること
  • スタイル適用が中途半端な形で表示されてしまう瞬間・時間帯がある場合もFOUCに含められている模様
    • レイアウト用のスタイルだけが適用された状態で一瞬表示されて、遅れてタイポグラフィー用のスタイルが適用されること
    • スタイルを指定しているのは、html内でも、cssファイルでもjsファイルでも構わない
  • ページ設計者が意図していない形でページが表示される瞬間・時間帯があること
  • 段階的に表示が変化するように設計している場合。これはFOUCではない。

IEにおけるFOUC ~2006年

FOUCはまずIEにおいて発生する問題として認識された

//www.bluerobot.com/web/css/fouc.asp/" target="_blank">Flash of Unstyled Content (FOUC):FOUC問題の初出?ウィキペディアでは2001年のエントリとのこと。IEで生じる問題と位置づけ、解決方法2点提示。前にちょっと書いた
<style type="text/css" media="screen">@import "style.css";</style>

style要素を使い、さらに@importでcssファイルへリンクしているときに発生。bluerobotのエントリで提示されててる解決策は2つ。

  • 問題になるstyle要素の上にlink要素を置く(印刷用のcssファイルへのリンクなど)
  • 問題になるstyle要素に上に空のscript要素を置く
bluebotさんの解決方法だと不要な要素を敢えて置く場合が発生しうる。つまり、ちょっと妥協してる

そうした妥協のない解決法は、Yahoo!Developer Networkの有名な記事Best Practices for Speeding Up Your Web Siteにある

  • link要素を使ってcssファイルへリンクする(style要素と@importの組み合わせは使わない)。

を実行すればよくなる。

SafariにおけるFOUC 2006年~

SafariでもFOUC問題が発生する。

//webkit.org/blog/66/the-fouc-problem/" target="_blank">Surfin’ Safari - Blog Archive ≫ The FOUC Problem:Safariで生じるFOUCについて。生じる元になるブラウザの挙動について書かれてる。by Dave Hyatt
//www.456bereastreet.com/archive/200609/flash_of_unstyled_content_fouc_explained/" target="_blank">Flash of Unstyled Content (FOUC) explained | 456 Berea Street:bluerobotのエントリと>Surfin’ Safariの記事への反応記事。情報量は追加されてる情報は特に無し。SafariでのFOUCの出現はランダムとか言ってるが、これは問題に関係するcssファイルとjsファイルがキャッシュされてるかどうかが念頭になければ「ランダムに起こってるかのように見える」という話だと解した。

Surfin’ Safariのエントリの内容

スタイルシートのロード要求時にブラウザが取りうる反応は3パターン。

  • パースを中断しスタイルシートをロード。Geckoはこれ。
  • パースは続行。Safariはこれ。
  • 必要に応じて中断(stall on demand)。、Dave Hyattはこれが理想と言ってる

Safariでは、パースを中断しないために表示するタイミングも速くなるが、スタイルシートのロード要求時もパースをそのまま続行するために、必要なスタイル情報をロードする前にページを表示する可能性が出てくる。これがSafariにおけるFOUC問題。
cssおよびjsに含まれるスタイル情報が全てロードされる前に、

  • scrollHeight
  • offsetWidth

といったプロパティへjavascriptでアクセスするとFOUCが出現する、とDave Hyattは言ってる。
この記事が書かれた頃(2006年9月)はSafariのバージョンは2だが、ナイトリーでは、全くスタイル適用されてないコンテンツの表示だけは回避しようとしたとある(これが、現在のSafari3では実装済みなのかは、後で調べる)。
パース中断とパース続行。どちらがいいのかは、中途半端なスタイルが適用されたページの表示と完全にスタイル適用されたページの表示の落差(滑稽、おかしい、醜い、等々)次第。ということで3番目の

  • 必要に応じて中断(stall on demand)

が理想案として浮上してくる。「stall on demand」は

  • パースは続行する。
  • パース中にレイアウト関連のプロパティへのアクセスが発生時は、パースを中断しスタイルシートのロードを待つ
  • スタイルシートのロード完了後中断したパース(およびレイアウト関連のプロパティへの再アクセス)再開

とまとめられてる。確かに一番柔軟で魅力的に思える。ネックは

  • 技術的に要求されてることが高い
  • 現在ウェブの世界で圧倒的に普及してるスクリプトでは「stall on demand」の魅力が奪われる可能性が高い
  • 「stall on demand」からメリットを差し引けば「パースを中断しスタイルシートをロード」と効果の点で大差なくなる

という点にある、圧倒的に普及してるスクリプトとして念頭にあるのはAdSenseのスクリプト。
Google AdSenseのためにhtml内に埋め込むスクリプト(ex.show_ad.js)の特徴は

  • インラインでコード実行(not-unobtrusiveなjavascript
  • レイアウト関連へのプロパティへアクセスが頻発

.....
C.prototype.oa = function ()
{
var a = this.s, b, c;
if (!A && a.compatMode == "CSS1Compat") {
b = a.documentElement.scrollLeft;
c = a.documentElement.scrollTop
}
....

しかし、AdSenseのコードは改変できないからAdSenseを入れてるページではFOUCは回避できない。
「on demand stall」を実装できても、「適宜中断」が常態化してしまえば「常に中断」と大差なくなる。
AdSenseのコードを使わざるを得ない状況のためFOUCが頭痛の種になる。

FOUC in Safariへはどうしたらいいか

  • スクリプトを見直す
    • ページへのスタイル適用が完了するまでは、スクリプトがレイアウト関連のプロパティ(ex.offsetWidth)へアクセスしない
  • AdSenseのコードが改善される(上で紹介した456 Berea Streetの人もこれを言ってる)

FOUC問題 2008年移行

deliciousで関連記事探しをしてみたら見つけたエントリ

//www.learningjquery.com/2008/10/1-awesome-way-to-avoid-the-not-so-excellent-flash-of-amazing-unstyled-content" target="_blank"> 1 (Awesome) Way To Avoid the (Not So Excellent) Flash of (Amazing) Unstyled Content ≫ Learning jQuery - Tutorials and Information:2008年10月の記事。Safari上でのFOUC問題とは関係なかった。

どういう問題・・・
jsで視覚整形しようとするとき、CSSによる視覚整形と同時にできない場合があり、そのとき中途半端なスタイル表示させてしまう。
この問題に対する解決方法・・・

  • cssのスタイルが適用されるタイミングとjsによるスタイル適用のタイミングが同時じゃないのが問題
  • jsによるスタイル適用が完了するまでページの表示を止める。
  • jsがオフのときcssのみのスタイル適用でレイアウト崩れが起きないようにする progressive enhancement
  • head要素内からscript要素が読み込まれてる時点でhtml要素は既にアクセスできることを利用
  • html要素へjsファイルからクラス追加。
  • 中途半端な表示のされる要素(デモページだとul#flash)が予め用意していた.jsを含むセレクタ(".js #flash")によって非表示にされる。
  • DOMツリー構築後、非表示にされてた要素のスタイルを上書き

脱線だがlearningjqueryのエントリ読んで勉強になったこと

  • htmlにclass指定するのは仕様違反であること
  • noscript要素の子要素にstyle要素を入れることは仕様違反。他には色々子要素になれる要素がある。