2007-09-13
CSSをURIのクエリーで切り替えるJavaScript
PC |
2008-03-06 追記: jQuery無しで動くように修正しました
あるといいかもと思って試しに作ってみたら、IE*1の印刷対応に端を発する問題でえらい苦労しました。苦労話は後回しにするとして、次が動作サンプルページとサンプルコードです。(要jQuery。要素の取得で楽してコードを短くするために使っています)
(function(){ //クエリーにstylesheetがあれば切り替え関数呼び出し if(location.search.match(/\bstylesheet=([\w]+)/)){ var titleValue = RegExp.$1; changeStyle(titleValue); } // CSS切り替え関数 // 引数titleValueと一致したtitle属性を持つlink要素を有効にする // 一致しなかったものは無効にする // もともとtitle属性が無いものには何もしない function changeStyle(titleValue){ //relとtitle属性の有るlinkを配列に格納 //var links = $('link[@rel*=style][@title]'); var head = document.getElementsByTagName('head')[0]; var allLinks = head.getElementsByTagName('link'); var links = []; for(var i=0; i<allLinks.length; i++){ var link = allLinks[i]; var linkRel = link.getAttribute('rel'); var linkTitle = link.getAttribute('title'); if(linkTitle && linkRel.match(/\bstylesheet\b/)){ links.push(link); } } //すべてDOMから撤去する var matchedLink; for(var i=0;i<links.length;i++){ links[i].disabled = true; links[i].parentNode.removeChild(links[i]); //引数と一致したものをmatchedLinkに保存する if(links[i].title == titleValue){ matchedLink = links[i]; } } if(!matchedLink)return; //有効にするlinkのrelをstylesheetにし、新規にDOMに追加 var link = document.createElement('link'); link.disabled = true; var attrs = matchedLink.attributes; for(var i=0;i<attrs.length;i++){ var nodeValue = attrs[i].nodeValue; var nodeName = attrs[i].nodeName; if(nodeValue){ if(nodeName=='rel'){ nodeValue='stylesheet'; } link.setAttribute(nodeName,nodeValue); } } head.appendChild(link); link.disabled = false; } })();
<link href="css/1st-impact_graphical.css" rel="stylesheet" type="text/css" title="graphical" /> <link href="css/1st-impact_simple.css" rel="alternate stylesheet" type="text/css" title="simple" />
↑こんな感じで代替スタイルシートを用意して、JavaScriptで切り替えるっていうよくあるかもしれないやつです。ただ、クエリーで切り替えたい!ってことで自分でも作ってみたんだけど、印刷にも応しようとしたらこれが結構な地雷原だったのです。上のコードで何でこんなことやってんの?な部分の理由は以下。
地雷その1「IEは rel="alternate stylesheet" を印刷で無視する」
例えば上の例では title="simple" のやつに切り替えてスクリーン上でそのCSSが適用されていても rel が "alternate stylesheet" のままだと印刷では無視されます。
地雷その2「じゃぁ切り替えるときに rel="stylesheet" に書き換えればよくね? → NG」
その読み込む CSS ファイルの中で @import が使われていた場合、IE ではそれが正常に読み込まれない問題が発生。(IE Developer Toolbar で確認してみると、該当 link の readyState が loading で固まってます)
地雷その3「じゃぁ DOM ツリーから一度撤去(remove)して、書き換えてから追加(append)するのはどうか → NG」
DOM ツリーから撤去した要素の属性は、IE ではリードオンリーになるらしく、書き換えようとするとエラーが発生してしまう。Firefox とか Opera は平気。
もうこの辺で嫌になって最終手段にでました。
解決編「元の link 要素を一度すべて撤去し、必要な link 要素を新たに作り、属性をコピって追加」
もちろん rel="stylesheet" にして。さすがにここまでやると IE も問題無し。大技と言うか大味というか、エレガントさがまるで感じられませんが、とりあえず今回はこれで妥協しました。
おまけ
実は link の disabled 属性を活用するともっと簡単に対応できます。
<link href="css/1st-impact_graphical.css" rel="stylesheet" type="text/css" title="graphical" /> <link disabled="disabled" href="css/1st-impact_simple.css" rel="stylesheet" type="text/css" title="simple" />
↑こんな感じで準備して、disabled を切り替える方法です。でもこれ Validator 通らないし Dreamweaver も disabled を無視するしで、とても作業がしづらくなるのでやめました。
