画像を使わずにツリー状のサイトマップを実現するCSS

表題のとおりなんですが、画像を使わないで、ツリー状のサイトマップを作る方法をご紹介します。

キャプチャのようなサイトマップが出来上がります。

サイトマップのキャプチャ

まずはHTMLから。

<section>
  <h1><a href="#">ホーム</a></h1>
  <ul>
    <li>
      <a href="#">ページ1</a>
    </li>
    <li>
      <a href="#">ページ2</a>
      <ul>
        <li>
          <a href="#">ページ2-1</a>
        </li>
        <li>
          <a href="#">ページ2-2</a>
          <ul>
            <li>
              <a href="#">ページ2-2-1</a>
            </li>
          </ul>
        </li>
      </ul>
    </li>
    <li>
      <a href="#">ページ3</a>
      <ul>
        <li>
          <a href="#">ページ3-1</a>
        </li>
      </ul>
    </li>
  </ul>
</section>

とてもオーソドックスなul要素を使った階層構造になっています。HTML5でのマークアップを想定しているため、section要素を使っています。

次にCSSです。

a {
  display: inline-block;
}
h1 {
  margin: 0;
  line-height: 1.5;
  font-weight: normal;
  font-size: 100%;
}
ul {
  margin: 0;
  padding: 0;
  line-height: 1.5;
  list-style: none;
}
ul li {
  margin: 0 0 0 0.5em;
  padding: 0;
  border-left: 1px solid #999;
  zoom: 1;
}
ul li:before {
  margin-right: 0.5em;
  border-bottom: 1px solid #999;
  float: left;
  width: 1em;
  height: 0.75em;
  overflow: hidden;
  content: "";
}
ul li:last-child {
  border: none;
}
ul li:last-child:before {
  border-left: 1px solid #999;
}
ul li ul {
  margin-left: 1.5em;
}

注目して欲しいのはborderが指定してある箇所。li要素にborder-leftが指定してありますが、ul要素内の最後のli要素(li:last-child)だけはborderを表示させません。

そして、li要素内に生成させる擬似要素(li:before)に一行(line-height: 1.5)の半分の高さ(height: 0.75em)とborder-bottomを指定し、最後のli要素内の擬似要素にだけborder-leftを指定してあげます。

borderの長さはemで指定してあるので、どんなに文字サイズを変更しようとも崩れることはありません。

もちろん、borderの代わりに背景画像を適用してもOKです。

めでたしめでたし!と締めくくりたいところですが、これだけではモダンブラウザしかきちんと表示されません。残念なブラウザ代表のIEはCSS3のlast-child擬似クラスはもちろんのこと、IE6/7はCSS2のbefore擬似要素にすら対応していません。

IEに限らず、古いバージョンのブラウザはCSS2.1には準拠していても、CSS3には対応していない可能性もあります。そんなときこそjQueryの出番です。

jQueryの記述例は以下のとおり。

$(function(){
  $('section').addClass('tree')
  $('section.tree ul')
    .find('li:last-child')
      .addClass('last-child')
    .end()
    .find('li')
      .prepend('<span class="before"></span>')
    .end()
    .find('li:last-child>span.before')
      .addClass('last-child-before')
    .end();
});

そしてCSSを以下のように書き換えます。

section.tree a {
  display: inline-block;
}
section.tree h1 {
  margin: 0;
  line-height: 1.5;
  font-weight: normal;
  font-size: 100%;
}
section.tree ul {
  margin: 0;
  padding: 0;
  line-height: 1.5;
  list-style: none;
}
section.tree ul li {
  margin: 0 0 0 0.5em;
  border-left: 1px solid #999;
  zoom: 1;
}
section.tree ul li:before,
section.tree ul li .before {
  margin-right: 0.5em;
  border-bottom: 1px solid #999;
  float: left;
  width: 1em;
  height: 0.75em;
  overflow: hidden;
  content: "";
}
section.tree ul li.last-child {
  border: none;
}
section.tree ul li.last-child:before,
section.tree ul li .last-child-before {
  border-left: 1px solid #999;
}
section.tree ul li ul {
  margin-left: 1.5em;
}

jQueryでsection要素にtreeというクラスを追加した理由は、JavaScriptが無効になっていた場合、普通のリストととして表示させるためです。

これで、全てのブラウザに対応した、画像を使用しないツリー状のサイトマップは完成です。

残念ながら、CSSだけで全てのブラウザに対応したツリー状のサイトマップを作るのは厳しいです。もちろん手打ちでHTMLファイルを作る場合はbefore擬似要素やlast-child擬似クラスを使わないようにマークアップすればいいのですが、CMSで自動的にHTMLファイルを生成するようなケースでは、jQueryなどのスクリプトでクロスブラウザを実現する方が効率的です。

現在のウェブ制作手法の個人的見解ですが、とりあえずクロスブラウザのことは考えず、CSS2.1までのプロパティで一気に書き上げ、そのあとCSS3のプロパティが必要になる部分やIEなどのレガシーブラウザに対してはJavaScriptで対処するというのがベストプラクティスではないかと思っています。

流行のHTML5を採用する場合は、IE対策としてJavaScriptを使用することになるので、いっそのことCSSもある程度JavaScriptに依存してもいいのではないかと。