纯CSS多级菜单

这是一个相当炫的功能,让网页看起来像桌面程序,如 window 的开始菜单。实现原理基本和纯 CSS 相册差不多,但要注意的事项比较多,让我们一步步来吧。

先来一个非常简单的一级菜单与悬停效果。

<ul id="menu">
  <li>
    <a href="http://www.cnblogs.com/rubylouvre/">
      菜单一<!--小图--><span><!--大图--></span>
    </a>
  </li>
  <li>
    <a href="http://www.cnblogs.com/rubylouvre/">
      菜单二<!--小图--><span><!--大图--></span>
    </a>
  </li>
  <li>
    <a href="http://www.cnblogs.com/rubylouvre/">
      菜单三<!--小图--><span><!--大图--></span>
    </a>
  </li>
  <li class="last">
    <a href="http://www.cnblogs.com/rubylouvre/">
      菜单四<!--小图--><span><!--大图--></span>
    </a>
  </li>
</ul>

结构很熟悉吧,就是把原来放图片的地方换成文字而已。我还特意标出来了。接着下来的表现层代码非常简单。

* {
  margin:0;
  padding:0;
}
.menu {
  font-size:12px;
}
.menu li {/*水平菜单*/
  float:left;
  list-style:none;
}
.menu a {
  display:block;
  position:relative;
  height:32px;
  width:100px;
  line-height:32px;
  background:#a9ea00;
  color:#ff8040;
  text-decoration:none;
  text-align:center;
}
.menu a:hover {
  background:#369;
  color:#fff;
}
.menu li span {
  display:none;
  position:absolute;
  left:0;
  top:32px;
  width:200px;
  height:150px;
  background:#B9D6FF;
}
.menu a:hover span {
  display:block;
}
<!doctype html> <title> 纯 CSS 相册 by 司徒正美 </title> <meta charset="utf-8"/> <meta name="keywords" content="纯 CSS 多级菜单 by 司徒正美" /> <meta name="description" content="纯 CSS 多级菜单 by 司徒正美" /> </head> <body> <style type="text/css"> * { margin:0; padding:0; } .menu {font-size:12px;} .menu li {/* 水平菜单 */ float:left; list-style:none; } .menu a { display:block; position:relative; height:32px; width:100px; line-height:32px; background:#a9ea00; color:#ff8040; text-decoration:none; text-align:center; } .menu a:hover { background:#369; color:#fff; } .menu li span { display:none; position:absolute; left:0; top:32px; width:200px; height:150px; background:#B9D6FF; } .menu a:hover span {display:block;} <p></style></p> <p><h1> 纯 CSS 多级菜单 by 司徒正美 </h1><br /> <div class="menu"><br /> <ul><br /> <li><br /> <a href="<a href="http://www.cnblogs.com/rubylouvre/">http://www.cnblogs.com/rubylouvre/</a>"><br /> 菜单一 <!-- 小图 --><span><!-- 大图 --></span><br /> </a><br /> </li><br /> <li><br /> <a href="<a href="http://www.cnblogs.com/rubylouvre/">http://www.cnblogs.com/rubylouvre/</a>"><br /> 菜单二 <!-- 小图 --><span><!-- 大图 --></span><br /> </a><br /> </li><br /> <li><br /> <a href="<a href="http://www.cnblogs.com/rubylouvre/">http://www.cnblogs.com/rubylouvre/</a>"><br /> 菜单三 <!-- 小图 --><span><!-- 大图 --></span><br /> </a><br /> </li><br /> <li class="last"><br /> <a href="<a href="http://www.cnblogs.com/rubylouvre/">http://www.cnblogs.com/rubylouvre/</a>"><br /> 菜单四 <!-- 小图 --><span><!-- 大图 --></span><br /> </a><br /> </li><br /> </ul><br /> </div><br /> </body><br /> </html><br />

运行代码

这里有两个值得注意的地方。我们先说第一个。子菜单 (span 元素) 的 top 应该能使其顶部停留在 a 元素的范围内,如果包含块是 li 元素,同理。当 span 的 top 值大于 32px,如 40px,我们就无法把鼠标移到 span 元素上。因为离开了 a:hover 的作用范围,span 元素又重新隐藏。

.menu li span {
  display:none;
  position:absolute;
  left:0;
  top:40px;/*★★修改这里★★*/
  width:200px;
  height:150px;
  background:#B9D6FF;
}
<!doctype html> <title> 纯 CSS 相册 by 司徒正美 </title> <meta charset="utf-8"/> <meta name="keywords" content="纯 CSS 多级菜单 by 司徒正美" /> <meta name="description" content="纯 CSS 多级菜单 by 司徒正美" /> </head> <body> <style type="text/css"> * { margin:0; padding:0; } .menu {font-size:12px;} .menu li {/* 水平菜单 */ float:left; list-style:none; } .menu a { display:block; position:relative; height:32px; width:100px; line-height:32px; background:#a9ea00; color:#ff8040; text-decoration:none; text-align:center; } .menu a:hover { background:#369; color:#fff; } .menu ul span { display:none; position:absolute; left:0; top:40px;/*★★修改这里★★*/ width:200px; height:150px; background:#B9D6FF; } .menu a:hover span {display:block;} <p></style><br /> <h1> 纯 CSS 多级菜单 by 司徒正美 </h1><br /> <div class="menu"><br /> <ul><br /> <li><br /> <a href="<a href="http://www.cnblogs.com/rubylouvre/">http://www.cnblogs.com/rubylouvre/</a>"><br /> 菜单一 <!-- 小图 --><span><!-- 大图 --></span><br /> </a><br /> </li><br /> <li><br /> <a href="<a href="http://www.cnblogs.com/rubylouvre/">http://www.cnblogs.com/rubylouvre/</a>"><br /> 菜单二 <!-- 小图 --><span><!-- 大图 --></span><br /> </a><br /> </li><br /> <li><br /> <a href="<a href="http://www.cnblogs.com/rubylouvre/">http://www.cnblogs.com/rubylouvre/</a>"><br /> 菜单三 <!-- 小图 --><span><!-- 大图 --></span><br /> </a><br /> </li><br /> <li class="last"><br /> <a href="<a href="http://www.cnblogs.com/rubylouvre/">http://www.cnblogs.com/rubylouvre/</a>"><br /> 菜单四 <!-- 小图 --><span><!-- 大图 --></span><br /> </a><br /> </li><br /> </ul><br /> </div><br /> </body><br /> </html><br />

运行代码

第二个问题是 IE6 特有的,就是子菜单在对应的包含块 mouseout 后仍不消失的问题。hover 伪类相当于 moverover 与 moverout。我们在可以在 mouseover 给它的子孙元素认定一种样式,mouseout 时认定另一种。换言之,display 现在在 IE6 无法切换(img 元素除外)。解决方法用 visibility 代替 display。

<!doctype html> <title> 纯 CSS 相册 by 司徒正美 </title> <meta charset="utf-8"/> <meta name="keywords" content="纯 CSS 多级菜单 by 司徒正美" /> <meta name="description" content="纯 CSS 多级菜单 by 司徒正美" /> </head> <body> <style type="text/css"> * { margin:0; padding:0; } .menu {font-size:12px;} .menu li {/* 水平菜单 */ float:left; list-style:none; } .menu a { display:block; position:relative; height:32px; width:100px; line-height:32px; background:#a9ea00; color:#ff8040; text-decoration:none; text-align:center; } .menu a:hover { background:#369; color:#fff; } .menu ul span { visibility:hidden; position:absolute; left:0; top:32px; width:200px; height:150px; background:#B9D6FF; } .menu a:hover span {visibility:visible;} <p></style><br /> <h1> 纯 CSS 多级菜单 by 司徒正美 </h1><br /> <div class="menu"><br /> <ul><br /> <li><br /> <a href="<a href="http://www.cnblogs.com/rubylouvre/">http://www.cnblogs.com/rubylouvre/</a>"><br /> 菜单一 <!-- 小图 --><span><!-- 大图 --></span><br /> </a><br /> </li><br /> <li><br /> <a href="<a href="http://www.cnblogs.com/rubylouvre/">http://www.cnblogs.com/rubylouvre/</a>"><br /> 菜单二 <!-- 小图 --><span><!-- 大图 --></span><br /> </a><br /> </li><br /> <li><br /> <a href="<a href="http://www.cnblogs.com/rubylouvre/">http://www.cnblogs.com/rubylouvre/</a>"><br /> 菜单三 <!-- 小图 --><span><!-- 大图 --></span><br /> </a><br /> </li><br /> <li class="last"><br /> <a href="<a href="http://www.cnblogs.com/rubylouvre/">http://www.cnblogs.com/rubylouvre/</a>"><br /> 菜单四 <!-- 小图 --><span><!-- 大图 --></span><br /> </a><br /> </li><br /> </ul><br /> </div><br /> </body><br /> </html><br />

运行代码

好了,现在我们真正做二级菜单,把有关 span 的 CSS 全删掉,并在结构层原 span 的位置改为如下代码:

<ul>
  <li><a href="http://www.cnblogs.com/rubylouvre/">二级菜单_11</a></li>
  <li><a href="http://www.cnblogs.com/rubylouvre/">二级菜单_12</a></li>
</ul>
<!doctype html> <title> 纯 CSS 相册 by 司徒正美 </title> <meta charset="utf-8"/> <meta name="keywords" content="纯 CSS 多级菜单 by 司徒正美" /> <meta name="description" content="纯 CSS 多级菜单 by 司徒正美" /> </head> <body> <style type="text/css"> * { margin:0; padding:0; } .menu {font-size:12px;} .menu li {/* 水平菜单 */ float:left; list-style:none; } .menu a { display:block; position:relative; height:32px; width:100px; line-height:32px; background:#a9ea00; color:#ff8040; text-decoration:none; text-align:center; } .menu a:hover { background:#369; color:#fff; } </style> <h1> 纯 CSS 多级菜单 by 司徒正美 </h1> <div class="menu"> <ul> <li> <a href="http://www.cnblogs.com/rubylouvre/"> 菜单一 <ul> <li><a href="http://www.cnblogs.com/rubylouvre/"> 二级菜单 _11</a></li> <li><a href="http://www.cnblogs.com/rubylouvre/"> 二级菜单 _12</a></li> </ul> </a> </li> <li> <a href="http://www.cnblogs.com/rubylouvre/"> 菜单二 <ul> <li><a href="http://www.cnblogs.com/rubylouvre/"> 二级菜单 _21</a></li> <li><a href="http://www.cnblogs.com/rubylouvre/"> 二级菜单 _22</a></li> </ul> </a> </li> <li> <a href="http://www.cnblogs.com/rubylouvre/"> 菜单三 <ul> <li><a href="http://www.cnblogs.com/rubylouvre/"> 二级菜单 _31</a></li> <li><a href="http://www.cnblogs.com/rubylouvre/"> 二级菜单 _32</a></li> </ul> </a> </li> <li class="last"> <a href="http://www.cnblogs.com/rubylouvre/"> 菜单四 <ul> <li><a href="http://www.cnblogs.com/rubylouvre/"> 二级菜单 _41</a></li> <li><a href="http://www.cnblogs.com/rubylouvre/"> 二级菜单 _42</a></li> </ul> </a> </li> </ul> </div> </body> </html>

运行代码

我们在各游览器看一下,感觉非常乏力。IE6 与 Opera10 的二级菜单项是垂直的,但我们并没有清除浮动啊?firefox3.5 与 chrome 与 safari4 的二级菜单项是水平分布了,但上面好像多出一个菜单项……IE8 同学这次反而是表现得最好的。我没有装 IE7,所以一向忽略它。

我们重新设置一下样式,如把包含块改设在 li 元素上,让二级菜单项呈垂直显示。

* {
  margin:0;
  padding:0;
}
.menu {
  font-size:12px;
}
.menu li {/*水平菜单*/
  float:left;
  list-style:none;
  position:relative;/*把包含块移动li元素*/
}
.menu a {
  display:block;
  /*position:relative;发现放在a元素中,
  在标准游览器中惨不忍睹,
  和纯CSS相册3的第一个运行框在chrome中遇到的bug一样*/
  height:32px;
  width:100px;
  line-height:32px;
  background:#a9ea00;
  color:#ff8040;
  text-decoration:none;
  text-align:center;
}
.menu a:hover {
  background:#369;
  color:#fff;
}
/*新增的二级菜单部分*/
.menu ul ul {
  visibility:hidden;/*开始时是隐藏的*/
  position:absolute;
  left:0px;
  top:32px;
}
.menu ul a:hover ul{
  visibility:visible;
}
.menu ul ul li {
  clear:both;/*垂直显示*/
  text-align:left;
}
<!doctype html> <title> 纯 CSS 相册 by 司徒正美 </title> <meta charset="utf-8"/> <meta name="keywords" content="纯 CSS 多级菜单 by 司徒正美" /> <meta name="description" content="纯 CSS 多级菜单 by 司徒正美" /> </head> <body> <style type="text/css"> * { margin:0; padding:0; } .menu {font-size:12px;} .menu li {/* 水平菜单 */ float:left; list-style:none; position:relative;/* 把包含块移动 li 元素 */ } .menu a { display:block; /*position:relative; 发现放在 a 元素中, 在标准游览器中惨不忍睹, 和纯 CSS 相册 3 的第一个运行框在 chrome 中遇到的 bug 一样 */ height:32px; width:100px; line-height:32px; background:#a9ea00; color:#ff8040; text-decoration:none; text-align:center; } .menu a:hover { background:#369; color:#fff; } /* 新增的二级菜单部分 */ .menu ul ul { visibility:hidden;/* 开始时是隐藏的 */ position:absolute; left:0px; top:32px; } .menu ul a:hover ul{visibility:visible;} .menu ul ul li { clear:both;/* 垂直显示 */ text-align:left; } </style> <h1> 纯 CSS 多级菜单 by 司徒正美 </h1> <div class="menu"> <ul> <li> <a href="http://www.cnblogs.com/rubylouvre/"> 菜单一 <ul> <li><a href="http://www.cnblogs.com/rubylouvre/"> 二级菜单 _11</a></li> <li><a href="http://www.cnblogs.com/rubylouvre/"> 二级菜单 _12</a></li> </ul> </a> </li> <li> <a href="http://www.cnblogs.com/rubylouvre/"> 菜单二 <ul> <li><a href="http://www.cnblogs.com/rubylouvre/"> 二级菜单 _21</a></li> <li><a href="http://www.cnblogs.com/rubylouvre/"> 二级菜单 _22</a></li> </ul> </a> </li> <li> <a href="http://www.cnblogs.com/rubylouvre/"> 菜单三 <ul> <li><a href="http://www.cnblogs.com/rubylouvre/"> 二级菜单 _31</a></li> <li><a href="http://www.cnblogs.com/rubylouvre/"> 二级菜单 _32</a></li> </ul> </a> </li> <li class="last"> <a href="http://www.cnblogs.com/rubylouvre/"> 菜单四 <ul> <li><a href="http://www.cnblogs.com/rubylouvre/"> 二级菜单 _41</a></li> <li><a href="http://www.cnblogs.com/rubylouvre/"> 二级菜单 _42</a></li> </ul> </a> </li> </ul> </div> </body> </html>

运行代码

发现二级菜单在 firefox 与 safari 与 chrome 中没有反应,弹不出来(这三个浏览器的 CSS 部分互相抄袭严重啊)。opera10 表现得最好,IE8 其次。不过,标准浏览器的所有元素都支持 hover 伪类,不像 IE6,非带 href 的 a 元素不可。我们改写部分 CSS 代码:

.menu ul li:hover ul,/*非IE6*/
.menu ul a:hover ul{/*IE6*/
  visibility:visible;
}
<!doctype html> <title> 纯 CSS 相册 by 司徒正美 </title> <meta charset="utf-8"/> <meta name="keywords" content="纯 CSS 多级菜单 by 司徒正美" /> <meta name="description" content="纯 CSS 多级菜单 by 司徒正美" /> </head> <body> <style type="text/css"> * { margin:0; padding:0; } .menu {font-size:12px;} .menu li {/* 水平菜单 */ float:left; list-style:none; position:relative;/* 把包含块移动 li 元素 */ } .menu a { display:block; /*position:relative; 发现放在 a 元素中, 在标准游览器中惨不忍睹, 和纯 CSS 相册 3 的第一个运行框在 chrome 中遇到的 bug 一样 */ height:32px; width:100px; line-height:32px; background:#a9ea00; color:#ff8040; text-decoration:none; text-align:center; } .menu a:hover { background:#369; color:#fff; } /* 新增的二级菜单部分 */ .menu ul ul { visibility:hidden;/* 开始时是隐藏的 */ position:absolute; left:0px; top:32px; } .menu ul li:hover ul,/* 非 IE6*/ .menu ul a:hover ul{/*IE6*/ visibility:visible; } .menu ul ul li { clear:both;/* 垂直显示 */ text-align:left; } </style> <h1> 纯 CSS 多级菜单 by 司徒正美 </h1> <div class="menu"> <ul> <li> <a href="http://www.cnblogs.com/rubylouvre/"> 菜单一 <ul> <li><a href="http://www.cnblogs.com/rubylouvre/"> 二级菜单 _11</a></li> <li><a href="http://www.cnblogs.com/rubylouvre/"> 二级菜单 _12</a></li> </ul> </a> </li> <li> <a href="http://www.cnblogs.com/rubylouvre/"> 菜单二 <ul> <li><a href="http://www.cnblogs.com/rubylouvre/"> 二级菜单 _21</a></li> <li><a href="http://www.cnblogs.com/rubylouvre/"> 二级菜单 _22</a></li> </ul> </a> </li> <li> <a href="http://www.cnblogs.com/rubylouvre/"> 菜单三 <ul> <li><a href="http://www.cnblogs.com/rubylouvre/"> 二级菜单 _31</a></li> <li><a href="http://www.cnblogs.com/rubylouvre/"> 二级菜单 _32</a></li> </ul> </a> </li> <li class="last"> <a href="http://www.cnblogs.com/rubylouvre/"> 菜单四 <ul> <li><a href="http://www.cnblogs.com/rubylouvre/"> 二级菜单 _41</a></li> <li><a href="http://www.cnblogs.com/rubylouvre/"> 二级菜单 _42</a></li> </ul> </a> </li> </ul> </div> </body> </html>

运行代码

二级菜单能弹出来了,但那个神秘的 li 元素也现形了。我用双重浮动的收缩包围 (shrink-wrapping) 现象都无法消除这个神秘的 li 元素。参考外国的代码,方法是,把整个子菜单放到 a 元素的外面,然后用 li:hover 控制样式的切换。于是结构层改写如下:

<div class="menu">
  <ul>
    <li>
      <a href="http://www.cnblogs.com/rubylouvre/">菜单一 </a>
      <ul>
        <li><a href="http://www.cnblogs.com/rubylouvre/">二级菜单_11</a></li>
        <li><a href="http://www.cnblogs.com/rubylouvre/">二级菜单_12</a></li>
      </ul>
    </li>
    <li>
      <a href="http://www.cnblogs.com/rubylouvre/">菜单二</a>
      <ul>
        <li><a href="http://www.cnblogs.com/rubylouvre/">二级菜单_21</a></li>
        <li><a href="http://www.cnblogs.com/rubylouvre/">二级菜单_22</a></li>
      </ul>
    </li>
    <li>
    //***************略************
    </li>
    <li>
    //***************略************
    </li>
  </ul>
</div>
<!doctype html> <title> 纯 CSS 相册 by 司徒正美 </title> <meta charset="utf-8"/> <meta name="keywords" content="纯 CSS 多级菜单 by 司徒正美" /> <meta name="description" content="纯 CSS 多级菜单 by 司徒正美" /> </head> <body> <style type="text/css"> * { margin:0; padding:0; } .menu {font-size:12px;} .menu li {/* 水平菜单 */ float:left; list-style:none; position:relative;/* 把包含块移动 li 元素 */ } .menu a { display:block; /*position:relative; 发现放在 a 元素中, 在标准游览器中惨不忍睹, 和纯 CSS 相册 3 的第一个运行框在 chrome 中遇到的 bug 一样 */ height:32px; width:100px; line-height:32px; background:#a9ea00; color:#ff8040; text-decoration:none; text-align:center; } .menu a:hover { background:#369; color:#fff; } /* 新增的二级菜单部分 */ .menu ul ul { visibility:hidden;/* 开始时是隐藏的 */ position:absolute; left:0px; top:32px; } .menu ul li:hover ul,/* 非 IE6*/ .menu ul a:hover ul{/*IE6*/ visibility:visible; } .menu ul ul li { clear:both;/* 垂直显示 */ text-align:left; } </style> <h1> 纯 CSS 多级菜单 by 司徒正美 </h1> <div class="menu"> <ul> <li> <a href="http://www.cnblogs.com/rubylouvre/"> 菜单一 </a> <ul> <li><a href="http://www.cnblogs.com/rubylouvre/"> 二级菜单 _11</a></li> <li><a href="http://www.cnblogs.com/rubylouvre/"> 二级菜单 _12</a></li> </ul> </li> <li> <a href="http://www.cnblogs.com/rubylouvre/"> 菜单二 </a> <ul> <li><a href="http://www.cnblogs.com/rubylouvre/"> 二级菜单 _21</a></li> <li><a href="http://www.cnblogs.com/rubylouvre/"> 二级菜单 _22</a></li> </ul> </li> <li> <a href="http://www.cnblogs.com/rubylouvre/"> 菜单三 </a> <ul> <li><a href="http://www.cnblogs.com/rubylouvre/"> 二级菜单 _31</a></li> <li><a href="http://www.cnblogs.com/rubylouvre/"> 二级菜单 _32</a></li> </ul> </li> <li> <a href="http://www.cnblogs.com/rubylouvre/"> 菜单四 </a> <ul> <li><a href="http://www.cnblogs.com/rubylouvre/"> 二级菜单 _41</a></li> <li><a href="http://www.cnblogs.com/rubylouvre/"> 二级菜单 _42</a></li> </ul> </li> </ul> </div> </body> </html>

运行代码

好了,搞定非 IE6 的所有游览器了,下一部分我们来处理 IE6 这个固执的孩子。

如果您觉得此文有帮助,可以打赏点钱给我支付宝 1669866773@qq.com ,或扫描二维码