Suckerfish 下拉菜单的衍生
作者:Patrick Griffiths 和 Dan Webb。
最初在 A List Apart 上发布的 Suckerfish Dropdowns 文章,证明了实现轻量级、可访问的 CSS 下拉菜单的一种流行方法,通过模拟 :hover
伪类来兼容 Internet Explorer。
现在它们回来了,而且它们更具可访问性,更轻量级(只有 12 行 JavaScript),具有更强的兼容性(现在无需任何 hack 即可在 Opera 和 Safari 中运行),并且可以实现多级。
单级下拉菜单
好了,我们直奔主题。我们处理的初始 HTML 大致如下:
<ul id="nav">
<li><a href="#">Percoidei</a>
<ul>
<li><a href="#">Remoras</a></li>
<li><a href="#">Tilefishes</a></li>
<li><a href="#">Bluefishes</a></li>
<li><a href="#">Tigerfishes</a></li>
</ul>
</li>
<li><a href="#">Anabantoidei</a>
<ul>
<li><a href="#">Climbing perches</a></li>
<li><a href="#">Labyrinthfishes</a></li>
<li><a href="#">Kissing gouramis</a></li>
<li><a href="#">Pike-heads</a></li>
<li><a href="#">Giant gouramis</a></li>
</ul>
</li>
<!-- etc. -->
</ul>
一个良好、健康的结构化无序列表。
要进行设置,我们需要一些基本的样式
#nav, #nav ul {
padding: 0;
margin: 0;
list-style: none;
}
#nav a {
display: block;
width: 10em;
}
#nav li {
float: left;
width: 10em;
}
请注意,您需要在 #nav li
选择器中指定宽度,否则 Opera 会出问题。同时请记住,由于我们正在使用浮动,因此下拉菜单下的内容也需要清除(clear: left
)。
我们显然需要隐藏我们想要“下拉”的列表,但为了尽可能地提高可访问性,我们需要避免使用 display: none
。正如在替换图像的写文中经常提到的那样,这会隐藏某些屏幕阅读器无法访问的元素。您可能会认为有多种方法可以解决这个问题,但在对宽度、高度、边距、顶部和剪辑在大量浏览器中进行详尽实验后,最好的解决方案(顺便说一句,它也兼容多级列表)在于操纵 left
属性。
CSS 规范指出,top
、right
、bottom
和 left
值应该使绝对定位的框与其包含块偏移。但不幸的是,Opera 决定相对于页面偏移绝对定位的框,这就是为什么最初的 Suckerfish Dropdowns 在 Opera 上不起作用的原因——因为它们依赖于带有显式长度的 top
和 left
属性。
所以,我们不使用 display: none
,而是使用 left: -999em
将下拉列表推到视图之外,然后使用 left: auto
(而不是 left: 0
)将其带回。
#nav li ul {
position: absolute;
width: 10em;
left: -999em;
}
#nav li:hover ul {
left: auto;
}
这样就可以解决所有完全支持 :hover
伪类的浏览器的问题,但对于 Internet Explorer,我们需要启动 Suckerfish JavaScript。
sfHover = function() {
var sfEls = document.getElementById("nav").getElementsByTagName("LI");
for (var i=0; i<sfEls.length; i++) {
sfEls[i].onmouseover=function() {
this.className+=" sfhover";
}
sfEls[i].onmouseout=function() {
this.className=this.className.replace(new RegExp(" sfhover\\b"), "");
}
}
}
if (window.attachEvent) window.attachEvent("onload", sfHover);
基本上,当“nav”id
的 ul
元素中的 li
元素被“鼠标悬停”时,此代码会将“sfhover”类应用于它们,并在“鼠标移出”时使用正则表达式将其移除。
既然 Suckerfish 正在生成新的类,下一步就是简单地复制 :hover
选择器并添加“sfhover”类选择器。
#nav li:hover ul, #nav li.sfhover ul {
left: auto;
}
好了,您就得到了一个标准的 单级下拉菜单。
多级下拉菜单
最初的 Suckerfish Dropdowns 文章只涵盖了单级下拉菜单,但通过对级联逻辑稍作扩展,使用 CSS 也可以创建多级下拉菜单。而且,与原始的 Suckerfish JavaScript 代码不同,'sfHover' 函数现在将行为应用于“nav”的所有后代 li
元素,而不仅仅是直接子元素,因此现在 Internet Explorer 中也可以使用多级下拉菜单。
因此,为了开始,假设我们处理的列表结构有更多层级,如下所示:
<ul id="nav">
<li><a href="#">Percoidei</a>
<ul>
<li><a href="#">Remoras</a>
<ul>
<li><a href="#">Echeneis</a></li>
<li><a href="#">Phtheirichthys</a></li>
<li><a href="#">Remora</a></li>
<li><a href="#">Remorina</a></li>
<li><a href="#">Rhombochirus</a></li>
</ul>
</li>
<li><a href="#">Tilefishes</a></li>
<li><a href="#">Bluefishes</a></li>
<li><a href="#">Tigerfishes</a></li>
</ul>
</li>
<li><a href="#">Anabantoidei</a>
<!-- etc. -->
</li>
<!-- etc. -->
</ul>
我们需要在单级方法中添加一些东西。首先,第三级列表(在本例中为“Echeneis”、“Phtheirichthys”等)需要下拉到相应列表项(在此例中为“Remoras”)的侧面,因此我们需要添加此规则,它将适用于第一个下拉菜单之后的所有下拉菜单。
#nav li ul ul {
margin: -1em 0 0 10em;
}
因为我们无法显式指定绝对定位框的顶部,所以它们会出现在悬停列表项的下方,这就是为什么下一级列表的顶部边距需要设置为 -1em
。但这还不足以将菜单拉到与相应列表项对齐的位置,因为默认情况下行高大于 1em(通常为 1.2em),所以我们需要在初始 ul
规则集中添加一些内容。
#nav, #nav ul {
padding: 0;
margin: 0;
list-style: none;
line-height: 1;
}
由于级联效应,当显示第二级列表时,第三级列表也会显示出来,因此我们还需要显式隐藏第三级列表(请记住,我们需要复制 :hover
伪类和 .sfhover
类)。
#nav li:hover ul ul, #nav li.sfhover ul ul {
left: -999em;
}
现在,此规则可以被覆盖,以便当相应列表项被悬停时显示它,方法是扩展下拉菜单的显示(在单级下拉菜单中是 #nav li:hover ul, #nav li.sfhover ul { left: auto; }
)。
#nav li:hover ul, #nav li li:hover ul, #nav li.sfhover ul, #nav li li.sfhover ul {
left: auto;
}
这将建立一个稳固的 两级下拉菜单。
遵循相同的逻辑,您可以根据需要支持任意多级下拉菜单。
对于 三级下拉菜单。
#nav li:hover ul ul, #nav li:hover ul ul ul, #nav li.sfhover ul ul, #nav li.sfhover ul ul ul {
left: -999em;
}
#nav li:hover ul, #nav li li:hover ul, #nav li li li:hover ul, #nav li.sfhover ul, #nav li li.sfhover ul, #nav li li li.sfhover ul {
left: auto;
}
如果您疯狂地需要四级菜单。
#nav li:hover ul ul, #nav li:hover ul ul ul, #nav li:hover ul ul ul ul, #nav li.sfhover ul ul, #nav li.sfhover ul ul ul, #nav li.sfhover ul ul ul ul {
left: -999em;
}
#nav li:hover ul, #nav li li:hover ul, #nav li li li:hover ul, #nav li li li li:hover ul, #nav li.sfhover ul, #nav li li.sfhover ul, #nav li li li.sfhover ul, #nav li li li li.sfhover ul {
left: auto;
}
示例
因此,您可能已经查看了 一级、二级 和 三级 的基本示例,这些可能是查看未被干扰的源代码的最好地方,但当然,您可以 让事情看起来更漂亮。您甚至可以将它变成一个 垂直菜单 而不是水平菜单。