2013年6月7日金曜日

alternate stylesheet (代替スタイルシート) が使えない場合の回避策とか

HTML5 上で、動いている端末や設定毎に一部のスタイルシートを動的に切り替えたい。という需要が発生し、alternate stylesheet を使って対処を行ったのだが、Windows版 Safari 5.1 で使えないために、対策を行った時のメモを残しておく。



おおよその定義としては、以下の様な感じで、JavaScript で制御するために id を付けている点。

<link id="css1" rel="stylesheet" type="text/css" href="hoge1.css">
<link id="css2" rel="alternate stylesheet" type="text/css" href="hoge2.css">
<link id="css3" rel="alternate stylesheet" type="text/css" href="hoge3.css">

一般的に公開されているサンプルでは、ボタン押下のタイミングで使う CSS の rel を 'stylesheet' に設定し、他の不要なものを 'alternate stylesheet' に切り替えるものが多いと思われる。これ自体は W3C の公式な仕様のはずだが、一部のブラウザでは主要なものでありながらサポートされていない事もあるようだ。

Windows版の Safari 5.1 の場合、定義している一通りのスタイルシートが有効になっているようで、中の定義自体が重複する場合は一番最後のスタイルシートが強制的に使われるみたい。


回避策としては、いくつか挙げられるだろう。

・その手の JavaScript ライブラリを使う。
  実装が結構変わったり、依存性が発生するかもしれない。私はその点を考慮し敬遠した。
  そもそもに組んでいるのが JavaScript のこってりしたアプリだったから、というのもある。

・HTML 上の可変部分の link 定義を一つに絞り、href を動的に書き換える。
  一応動きはするが、キャッシュされていないスタイルシートが指定された場合、
  ファイル自体は設定されてはいるが、ページに意図した CSS が適用されていない
  (スタイルシート読み込み中の)状態がしばらくの間発生する。

  単純に切り替えだけで済む場合は一時的に意図しないレイアウトになるだけで
  致命的な状態にはならないかもしれないが、見栄えが良くない状況は発生しうる。

  それ以上に、JavaScript で動的に HTML のパーツを生成しながら、スタイルシートが
  適用されたパーツの座標を基準値として使うような実装を行なっていた場合
  タイミングによっては座標のズレが多発してしまい非常に大きな問題となりうる。
  大概の場合、初回の読み込み時点では、まともに動くことはないだろう。

  上記は、スタイルシートの適用完了後に処理を進める必要があるため、完了を捉える
  イベントが必要となるが、img タグ onload イベントに相当するイベントは link タグに
  存在しないため、自前で対象ブラウザに依存しない判定処理の実装が必要となる。
  実現は可能であるが、問題回避のために別な問題が起こるため微妙な策といえる。



私が作っていたのは単純なウェブページではなく、ページ単体の JavaScript がメインとなり、Ajax でサーバからデータを取りつつゴリゴリと動く、こってりした仕様のアプリケーションだったのだが、複数定義してある CSS の中から、使う CSS が確定するタイミングはアプリの起動時点で、もし、使う CSS を含む設定変更やらが発生した場合には、localStorage に設定情報を確保後、ページのリロードを行う。という設計が取れたため、別な回避策を取ってみた。

具体的には、一般的な alternate stylesheet を使うように、一通りの定義をしておく。そして、CSS が確定できたタイミングにおいて、以下の処理を一度だけ呼び CSS を確定させた。

function decideCss(target) {
  for (i=1;i<=3;i++) {
    var link = document.getElementById('css' + i);
    if (target == i) {
      link.rel = "stylesheet";
    }
    else {
      // link.rel = "alternate stylesheet";
      link.parentNode.removeChild(link);
    }
}

つまり、alternate stylesheet を使うのではなく、完全に link を削除したのである。当然ながら、ページをリロードすること無く、動的に切り替える仕様では全く使えないものであるが、今回の私の開発対象のように起動時点で設定が一通り確定するようなページを作る場合にはロジックも大きく変えること無く実装できる。

また、ページ読み込み完了後のタイミングで、このロジックを動かしておけば、一通りの CSS の読み込みが確定できた段階で切り替えができるため、安定性についても問題ない。ブラウザによっては CSS を切り替えてから適用されるのが、一連の function を抜けた後になっているようなので、この問題については、タイマー (setTimeout) を使って自ら遅延イベントを作り、一旦制御をブラウザに渡してから後続の処理を進めることで解決した。


実装してみて解ったことだが、アプリの設定適用をページリロードにしてしまうのは、途中で設定変更して処理を続行する事態が一切発生しなくなるため、処理の流れを非常にシンプルにできるため、オススメである。

0 件のコメント:

コメントを投稿