横並びのブロック要素の作成はWeb技術入門者にとっての最初の難関であり、その考え方を理解するのに多くの時間を要します。テーブルレイアウトに逃げてしまう元凶でもあります。
ボタンを左から右に向かって並べるナビゲーションバーなんて、まさにその好例です。
どのようにマークアップすべきか?
かつて「テーブルレイアウト」と呼ばれる手法が一般的だった頃、この実装は「table要素」を使うのがスタンダードでした。
しかし「フルCSSレイアウト」が推奨される現在では、このやり方はご法度です。Webにはセマンティクスという考え方があり、table要素は表を作る以外の目的に使うことが推奨されません。検索エンジンのクローラーや視覚障がい者に優しくない、つまり「アクセシビリティ」の低いマークアップと評価されます。
HTML4.01/XHTML1.0で作るのであれば、以下のマークアップが推奨される方法でしょう。
div要素を使う人も多いですが、よりセマンティクスにマークアップしたいという人はul/liを使って記述する印象があります。
HTML5であれば、ナビゲーションを表す専用のタグがあります。少し非効率的ですが、ナビゲーションバーは以下のように記述すべきです。
ただし、この方法はIE9以上でしか動作しません。インターネット上のWebコンテンツ製作で利用するには、もう少し時間が必要でしょう。
ナビゲーションバー用途のリンクは、マウスオーバー時にビジュアルデザインを変えるのが普通です。aタグの:hoverが活用できると、JavaScriptを利用せずにインタラクティブなビジュアルを実現できます。
したがって、2013年の現時点では、以下のマークアップが最小で効率的です。
float:leftの問題点
フルCSSレイアウトで横並びのナビゲーションバーを作る場合、ブロック要素に対してCSSのfloatプロパティにleftを指定します。例えば、以下のような構造を持つHTMLドキュメントでは・・・
<div class="nav"> <a class="item" href="#">項目1</a> <a class="item" href="#">項目2</a> <a class="item" href="#">項目3</a> <a class="item" href="#">項目4</a> </div>
CSSは、以下のように指定します。
.nav>.item { display : block; float : left; ~ }
「.nav>.item」で、navクラス内のitemクラスに限定して、スタイルを適用させています。
a要素はそのままだと「インライン要素」と呼ばれる、テキスト中での利用を想定した動作をしてしまいます。これを、ボタンや記事表示枠といった、画面を構成するコンポーネントとして動作する「ブロック要素」という状態へ切り替えを行う必要があります。「display:block;」が、その指定です。
そして最も注目すべきは「float : left;」です。float:leftが指定されたブロック要素は、親のブロック要素内で左寄せが行われます。そして後続するブロック要素にもfloat:leftが指定されていた場合は、そのまま隣へ配置されます。
floatの持つこの特性を利用すれば、左から右に向かって並ぶボタンを実現することが可能になります。
ただ、floatには少し厄介な癖があります。元々floatはWebページ上のテキストで、画像などのイメージの配置を目的に作られたものです。新聞の記事のように、写真の周りへ文章の回り込みを行わせることを意図して作られたものです。
上記のCSSの設定だと、ナビゲーションバー直後に記述した文章が回りこんでしまいます。Webブラウザで表示すると、こんな見た目になります。
floatは、回り込みをして欲しくない場合に、「clear:both;」というプロパティをセットしなくてはいけません。この例では、あとに続くテキストを含んだ要素に対して、clear:bothを指定する必要があります。
つまり、こんなイメージです。
clear:bothの設定により、ナビゲーションバーの後のテキストは、回り込まれること無く下に表示されます。
独立した2つのコンポーネント間で、互いにCSSの設定が依存し合うというのは、デザインとしてあまり美しくありません。
今回のケースでは、「ナビゲーションバー」と「記事」という2つコンポーネントの間に別のコンポーネントを埋め込みたくなった場合、「ナビゲーションバー」のデザインに引っ張られて、「記事」からclear:bothを削除し、「追加対象のコンポーネント」へclear:bothを追加する必要が生じます。
「ナビゲーションバー」と後に続く「記事」は、独立することが望ましいでしょう。そこで、以下のようなマークアップとCSSプロパティ設定が考えられました。
何の情報も持たない「ダミー要素」を作るのです。ダミー要素をナビゲーションバーの一部として扱うことで、ナビゲーションバーのCSSプロパティを独立させることができます。
ただ、デザインのために無意味な要素を追加するというのは、デザインの作りこみをHTMLドキュメント上で行うことになります。ビジュアルデザインを決定する情報はCSS内で完結させるのが理想なわけですから、当然、良いデザインとは言えません。そこで・・・
clearfix法
オーストラリアのとあるWebデベロッパが、この問題を解決するCSS記述方法を考案しました。のちにこれは全世界で広く利用されることになります。その方法とは「clearfix法」です。デファクトスタンダードなので、なぜclearfixがclearfixなのか、考えたことすらない人もいるでしょう。
先ほどの例では、HTMLドキュメント上でダミー要素を使ってclear:bothを設定しましたが、これをCSS側で擬似要素として生成して設定してしまおうというアイデアがclearfixです。他の要素に迷惑をかけずに、floatを閉じるためのベストプラクティス。
そのCSSとは、以下の通りです。
.nav>.item { float : left; } .nav:after { content : ""; display : block; clear : both; }
「::after」という擬似要素の指定子を利用しています。実にシンプルです。IE8からサポートされたため、動作するのはIE8以上とモダンブラウザです。
この手法、発明されたのは2004年頃らしく、当時はIE5や6でも動かすことを想定し、より複雑な記述をしていました。しかし、Web標準への準拠が高いIE8を想定するのであれば、この書き方で十分です。
IE6以上でclearfixするには?
未だにIE6を対象にしなくてはいけないケースもあるでしょう。この技術が作られた当時は、ネットスケープも対象にしていたので、今よりもっと複雑な状況です。
ここではあくまで、IE6にフォーカスを当てましょう。CSSは以下の記述です。
.clearfix { *zoom: 1; } .clearfix:after { content: "."; display: block; clear: both; height: 0; visibility: hidden; overflow: hidden; }
前の章の「.nav」をそのまま「.clearfix」に置き換えて下さい。書き方が独特なので、多くの場合「.clearfix」という汎用的なクラスにすることで、再利用性を高めています。
様々な派生があり、例えばclearfixをwidth:100%にする亜種も発見されています。ただ、ナビゲーションバーそのもののビジュアルデザインを柔軟に変更できる方が有益なため、padding/marginの制約が小さなzoom:1が広まっているように思えます。
頭にアスタリスクの付いているzoomは、::afterに対応していない古いIE対策です。アスタリスクハックとも言われます。IEのバグを突いて、古いIEのみで動作するCSS記述を行っています。Webブラウザ固有の作りこみは悪とは言われますが、この手の定石では許されるでしょう。(古いIEだとCSS Expresstionも使えますが、奴はもっと悪です!!)
ベストプラクティスといいつつも、実際は状況に応じて最適な方を選ぶべきという答えになるかと思います。
sassでclearfixするには?
こんな感じです。凄くシンプルですね。
.clearfix { float : left; &:after { content : ""; display : block; clear : both; } }
Web標準でclearfixするには?
clearfixはデファクトスタンダードですが、最近はこれをWeb標準でなんとかしようという方向に向かっているようです。ただ、HTML5の設計面の改善はやや遅れてスタートしてて、使いもにになるにはまだ先です。
まず、clear-afterというプロパティがあります。これは2013年現在Editors Draftです。2013年に更新したようですので、これから改善が進められる段階です。
また、flex boxなる標準もあります。flexboxといえば、勧告候補にまでいきながら突如としてEditors Draftに落っこち名前ごとごっそり変わるというハチャメチャな過去を持っていますが、なんとか持ち直して勧告候補になっています。今もなお絶賛テスト中ですが、なんだかんだ言っても一番無難に使われそうな候補です。
古いIEが駆逐されないことには、使い物にならないのですが・・・。
inline-block法(※小西俊司氏のご指摘により追記。)
floatが好きになれない人には、「inline-block法」という選択肢もあります。
clearfix法では、ナビゲーションバーの各項目を明示的に左から右に向かって配置されるよう、CSS上で定義していていました。対してinline-block法では、テキストの文字は左から右に書き出されるというルールを利用して項目を配置します。
要素のdisplayプロパティに「inline-block」を指定すると、インライン要素の「テキストへ文字のように埋め込める」という特性と、ブロック要素の「縦・横幅が指定できる」という双方の特性を持ちます。プレーンなブロック要素を並べると上から下に向かって配置されますが、インラインブロック要素を並べるとテキストの行(line)の特性に従って左から右へ並べられます。(※英語・日本語が前提です。)
floatは要素の「場所」を指定するためのものでしたが、inline-blockは要素を「テキストの行内に配置」することを目的としたものです。そしてそこが問題になります。
例えば、改行やタブは、Webページ上でテキストとして扱う場合、1文字分のスペースとして扱われてしまいます。HTMLドキュメントのマークアップ方法にもよりますが、メンテナンス性を損なわないためにナビゲーションバーの各項目を一行で記述すると、必然として項目間に「改行やスペース」を含むことになります。
<div class="nav"> <div class="item">項目1</div> → display:inline-block; <div class="item">項目2</div> → display:inline-block; <div class="item">項目3</div> → display:inline-block; <div class="item">項目4</div> → display:inline-block; </div>
そしてこの改行やスペースは、Webページ上ではテキストという扱いとなるため、各項目間に1文字分のスペースを作ってしまいます。
これを埋めるために、以下のCSSを設定するのが定石です。
.nav{ letter-spacing:-0.4em; } .nav>.item { letter-spacing: normal; }
ナビゲーションバーの大元の部分、つまり各項目の親となる要素で、文字間隔を短くするCSSプロパティ(letter-spacing:-0.4em)を設定します。しかしこのままでは各項目内のテキストの文字幅に影響が出るため、文字間隔を元に戻す(letter-spacing: normal)という設定を行います。
するとこの通り、各項目の間のスペースがなくなります。
inline-block法では、配置場所を指定するためのCSSプロパティに、一見して無関係なテキストの書き出し方を指定するという泥臭い記述を行う必要があります。
clearfixとinline-block、どちらも普通なら思いつかないようなハックを行う必要があり、知らない人から見た可読性の酷さはどんぐりの背比べ状態です。ただ、inline-block法の場合は「テキストの特性」を要素の配置位置の決定要因として利用するという、完全にWeb標準の意図から外れた方法を利用しています。デザインはフルCSSレイアウト派かテーブルレイアウト派かという議論で、私は前者を勧めるタイプの人間なので、inline-block法もまた私好みのやり方ではありません。
しかし、テーブルレイアウトと同様、シンプルで直感的な記述ができるため、選択の一つとして持っておく価値はあるでしょう。どちらも方法も完全な正攻法とは言い難く、好みの問題と言わざるを得ません。
最後に
メンテナブルなナビゲーションバーを実現するclearfix法やinline-block法ですが、SEOやらアクセシビリティ的にかなり重要なHTMLドキュメントは守られても、CSSは超泥臭い本来の用途から外したある種のハックです。一時のテーブルレイアウト排除運動みたいな流れが来る気がします。ただ、テーブルレイアウトと違って相互運用性に関わるため、悪という言い方はしにくいでしょう。
clearfixに至ってはIEのバグまで使っていますが、バグを仕様と見なすのは80386なCPUから残る悪しき伝統なので、むしろ美のレベルに達している気がします。最初にこれを発見した人は、天才じゃないかとさえ思えます。
cleafix法やinline-block法は今のところ、多くのネット上のサービスではユーザCSSに含まれています。ただ個人的には、もう少し違うレイヤーに組み込まれてもいいのではと思っています。sassみたいなビルドプロセスも当たり前になりつつあるので、このあたりで上手く改善できるはずです。
古いIEが消えない状況下では、ビルドプロセスは結構熱い分野に思えます。JSPみたいなリスクもありますが、既存のWeb標準を拡張させる類のメカニズムは、Web標準そのものに良い影響を与えるはずです。