CSSハックについての具体的な技術の話の前に、そもそもなんでCSSハックが必要だったのか、というところから話したいと思います。
CSS崩れまくるし意味ワカンネと思っている方は、なぜあんなにめんどくさいのか、その理由を知ってください。
もう散々やってきたよという方は読み飛ばしても問題ありません。
最近IE6~8のサポートが終わったので、今後どんどん対応案件はなくなっていくんだろうな~と感じていますが、せっかくなので前時代の情報を残しておこうと思います。
今はもう、プログレッシブエンハンスメント
CSSという技術自体には仕様が当然あるのですが、各ブラウザの対応はまちまちです。
とくにCSSのバージョンアップに従って、古いブラウザでは新しいセレクタやプロパティに対応していないために実現できない表現なども出てきます。
最近ではプログレッシブエンハンスメントの考え方が主流です。
すべてのブラウザでまったく同じ見た目を再現することに拘泥するのではなく、たとえば「CSSに対応していればborder-raduisで角丸を表現するが、対応していないブラウザではわざわざ角丸にしなくてもコンテンツに影響はないよね」という感じ。
レガシーブラウザでは最低限の表示、モダンブラウザではよりリッチな表現を使うということなのですが、根底には「あくまでもコンテンツが大事で、正しいHTMLの記述を心がける」ということがあります。
実現したいデザインが前提にあってそこから考えてしまうと「古いブラウザを切り捨てる」という考え方になってしまうのですが、コンテンツから考えればそんなことをしていいはずもなく、たとえCSSがまったく効かなくても文書は読めなければいけない。
× 古いブラウザでは崩れててもいいや
○ CSSに対応しているブラウザならばよりリッチな表現をしよう
ということですね。
ところが、少し前の時代はそうではありませんでした。
ピクセルパーフェクトと呼ばれる、デザインと1pxたりともズレのない表示を実現することこそが至高という概念がありました。(今もありますが)
しかしブラウザによってレンダリングの実装がまちまちなので、仕様通りに書いたCSSでもすべてのブラウザで同じ表示にはなりません。
reset.cssの必要性
さらに、ブラウザには「User Agent Style Sheets」という、ブラウザ自体がもともと持っているスタイルシートがあります。
それがあるから、なんのスタイルもつけていないHTMLでも、h1が太文字でフォントサイズが大きくなったり、p要素の前後に余白ができたり、li要素にリストマーカーがついたりして、読みやすい形で表示されるわけですね。
でもこれ、自分で決めたデザインがあって、CSSの仕様を知っていて、正しくマークアップしたHTMLとCSSを書ける人にとっては、邪魔なだけなんですね。
とくに、ピクセルパーフェクトを実現したいときには、勝手に余白がついたり、その余白のサイズがブラウザによって違うなんて、非常に困ったことになるわけです。
そこで、reset.cssというテンプレートが発明されました。
自分でスタイルを記述する前に、装飾をなくすような記述をして、UserAgentStyleSheetsの効果をなくしてしまおう!というものです。
これで各ブラウザでまちまちだったmarginが0になったり、tableのborderが消え去ったりして、まっさらな状態からデザインをスタートできます。
使用するうえでのメリット・デメリットはこちらの記事が詳しいです。
ブラウザの持っているスタイルを上書きして、さらに自分のデザインのスタイルで上書きする、これはレンダリングのパフォーマンスにも影響します。
normalize.cssが出てきた
そこでしばらくすると、normalize.cssというテンプレートが発明されました。
すべての要素を上書きして無理やり更地にするのではなく、ブラウザ間の差異を埋めて近づけるというものです。
差異を埋めるならreset.cssで更地にしとけばいいじゃん、と思うかもしれませんが、前述したとおり上書きに上書きを重ねることになります。
たとえば、p要素の前後に余白を持たせたいとき、UserAgentStyleSheetsですでにmarginが設定されているのに、それを0で上書き。さらに自分で再度marginをつける。これってなんだか二度手間ですよね。
reset.cssで更地にしていたのは、ブラウザによって表示がまちまちの状態でCSSを書いてもずれてしまうからであって、最初からブラウザによってまちまちでなければ、その見た目に対して上書きしていけばいいので、同じサイズなら余白がついていてもそれほど困らないわけです。
ブラウザ間で挙動が違いすぎる部分だけあらかじめ上書きして表示を似せておいて、その状態でCSSを書きましょうね、というのがnormalize.cssです。
normalize.cssが何をしているのか、この記事で詳しく解説されています。
デメリットというか、これはCSSの仕組みをよく理解していて、かつパフォーマンスを少しでも向上させたいという人向けの技術かな、という印象です。
reset.cssで更地になる方が、初心者でも気にせず扱えるかと思います。
差異を近づけるだけで、あくまでもブラウザがもともと持っているスタイルを活かすという考え方なので、ピクセルパーフェクトを実現するのには向いていないかもしれません。どちらかというとプログレッシブエンハンスメントの考え方に近いですね。
それでもダメなら、コーラCSSハック
reset.cssで更地にしたぞ!
自分でスタイル書くぞ!
と意気込んだものの、やはり躓くことはあります。
reset.cssで更地にできるのはあくまでもUserAgentStyleSheetsで設定されたスタイルだけであって、ブラウザによる「CSSの解釈」の差は埋められません。
IEばかり槍玉にあげられることが多いですが、CSSの仕様からすればIEの実装の方が正しい場合もあります。
FirefoxやChromeはむしろ、使う側にとって都合のいい、期待する結果になるような実装をわざとしてるのかな?というときがありますね。
もちろんIEの未実装・独自実装が多いのも事実ですが。
width,margin,paddingあたりの組み合わせで、コンテナのサイズが変わってしまい中身がはみ出したりするのはそのせいです。
IE6だけバグがあるから、IE7以降と違うサイズの指定をしたい、なんていうときに「CSSハック」と呼ばれるゴリ押し技を使います。
有名なものだと、
*color: #000; _color: #333; color : green;
とかですね。
ほんの一例で、ほかにもたくさんあります。
しかしこの記述、一見、正しくない書き方のように見えます。
・・・そうです。正しくないのです。
正しくないから、正しく実装されたブラウザでは、解釈することができません。
古いブラウザではエラーとせずにこの余計な文字を無視して解釈して表示してしまったりします。
そこを逆手にとって、あえてこの書き方をして、解釈できるブラウザとできないブラウザでスタイルを変えているのですね。
ゴリ押し技と表現したくらいですから、私はこのCSSハックという技術、反対派です。
一応説明しておくと、ここでいうCSSハックはかなり狭義なので誤解しないでください。
正しい記述で読み込むCSSを分岐させるのもCSSハックと呼んだりしますので、あくまでも「言語の構文エラーを利用した手法」についての話だと思ってください。
CSSハックを使わないほうがいい理由
なぜ、使わないほうがいいのかというと、端的にいえば「わざとバグを起こしているから」ですね。
HTMLはプログラム言語と違って、構文が違っていてもエラー表示をせず、ブラウザが極力解釈して表示するようになっています。
でも、表示されないだけで本来はエラーが起こっている状態なのです。
その状態でそのままコーディングを続けていて、ブラウザが新しいバージョンになったときにバグが修正されていたら、ハックで対応していた部分のスタイルが適用されなくなってしまいます。(もちろんハックはそれを利用しているのだから当然ですね)
たとえばの話。
プロパティ名や値にスラッシュ「/」を入れるハックがあります。
今のモダンブラウザではその「/」が無視されています。
レガシーブラウザではその指定が効いています。
でも今後、「/」を用いた記法が生まれたらどうなるでしょうか。
現に、backgroundのショートハンドでbackground-sizeを指定するときなど、「/」が出てきますね。
毎回ハックのことも想定してブラウザが完璧に実装していればいいのですが、もしかしたらコード内に「/」が出てきたときの処理が変わっているかもしれません。不具合がある可能性もあります。
そうやって毎回毎回修正が必要になるの、困りますよね。
そしてそれは、間違ったCSSを書いているのだから、仕方のないことですよね。
CSSの仕様どおりに書ける方法
では、どうすればいいかというと、ブラウザのバグを探して利用するのはやめて、CSSの仕様でできることを探すことですね。
たとえば、こういうHTMLがあるとします。
<html lang="ja"> <body> <div> <p>テキスト</p> </div> </body> </html>
このpタグのスタイルを、IE6,7,8,それから9以降のモダンブラウザでそれぞれスタイルを切り替えたいとします。
p {background: #000;}/* ie6 */ div > p {background: #666;}/* ie7 */ html:lang(ja) p {background: #ccc;}/* ie8 */ html:root p {background: #fff;}/* ie9- */
これでオッケー☆⌒d(´∀`)ノ
すべて、仕様どおりで何の問題もない書き方です。
(ほかにもあるかもしれませんが私が知ってるのはこれだけ)
ひとつずつ説明します。
IE6
とりあずよけいなことは考えず、普通に指定しましょう。
セレクタは、「p」のみとしました。
IE7
セレクタは「div > p」となっています。
この「>」という子セレクタ(親要素の直下の子要素を指定する)は、IE6では実装されていませんでした。
IE7以降ではこのセレクタが使えるので、これでIE7以降では色が変わります。
IE8
時代が進み、使えるセレクタ(擬似class)が増えてきました。
「html:lang(ja)」がついていますね。
これはhtmlタグにlang属性がついていないと効かないので注意してください。
私が扱ったWebページでは省略することはなかったので、これを使っていました。
この擬似classはIE7では反映されないので、IE8以降で色が変わります。
IE9以降
もうわかってきましたね。
「:root」という擬似classがIE8ではまだ使えなかったので、セレクタを「html:root」とすることで、「html:root」が実装されているIE9以降で色が変わります。
上記の方法のメリットはもちろん、CSSの仕様に則った書き方なので、あとで使えなくなるおそれがあまりないということ。
古いブラウザを切り捨てるときは、セレクタ丸ごと消してしまえばいいということ。
「:root」などで検索すれば、そのあたりに上書き前のセレクタもあると思うので修正もしやすいです。
デメリットは、見てのとおり、コード量が多くなることです。
上書きする箇所があまりにも多くなるなら、最初からUAを見て読み込むCSSファイルを分岐させる方がいいですね。
IE6では基本のCSSのみ読み込み、モダンブラウザだったら上書き用のセレクタだけ書いたファイルを読み込む、など。
※これもある意味、CSSハックですが、人と話すときは文脈次第なので気をつけています。
まとめ
・・・と、紹介してきたのはいいものの、実を言うと私がこの方法を使っていたのは2年くらい前までで、今はIE6~8に対応する案件がほとんどないので使っていません(;´Д`)
でも、まだまだ対応中の現場も世の中にはたっくさんありますので、メモとして残しておきます。
CSSの仕組みを理解することで、あーでもないこーでもないと調整する手間が省け、すらすらコーディングできるようになってきます。
なんでも基本が大事ですね( ´∀`)bグッ!
いやモダンブラウザでしか見ないからハックとかより現代流の作り方教えてよ、って方にはこちらの書籍がおすすめです。