HTML Dog

跳至导航

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 规范指出toprightbottomleft 值应该使绝对定位的框与其包含块偏移。但不幸的是,Opera 决定相对于页面偏移绝对定位的框,这就是为什么最初的 Suckerfish Dropdowns 在 Opera 上不起作用的原因——因为它们依赖于带有显式长度的 topleft 属性。

所以,我们不使用 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”idul 元素中的 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;
}

示例

因此,您可能已经查看了 一级二级三级 的基本示例,这些可能是查看未被干扰的源代码的最好地方,但当然,您可以 让事情看起来更漂亮。您甚至可以将它变成一个 垂直菜单 而不是水平菜单。