CSSだけで作る「多階層」なドロップダウンメニュー

以前書いた「CSSだけで作る動きのあるドロップダウンメニュー|Webpark」という記事が検索エンジン経由でそこそこアクセスがあり、よく質問をいただきます。そのなかで多階層にできないのかという質問があったので作ってみました。

drop-down-menu-3level2.png

まずはサンプルをご覧ください。

IE7の場合擬似要素が使えませんので、次の階層があることを示す矢印が表示されません。ドロップダウンの機能はIE7でも問題ありませんので、IE7に対応させる場合は矢印を画像にするなどすればよいと思います。

それではHTMLからいきます。

HTML

かなり長くなるのでメニュー1の部分だけ載せています。

<ul id="dropmenu">
  <li><a href="#">メニュー1</a>
    <ul>
      <li><a href="#">子メニュー</a>
        <ul>
          <li><a href="#">孫メニュー</a></li>
          <li><a href="#">孫メニュー</a>
            <ul>
              <li><a href="#">ひ孫メニュー</a></li>
              <li><a href="#">ひ孫メニュー</a></li>
              <li><a href="#">ひ孫メニュー</a></li>
              <li><a href="#">ひ孫メニュー</a></li>
            </ul>
          </li>
          <li><a href="#">孫メニュー</a></li>
          <li><a href="#">孫メニュー</a></li>
        </ul>
      </li>
      <li><a href="#">子メニュー</a>
        <ul>
          <li><a href="#">孫メニュー</a></li>
          <li><a href="#">孫メニュー</a></li>
          <li><a href="#">孫メニュー</a></li>
          <li><a href="#">孫メニュー</a></li>
        </ul>
      </li>
      <li><a href="#">子メニュー</a>
         <ul>
          <li><a href="#">孫メニュー</a></li>
          <li><a href="#">孫メニュー</a></li>
          <li><a href="#">孫メニュー</a>
            <ul>
              <li><a href="#">ひ孫メニュー</a></li>
              <li><a href="#">ひ孫メニュー</a></li>
              <li><a href="#">ひ孫メニュー</a></li>
              <li><a href="#">ひ孫メニュー</a></li>
            </ul>
          </li>
          <li><a href="#">孫メニュー</a></li>
        </ul>
      </li>
      <li><a href="#">子メニュー</a>
        <ul>
          <li><a href="#">孫メニュー</a></li>
          <li><a href="#">孫メニュー</a></li>
          <li><a href="#">孫メニュー</a></li>
          <li><a href="#">孫メニュー</a></li>
        </ul>
      </li>
    </ul>
  </li>

かなり長いですが、要するに次の階層がある場合はliの中にリストを入れているだけです。

ということで以下のようになるのは間違いです。

<li><a href="#">メニュー</a></li>
<ul>
  <li><a href="#">子メニュー</a></li>
  <li><a href="#">子メニュー</a></li>
</ul>

CSS

前回は角丸にしたり、ボーダーを重ねたりしましたが、今回それをするとコードが膨大になりますのでシンプルにしました。逆にこちらの方が今っぽいかも。

ではまず全体を載せます。

#dropmenu {
  list-style-type: none;
  width: 960px;
  margin: 30px auto 500px;
  padding: 0;
}
#dropmenu li {
  position: relative;
  width: 20%;
  float: left;
  margin: 0;
  padding: 0;
  text-align: center;
}
#dropmenu li a {
  display: block;
  margin: 0;
  padding: 16px 0;
  background: #8a9b0f;
  color: #fff;
  font-size: 14px;
  font-weight: bold;
  line-height: 1;
  text-decoration: none;
}
#dropmenu li ul {
  list-style: none;
  position: absolute;
  z-index: 100;
  top: 100%;
  left: 0;
  width: 100%;
  margin: 0;
  padding: 0;
}
#dropmenu li ul li {
  overflow: hidden;
  width: 100%;
  height: 0;
  color: #fff;
  transition: .2s;
}
#dropmenu li ul li a {
  padding: 13px 15px;
  background: #7c8c0e;
  text-align: left;
  font-size: 12px;
  font-weight: normal;
}
#dropmenu > li:hover > a {  background: #7c8c0e }
#dropmenu > li:hover li:hover > a {  background: #6e7c0c }
#dropmenu li:hover > ul > li {
  overflow: visible;
  height: 38px;
}
#dropmenu li ul li ul {
  top: 0;
  left: 100%;
}
#dropmenu li:last-child ul li ul {
  left: -100%;
  width: 100%;
}
#dropmenu li ul li ul li a {  background: #616d0b }
#dropmenu li:hover ul li ul li:hover > a {  background: #535d09 }
#dropmenu li ul li ul:before {
  position: absolute;
  content: "";
  top: 13px;
  left: -20px;
  width: 0;
  height: 0;
  border: 5px solid transparent;
  border-left-color: #454e08;
}
#dropmenu li:last-child ul li ul:before {
  position: absolute;
  content: "";
  top: 13px;
  left: 200%;
  margin-left: -20px;
  border: 5px solid transparent;
  border-right-color: #454e08;
}

長くて嫌になるかもしれませんが順を追って考えればそんなに難しくはないと思います。

では、次からは個別に解説します。勉強になると思うのでお付き合いいただければと思います。

親メニュー
#dropmenu {
  list-style-type: none;
  width: 960px;
  margin: 30px auto 500px;
  padding: 0;
}
#dropmenu li {
  position: relative;
  width: 20%;
  float: left;
  margin: 0;
  padding: 0;
  text-align: center;
}
#dropmenu li a {
  display: block;
  margin: 0;
  padding: 16px 0;
  background: #8a9b0f;
  color: #fff;
  font-size: 14px;
  font-weight: bold;
  line-height: 1;
  text-decoration: none;
}

最初から表示されている親メニューの部分です。全体の長さは今回960pxにしています。ここ以外の横幅は全てパーセントで指定していますので、全体の幅を変えるときはここだけで大丈夫です。全体の幅はパーセントで指定するとリキッドなメニューになります。

「#dropmenu li」は子メニューの基準となりますので「position: relative;」を指定します。

他の部分については横並びメニューの基本ですので問題ないと思います。

子以下のメニュー
#dropmenu li ul {
  list-style: none;
  position: absolute;
  z-index: 100;
  top: 100%;
  left: 0;
  width: 100%;
  margin: 0;
  padding: 0;
}
#dropmenu li ul li {
  overflow: hidden;
  width: 100%;
  height: 0;
  color: #fff;
  transition: .2s;
}
#dropmenu li ul li a {
  padding: 13px 15px;
  background: #7c8c0e;
  text-align: left;
  font-size: 12px;
  font-weight: normal;
}

子、孫、ひ孫メニューに共通する設定です。例えば「#dropmenu li ul」は子メニューだけに適用されると思いがちですが、「#dropmenu li ul li ul」の孫メニュー、「#dropmenu li ul li ul li ul」のひ孫メニューにも適用されます。子メニューだけに適用させようと思うと「#dropmenu > li > ul」という風に間に子要素のみに適用させる「>」が必要になります。

で、それぞれ親要素を基準に絶対値で配置します。「top: 100%」と「left: 0;」は子メニュー用で、孫とひ孫は横に展開するので後で別に指定します。

liは元を「height: 0;」としているので表示されません。で、マウスを乗せたときに高さを持たせることで表示されるようになります。さらにtransitionを指定することで、子メニューが上から降りて現れます。

元が「display: none;」でマウスを乗せたときに「display: block;」でもいいと思います。そのときheightは必要ありません。

マウスを乗せたとき
#dropmenu > li:hover > a {  background: #7c8c0e }

マウスを乗せたときの親メニュー色の指定です。「a:hover」とすると子メニュー以下にマウスが移ったときに親メニューの色が元に戻ります。「li:hover a」とすることで、子メニュー以下にマウスがあるときも親メニューの色は変わったままになります。

#dropmenu > li:hover li:hover > a {  background: #6e7c0c }

先ほどと同じで、子メニューから孫メニューにマウスが移ったときの子メニューの色、孫メニューからひ孫メニューにマウスが移ったときの孫メニューの色が元の色に戻らないようにしています。

この辺は「>」が抜けると意図しないところに影響を与えることもあるので注意しましょう。

#dropmenu li:hover > ul > li {
  overflow: visible;
  height: 38px;
}

親メニューにマウスを乗せたときの子メニューの設定です。先ほど説明したように、元が「height:0」で表示されていなかったものを「height: 38px;」とすることで表示されるようにします。

「li:hover > ul > li」の「>」が抜けると孫メニューもひ孫メニューも一気に開いてしまいます。

孫、ひ孫メニュー
#dropmenu li ul li ul {
  top: 0;
  left: 100%;
}
#dropmenu li:last-child ul li ul {
  left: -100%;
  width: 100%;
}
#dropmenu li ul li ul li a {  background: #616d0b }
#dropmenu li:hover ul li ul li:hover > a {  background: #535d09 }

子以下のメニューのところの「#dropmenu li ul」で指定しましたが、これだと子メニュー用のように下にメニューが現れますので、孫、ひ孫メニュー用に横に現れるようにします。

高さは親メニュー(孫だと子)と同じにして、「left: 100%;」で右に隣り合うように配置されます。

で、一番右のメニュー5は孫メニューが右に現れるとディスプレイからはみ出ることも考えられるので左に配置されるようにします。

「:last-child」はIE8以下は非対応ですので、気になる場合はクラスを使うのがよいと思います。

下の階層があることを示す矢印

#dropmenu li ul li ul:before {
  position: absolute;
  content: "";
  top: 13px;
  left: -20px;
  width: 0;
  height: 0;
  border: 5px solid transparent;
  border-left-color: #454e08;
}
#dropmenu li:last-child ul li ul:before {
  position: absolute;
  content: "";
  top: 13px;
  left: 200%;
  margin-left: -20px;
  border: 5px solid transparent;
  border-right-color: #454e08;
}

下の階層にメニューがあることを示す矢印をつけます。多階層になる場合は、これがないとかなり使い勝手が悪いと思います。

度々紹介している擬似要素を使った三角形です。下の方は右端のメニュー用の矢印です。

ここのポイントは、子メニューに表示されている矢印は、孫メニューの外側にある矢印ということです。孫メニューがある場合の子メニューに矢印を付けるなんて指定ができればいいのですが、今のところそのようなセレクタはないと思います。

まとめ

多階層になるとセレクタも複雑になるので疲れました。結構長くなりましたが、最初は角丸やボーダーもあったのでもっと長くなっていました。

で、必要な部分だけ効率的に書こうと色々考えましたが、もっと効率的な方法もあるかもしれません。

今回のサンプルはひ孫までですが、追加すれば玄孫でも大丈夫なはずです。ここまで必要なサイトはなかなかないと思いますが。。

個人的にはあまり使うことがなさそうですが、今回作ってみていい頭の体操になりました。皆さんのお役に立てたならうれしいです。

フィードやTwitterで最新情報をチェック
follow us in feedly
この記事に付いているタグの最新記事一覧
loading...
コメント
多階層はややこしくなかなかうまくできなかったのですが、この記事で色々理解できました。解説も分かりやすかったのでとても助かります。ありがとうございます!
【2013/10/15 14:50】 | アット #- | [edit]
アットさん

コメントありがとうございます。
お役に立ててよかったです。
今後もよろしくお願いします!
【2013/10/15 20:43】 | 管理人 #E2ywrYdA | [edit]
はじめまして。
「menu css プルダウン」でググって一番上にありました。

非常に芸が細かくて、大変参考になりました。(というかそのまま利用できそうでした。)
ありがとうございます。


商売用に自作でホームページを作っております。そのページで利用(CCSSを売るのではなく、ページに取り込みたい)のですが、ライセンスの指定はありますでしょうか?(GNU/MIT/他)

しかし、趣味でココまで出来るのは凄いですね。リスペクトします。
【2014/01/16 10:39】 | れおにゃ #- | [edit]
れおにゃ さま

はじめまして!
コメントありがとうございます。
励みになります。

利用について特に取り決めはありません。

記事をそのまま転用されると困りますが、
仰られるような使い方でしたら構いませんので
ご自由にお使いください。

今後もよろしくお願いします。
【2014/01/17 05:37】 | 管理人 #E2ywrYdA | [edit]
管理人さま。

返信、ありがとうございます!
ベースとしてカスタムしつつ、使わせていただきます。


過去記事みつつ、またきます!ブログ応援します!
【2014/01/17 09:39】 | れおにゃ #- | [edit]
初めまして!cssは初心者ですが
なにかいいグローバルメニューはないものかと
検索しまくってここに辿り着きました。ありがたく使わさせて貰ってます。
グローバルメニューを色々なサイトで見てきましたが
一番シンプルで見やすいメニューだと思いました。

そこでこのグローバルナビを設置し、また改変してサイドナビにしているのですが
左カラム内に収まってしまいます。あと1行、2行追加したらできると思うのですが
この先に進めません。ヘッダに設置したら全部表示されます。
左カラムからメインをまたいで表示させる事ができません。

・左カラムからメインにメニューをまたいで表示はなんと記述したらよいでしょうか?
・「孫、ひ孫、メニューだけ」のwidthを変更したいのですがなんと記述したらよいでしょうか?

参考 自サイトです↓
ttp://t-hayato.net/

差し支えない範囲でご教授頂けたらと思います。


【2014/01/18 13:37】 | 名無しさん@ニュース2ちゃん #0EjQUAK2 | [edit]
↑を止めて↓のメガメニューを試しているのですが
ttp://weboook.blog22.fc2.com/blog-entry-380.html

HTMLとCSSをサンプルのまま貼り付けたのですが
メニューやサブメニューが縦に全部表示されてしまい、横にも飛び出しません。
シーサーブログですがこれはなぜなのでしょうか。

【2014/01/18 18:23】 | 8810 #5gd5.Dy6 | [edit]
名無しさん@ニュース2ちゃん 、8810さん

同じ方ということでよろしいのでしょうか?
お返事遅くなり申し訳ございません。

先ほどブログを拝見させていただきましたが、
うまく表示されているようでよかったです。

今後もよろしくお願いします。
【2014/01/26 08:17】 | 管理人 #E2ywrYdA | [edit]
お世話になっています。
最後のメニュー(子 or 孫)にマウスオーバーした際に、コメントのポップアップを表示させたいのですが、他サイトのCSSでポップアップ表示を参考にやってみるのですがうまく機能しません。。。
ご伝授願います。
【2014/07/03 15:45】 | - #- | [edit]
こういった多階層のメニューを上手く作れず困っていたので、とてもありがたく使用させていただいております。
ありがとうございます!

ただ1点、どうにかしたいのですがうまく行かない部分があります。
リストに入っているテキストの文字量が増えた時に、テキスト量に合わせてリスト幅を広げることは可能でしょうか?

何卒、よろしくお願いいたします。
【2014/09/08 17:58】 | 迷える子 #euxXsA62 | [edit]
迷える子 さん

コメントありがとうございます。
この記事がお役に立ててよかったです。

ご質問の件ですが、
答えになっていないかもしれませんが、
以下の記事のように子メニューの大きさを変えるのが
一番手っ取り早いと思います。

http://weboook.blog22.fc2.com/blog-entry-359.html

text-overflowで省略するのも1つの手かもしれません。

子メニューは親メニューの中にあるものですので
親の幅を超えて自由に変えるのはCSSだけだと難しいと思います。

それでは今後もよろしくお願いします。
【2014/09/09 03:40】 | 管理人 #E2ywrYdA | [edit]
子メニューが15個くらいあり、孫メニュー以下からが全て下へと展開されると画面をスクロールしなければならなくなるので、子メニューの半分くらいからは上へ展開されるようにしたいのですが・・・
色々試してはいるのですが、うまくいきません。
お知恵を貸して下さい。
よろしくお願いします。
【2014/11/15 14:38】 | まきまき #F8l2eWZA | [edit]
まきまき さん

孫メニューを上へ展開ということですが、
孫メニューの高さに限らず上へ展開というのはCSSだけだと難しいと思います。

例えば、5つあるひ孫メニューを上へ展開させるには
ひ孫メニューにクラス名「five」を与えた上で
以下のスタイルを書くと対応すると思います。

#dropmenu li ul li ul.five { top: -500%; }

それぞれ割り振るのも面倒ですので
ひ孫をメガメニュー風にするのも1つの手段かもしれません。

うまくいくことを願っています。
今後もよろしくお願いします。
【2014/11/16 06:49】 | 管理人 #E2ywrYdA | [edit]









※コメントはご意見ご感想や間違いのご指摘等にしていただけますようお願いいたします。コメントを確認する時間がなく、技術的なご質問をいただいても答えできません。申し訳ございませんがご理解のほどお願いいたします。

Recent Entry
Popular Entry
  • このエントリーをはてなブックマークに追加