2013年1月22日火曜日

svgのサポート状況によってスタイルやスクリプトを切り替える・考察

前回はsvgをサポートしない環境において,背景画像に設定されたsvg画像をpng等の代替画像に入れ替える方法を見ました.この動作をもう少し離れた視点で眺めてみると,「svg未サポート環境でのみ適用されるスタイル」は作れるということになります.

となると,更なる疑問として「svgをサポートする環境でのみ適用されるスタイル」は作れるのか?問題が生まれてきます.また,styleと似た立ち位置に居るscript要素についても同様のことを考えることができます.

なお結論から言うと,svgをサポートする環境で「のみ」動作するスタイル・スクリプトをdomの構造だけで指定することは不可能か不完全です.本記事ではこれらについての考察をまとめたもので,完璧な解決策を与えるものではありません.

※誰かsafari6とか,試してみてくれんかなー・・・

問題提起・svgのサポート状態でhtmlの処理を切り替えたい.


現状svgをサポートしないwebブラウザの存在がsvg普及の足枷となっているのは否定できないことだと思います.従ってsvgのサポート状況によって処理の振り分けを考えるのは自然なことです.

ここでの処理とはスタイルの適用とスクリプト処理を指すこととします.すると,今回検討すべき問題は次の4つに集約されることとなります.

(1)svgをサポートする環境
のみ動作するスタイル
(2)svgをサポートしない環境
のみ動作するスタイル
(3)svgをサポートする環境
のみ動作するスクリプト
(4)svgをサポートしない環境
のみ動作するスクリプト

スタイル指定における考察


スタイルにおいては前回見た通りmetadata要素とlink要素とを組み合わせることでsvgをサポートしない環境でのみ動作するスタイル(つまり2番)を定義できることが判っています.

ですがこれはあくまでスタイルを追加するケースであって,(1)の「svgをサポートする環境でのみ動作するスタイル」を指定したことにはなりません.ではどうすれば良いのでしょうか?

この問題についていろいろ考えたのですが,筆者は不可能と結論付けます.
根拠としてはインラインsvgを含むhtml文書において,スタイルを指定する方法はlink要素を用いる方法とstyle要素を用いる方法の二つがありますが,これらは何れもsvgをサポートしない環境でも自動的に有効となってしまうからです.

(xml−stylesheet命令を使う方法はスタンドアロンのsvgでのみ有効な方法です)

従って何らかの補助的な手段(例えばjavascript) を利用する必要が出てきます.

スクリプトにおける考察

同様のことをscript要素について考えてみましょう.script要素において外部スクリプトファイルを参照する方法はhtmlとsvgとで異なります.
  • htmlの場合・・・src属性から参照する
  • svgの場合・・・xlink:href属性から参照する
従ってこの仕組みを応用することで環境に応じたスクリプトの実行を行うことができそうです.
例えば次のようなコードを考えてみましょう.
<html>
<body>
    <svg>
        <script xlink:href="a.js"></script>
        <script src="b.js"></script>
    </svg>
</body>
</html>

これをsvgのサポート状況毎に考えてみると,次のようになるでしょう.
  • svgをサポートする環境ではsvg要素配下のscript要素のうち,xlink:href属性を使って外部コードを参照している要素が正しい事になります.→つまりa.jsが実行されます.
  • svgをサポートしない環境ではsvg要素の存在が無視されます.従ってsrc属性を使って外部コードを参照している要素が正しいことになります.→つまりb.jsが実行されます.

このように処理の振り分けができました.従ってsvgが有効な環境でのみ有効となるスタイルもこのscript要素の効果を用いればなんとかなりそうです.

サンプルコードの記述


以上のことを踏まえて次のようなサンプルコードを記述して見ました.

●メインとなるhtml文書
<!DOCTYPE html>
<html>
 <head>
 <meta http-equiv="content-type" content="text/html; charset=UTF-8"/>
 </head>
 <body>
  <svg display="none">
   <!--svgをサポートする環境で実行したいもの-->
   <!--スクリプトレスでスタイルを指定する方法は無いので,スクリプト内部でスタイルを指定する.-->
   <script xlink:href="style.js"></script>
   <script xlink:href="svg.js"></script>
   <!--svgをサポートしない環境で実行したいもの-->
   <metadata>
    <link rel="stylesheet" href="nonsvg.css" type="text/css"/>
   </metadata>
   <script src="nonsvg.js"></script>
  </svg>
  <div id="msg1"></div>
  <div id="msg2"></div>
 </body>
</html>

○svgが有効な環境で動作させたいもの
●style.js
※スタイルを追加するスクリプト
var link = document.createElement("link");
link.rel = "stylesheet";
link.href = "svg.css";
link.type = "text/css";
document.head.appendChild(link);

●svg.css
div{
 color:blue;
}

●svg.js
window.onload = function(){
 document.getElementById("msg1").innerHTML = "svgサポート";
};

○svgが無効な環境で動作させたいもの
●nonsvg.css
div{
 color:red;
}

●nonsvg.js
window.onload = function(){
 document.getElementById("msg2").innerHTML = "svg非サポート";
};

このコードを実行すると次のようになるはずです.
  • svgをサポートする環境では青い文字で「svgサポート」と表示される.
  • svgをサポートしない環境では赤い文字で「svg非サポート」と表示される.
それでは試してみましょう.

実際に試してみると・・・


辻褄上はこれで全て問題は解決されるはずなのですが・・・意外な結果が得られました.
  • firefox・・・問題なし
  • chrome・・・問題なし
  • opera・・・問題なし
  • safari5・・・正常に動作せず
  • ie6,7,8・・・問題なし
  • ie9,10・・・問題なし
safariでのみ動作しないのです.原因は直ぐに判明して,どうやらsafariでは「インラインsvg配下のscript要素そのものが無効となる」 ようなのです.(※safari6での動作は不明です)

safariと言えばios系の環境における標準的なブラウザですから,多くの環境で利用されていることが想像できます.従ってこの方法を利用するにはデメリットが余りに大きいこととなります.

※なお,上記の結果から「svgをサポートしない環境でのみ実行されるscript要素」は問題なく動作していることが判ります.

svgの動作を判定するもうひとつの方法


ではこの他にsvgのサポート有無を判定する方法は無いのでしょうか?実はあります.svgdomの定義有無を確認するのです.例を示します.

<!DOCTYPE html>
<html>
 <head>
 <meta http-equiv="content-type" content="text/html; charset=UTF-8"/>
 </head>
 <body>
  <div id="msg"></div>
  <script type="text/javascript">
var msg = document.getElementById("msg");
if(document.createElementNS && document.createElementNS("http://www.w3.org/2000/svg", "svg").viewBox){
 msg.innerHTML = "svgサポート";
}else{
 msg.innerHTML = "svg非サポート";
}
  </script>
 </body>
</html>

  • documentオブジェクトがcreateElementNSメソッドを持っているかを確認します.
    →レガシーieのふるい落とし
  • createElementNSメソッドで試しにsvg要素し,その内容がviewBoxプロパティを持っているかを確認します.
    →古いandroid環境では空のsvgオブジェクトが生成されます.
この条件判定を元に処理の振り分けを行うことができます.

まとめ


以上のことから,svgのサポート有無による処理の振り分けを行う場合は次のようにするとよいでしょう.
  • スタイルは通常svgをサポートする環境での設定をメインとし,非サポート環境で必要となるスタイルは前回紹介した「svg-metadata-link」 パターンで追加(上書き)指定します.
  • スクリプトはsvgdomの実装有無で処理を分岐しましょう.
  • script要素の属性を使った処理の振り分けはsvgをサポートしない環境でのみスクリプトを実行したい場合に有効で,その逆はsafariで問題を起こします.
なんともややこしい結果となってしまいましたが,処理の振り分けギミックのバリエーションが増える分には,問題解決の糸口が増えるだけなので良いこととしましょう.

0 件のコメント:

コメントを投稿