[IE6]ブロック要素を中央寄せにするために、text-alignを使う件

ブロック要素を中央寄せにレイアウトする場合、左右marginをautoに設定するのが正しいCSSの書き方ですが、IE6の過去互換モードではこれが正しく動作しないのは有名です。

スクリーンショット:text-align:centerを使わずに中央寄せしようとすると

そこで、IEのために親ボックスにtext-align:centerを適用し、中央寄せにするというバッドノウハウと呼べなくもないテクニックが当たり前のように使われています。

text-alignプロパティは継承されますから、親ボックスにtext-align:centerを設定した場合、子孫ボックスではtext-align:leftと改めて設定して値を上書きすることがほとんどです。このtext-alignの上書きが面倒なのはもちろんですが、意図しない子孫要素にまで影響が及んでしまうので、できるならこのテクニックは使いたくないなー、というのが私の考えです。そこで、text-align:centerを使わずになんとかしようというアイデアをご紹介します。

親ボックスが div#wrapper で幅400px、中央寄せにしたい子ボックスが div#content で幅300pxという例でコードを考えていきます。次のコードは、text-align:centerを使った一般的なものです。

div#wrapper {
width: 400px;
text-align: center;
}
div#content {
width: 300px;
margin: 0 auto;
text-align: left;
}

これを以下のようにすることで、text-align:centerを使わずに、同じ表現をすることができます。

div#wrapper {
width: 400px;
}
div#content {
width: 300px;
margin: 0 auto 0 50px;
}

実際に中央寄せにした場合に左右に空くmarginを計算し、子ボックスの左marginに設定してやり、右marginはautoにします。これで、text-align:leftのまま、主要なブラウザ全てで中央寄せを実現できます。
このコードをIE6の互換モードで表示したのが、次のスクリーンショットです。(実際は見やすくするためにbackground-colorpaddingも併用しています)

スクリーンショット:text-align:centerを使わずに中央寄せ

ただし、弱点もあります。親ボックスが幅固定のレイアウトに限って有効なテクニックなので、リキッドレイアウトやエラスティックレイアウトでは使えないのはもちろん、body要素が直接含むボックスには使えない(body要素はウィンドウサイズによって幅が変わるため)という点です。
幅固定レイアウトでは絶大な(?)威力を発揮します。

ちょっとしたCSSのアイデアなのですが、あまり使ってる人見かけないな?と思ったのでご紹介しようと思いました。

置換インライン要素の縦方向の位置をborderで調整してみる

同じ行内にラジオボタンもしくはちっちゃな画像とか(つまり置換インライン要素)とテキストが混じっている場合、テキストの表示される縦方向の位置がブラウザによって結構違っていたりして、困りませんか?これを調整するには普通、ラジオボタンにvertical-alignプロパティを指定しますよね。baseline(規定値)とか、middleとか、bottomとか。

しかしvertical-alignプロパティで同じ値を指定しても、やっぱりブラウザによって微妙に位置がズレてしまうし、ピクセル単位での細かい指定ができません。これをborderで強引にやってみました。

置換インライン要素に縦方向のborderを設定してやることによって、borderの太さ分だけ縦位置をズラしてあげることができる……はずです。もちろんvertical-alignも併用するといいでしょう。
border-colorは背景色と同じにできるのがベストですが、背景画像があったりするとアウトなのでtransparentにします。

以下、実際にやってみたサンプルへのリンクです。

サンプルを見た方は既にお気づきかもしれませんが、残念ながらすごい微妙です。私がサンプルを表示確認をしたブラウザはIE6、IE7、Firefox2、Opera9、Safari3です。

Continue reading

hasLayout問題を解決するzoom:1;の落とし穴

IEのヘンテコなCSS解釈の原因と言われるhasLayoutプロパティ。
どうやらこのhasLayoutプロパティの値がfalse(デフォルト値)の場合に、いろいろとまずいことが起こるようです。IEだけfloatした要素周辺のmarginやpaddingがおかしかったり、相対配置/絶対配置した要素がどこかに消えてしまったりする・・・という経験はcssレイアウトの際に誰もがぶつかる問題です。

また、hasLayoutがTrueとFalseの要素が混在している場合には、IE7のズーム機能を利用した際に、隣あった要素が重なってしまったりします
この辺はコリスさんのIEでのCSSのバグを回避するhasLayoutに分かりやすい説明があります。

このhasLayoutの問題を解決するためにzoomプロパティが利用されることがあります。(zoomプロパティはIEの独自拡張であるため、これを使うこと自体どうか、という意見もありますがここでは触れません)
具体的には次のように、

* {
margin: 0;
padding: 0;
zoom: 1;
}

ユニバーサルセレクタに指定したり、

body,div,dl,dt,dd,ul,ol,li,h1,h2,h3,h4,h5,h6,pre,form,fieldset,input,textarea,p,blockquote,th,td {
margin:0;
padding: 0;
zoom:1;
}

などとして、ブラウザスタイルをリセットする際に同時に指定するのが一般的なようです。しかし、zoom:1;をli要素に指定してしまうとマーカーの表示がおかしくなってしまう問題があります
これに対しては

li {zoom: normal;}

としてzoomプロパティの値をデフォルトに戻すことで対処をできるようです。

ここまでは前提で、本題はここからです。

そんなわけで実際に業務で、スタイルリセットにzoom:1;を導入してみたのですが、さらなる落とし穴があることに気づいてしまいました。
STOPN’ LISTENさんのclearfixに関するの記事をご覧いただくとお分かりになると思うのですが・・・そうです。zoom:1;はclearfixにも使われるのです。

つまり、何でもかんでもzoom:1;にしてしまうと、floatが意図しないところでclearされてしまう恐れがあります。具体的には次の図のような例で、問題が起こります。

図: *{zoom:1}で問題の起こるレイアウトの例

XHTMLのコード

<p><img src="/img/2007/09/16_example_img.gif" alt="画像" width="120" height="90" class="photo" /> 段落1テキスト段落1テキスト段落1テキスト段落1テキスト段落1テキスト段落1テキスト段落1テキスト段落1テキスト段落1テキスト</p>
<p>段落2テキスト段落2テキスト段落2テキスト段落2テキスト段落2テキスト段落2テキスト段落2テキスト段落2テキスト段落2テキスト</p>

最初の段落に含まれている画像に対してfloat:leftを指定し、本文を右側に回りこませているという、何の変哲もないレイアウトです。回り込んでいる本文が途中で段落分けされていることがポイントです
ここで* {zoom: 1;}を指定すると、以下の図のようになります。

図: *{zoom:1}でレイアウトに問題が起こった状態の例

図と同じサンプルページも用意しました(IE6/7で見てくださいね)。

段落分けされた途端、clearされてしまっているのがお分かりになるかと思います。これはp要素に対してzoom:1;が効いているからです。これを回避するにはp {zoom:normal;}のようにしてデフォルトの値を上書きするしかありません。個人的にこれは全くナンセンスだと思うのですが・・・。

スタイルリセットの段階でzoom:1;を指定してしまうと、そのサイトのコンテンツは全てがこの挙動になってしまいます。

サイトをリニューアルする際にzoom:1;を新たに導入する、という場合には注意が必要です。今まで正常に表示されていたコンテンツが、先述の例のようにレイアウトが変わってしまうことがあるからです。

サイトを一人で制作し運営していく分には、この挙動を理解した上で制作をすることになるので、全く問題はないと思います。しかし、複数人でチームを組んで一つのサイトを制作・運営する場合には状況が違います。
制作チーム全員がこの挙動を理解することが必須となるのです。人数の多いチームともなると、これはなかなかに簡単なことではありません。

よって、複数人でチームを組んで制作・運営をしている大規模サイトの制作においては、スタイルリセットにzoom:1;を組み込むべきではない、と言えます。

ただ、やはりzoom:1;の便利さは捨てがたいものがあります。idセレクタや子孫セレクタなどを使用して、特定の領域にだけ限定的に利用する分には問題はないでしょう。

zoom:1;は便利ですがご利用は計画的にということです。

『Web標準の教科書―XHTMLとCSSでつくる“正しい”Webサイト』

写真: 『Web標準の教科書』 CYBER@GARDEN の益子貴寛氏が執筆した XHTML + CSS リファレンス本。2005年7月20日、第1版第1刷発行。

仕様に従った XHTML + CSS のコーディング、つまり Web 標準的なコーディングの大切さを教えてくれた本書は、コーダーとしての私のバイブルとなっています。

ブラウザで見る際の視覚的な結果からマークアップする要素を決定するのではなく、文章構造上妥当である要素を選択しマークアップしていくという考え方。今の日本の Web 業界にこういった Web 標準的な考え方を一般化するにあたり、本書はその一翼を担っていると私は確信しています。私はもし本書に出会わなかったら、コーダーになっていなかったかもしれません。

本書の内容では特に CSS のリファレンスが素晴らしく、各プロパティごとに「値・規定値・適用対象要素・継承するかしないか・パーセンテージ・メディア・未対応ブラウザ」といった項目が表にまとめられ、分かりやすい例を挙げながら解説されています。
XHTML の方も要素ごとに「書くことができる属性・含むことができる要素」などを詳しくまとめてあったらなお良いのでは・・・と思ってはいますが、要素ごとのマークアップ例は秀逸で、非常に参考になります。

600ページを越える書籍であるだけに、私は全てのページに目を通したわけではないのですが、必要となった時に必要とする項目だけを索引から引くという、辞書的な使い方をしています。コーディングをする上で手放せないものとなっていますので、会社用と自宅用で2冊持っていたりします。コーダー必携の1冊と言えるでしょう。

段組レイアウトのお約束:段組ボックスの底辺を揃える(揃っているように見せる)

使い古されたネタで申し訳ないのですが、自分が復習するという意味も含め、CSSで段組レイアウトをする際のお約束をまとめてみることにしました。まず第一回は「段組ボックスの底辺を揃える(揃っているように見せる)」です。

以下の画像のような見た目の段組を、実現することを目標とします。

目標とする段組レイアウト図

普通に考えると、float を使って横に並べたボックス各々に border を適用したくなります。が、このやり方ですと各ボックスの内容量が異なる場合、各々の高さが異なってしまい、ボックスの底辺が揃わなくなります。このように。

各段組ボックスの底辺が揃わないの図

そこで、border を使わずに目標の見た目を実現します。

以下に示す方法は、例として2カラムの段組を取り上げていますが、3カラム以上の段組でも同じように利用することができます。ただし、横幅固定デザインでのみ有効です。リキッドレイアウトやエラスティックレイアウトでは手法を変えないと利用できません。

まず以下のような画像を作り、スライスを切ります。

作成する画像と、それにスライスを切った図

緑色の部分がスライスと思ってください。段組中段の部分を実現するためのスライスと、段組底辺を実現するためのスライスです。わかりやすいように高さ10ピクセル程度のスライスにしていますが、実際は中段部分は高さ1ピクセル、底辺は線の太さと同じで大丈夫です。

で、次のようなソースを書きます。あくまで一例です。

XHTMLのコード

<div id="container">
<div class="section" id="columnAlpha">
<h2>コラムA</h2>
<p>サンプルテキストサンプルテキストサンプルテキスト...</p>
</div>
<div class="section" id="columnBeta">
<h2>コラムB</h2>
<p>サンプルテキストサンプルテキストサンプルテキスト...</p>
</div>
<p id="containerBtm">&nbsp;</p>
</div>

CSSのコード

div#container {background:url(中段部分の画像.gif) repeat-y;}
div#columnAlpha {
float: left;
width: 162px;
border-top: 2px solid #339;
margin-right: 10px;
}
div#columnBeta {
float: left;
width: 162px;
border-top: 2px solid #339;
}
p#containerBtm {
clear: both;
height: 2px;
background: url(底辺部分の画像.gif) no-repeat bottom;
font-size: 2px;
line-height: 2px;
}

段組しているボックスには上辺のみ border を指定し、親のコンテナボックスに対して段組中段部分の画像をY軸リピートで指定します。そして、段組ボックスの直後に、底辺となる p#containerBtm を挿入し clear:both と、底辺部分の画像を背景に指定します。height と font-size、line-height の指定は、このボックスを底辺画像の高さピッタリにするためのものです。

p#containerBtm にあたるものは、clear ができてかつ底辺画像が表示できれば、なんでもいいです。底辺画像は背景ではなくて、img 要素で埋め込んでも構いませんし。p#containerBtm 自体入れたくない方は、コンテナdivをもうひとつ追加して、そちらに底辺画像を背景で指定 & clearfix を使う、というやり方でもいいです。また、きちんと内容を持っているフッター的なボックスを持ってきてもいいでしょう。

以上のような方法で底辺を「揃っているように見せる」ことができます。(実際は揃ってないです)

JavaScriptを使って強引に揃えちゃったサイトを先日、はやしさんに教えていただきました。

オーケーズデリカ株式会社: お弁当

お弁当一覧のところです。なんとまあ・・・

なお、クロスブラウザな表示を実現する際に border は障害となる場合がありますので注意が必要です。この点は ADP: ピクセル単位で合わせるところではborderを使わない(IE7でも!) においてよくまとめられています。

段組ボックスでは上辺のみを表現し、親コンテナボックスで中段部分を表現するのがミソですね。この点を抑えて工夫すれば、リキッドレイアウトやエラスティックレイアウトにおいても実現する方法はありそうです。