只需 CSS 的下拉式导航菜单
我从事前端开发13年有余,一直致力于研究现代CSS解决方案,这是我该系列文章的第七篇
我们将探讨:
动画与CSS
transition
和transform
使用
:focus-within
伪类用于定位的
CSS grid
动态居中技术
下拉菜单的可访问性注意事项
如果你曾经为 "鼠标悬停"的概念而烦恼,那么这个升级版就是为你准备的!
在我们讲太深之前还是要提醒你,虽然我们的技术100%只使用了CSS,但有些用例可能会因为使用了一些vanilla-js
而受益,尤其是为移动用户创造更好的体验。还有一个关键的功能需要一个polyfill来实现这个功能- :focus-within
-以获得最可靠的支持。但相比于以往需要一个或多个jQuery插件来完成的日子,我们还是有了很大的进步。那么,让我们开始吧!
如果你没有使用过Sass,你可能需要花5分钟的时间来了解Sass的嵌套语法,这样才能最容易理解给出的代码示例。
基础导航HTML
我们会继续加强,但这里是我们的起点结构。
这是导航链接的语义标准。这种结构是灵活的,可以在你的页面上的任何地方使用,所以它可以像主导航一样轻松地成为侧边栏的目录。
一开始,我们就实现了两个专门针对可访问性的功能:
1.aria-label
在<nav>
上,当辅助技术被用来通过地标导航页面时,帮助识别它的目的。
2.ria-labelledby
在.dropdown__menu
上,链接到.dropdown__title
的id,让辅助技术读取类似于 "link, Donuts list Sweets 4 items level 2 "的内容,其中 "Sweets"是下拉标题的值
我们(大部分)默认的初始样式下:
最初的导航样式
首先,我们将给nav
赋予一些容器样式,并将其定义为grid
容器。然后,我们将删除nav ul
和nav ul li
中的默认列表样式。
我们已经失去了层次性的定义,但我们可以从以下几点开始把它找回来。
通过使用子组合器选择器>
,我们定义了顶层的ul
,它是nav
的直接子元素,应该把它的grid-auto-flow
切换到column
,这样可以有效地把它更新为沿x-axis
。然后我们给顶层的li
元素添加margin
,让它的定义更多一些。现在,未来的下拉项出现在 "Sweets"菜单的下方,并且更清楚地显示了它的子项。
接下来,我们先给所有的链接以及.dropdown__title
加点样式,然后除了.dropdown__title
外,只给顶层链接加点样式:
基础下拉样式
到目前为止,我们一直在依赖元素选择器,但我们将为下拉菜单引入类选择器,因为在一个给定的导航列表中可能会有多个选择器。
我们首先要对.dropdown__menu
和它的链接进行样式设置,通过定位和动画来帮助更清晰地识别它。
其中一个明显的问题是.dropdown__menu
影响了导航容器,从下拉菜单周围的灰色导航背景可以看出。
我们可以通过在.dropdown__menu
中添加position: absolute
来解决这个问题,这样就可以把它从正常的文档流程中剥离出来:
你可以看到它在父列表项的左侧和下方对齐。这可能是你设想中想要的位置。
我们要带出一个居中的技巧,将菜单中心对准列表项:
这种居中技术的神奇之处在于,菜单可以是任意宽度,甚至是动态宽度,而且可以适当居中。
下拉式展示样式
有两个主要的触发器我们希望用来打开菜单: :hover
和 :focus
。
但是,传统的:focus
不会保持下拉菜单的打开状态。一旦初始触发器失去了焦点,键盘焦点仍然可以在下拉菜单中移动,但从视觉上看菜单将消失。
:focus-within
有一个即将推出的伪类叫:focus-within
,它正是我们所需要的,使之成为一个只支持CSS的下拉菜单。正如介绍中提到的,如果你需要支持IE < Edge 79,它确实需要一个polyfill。
转载自MDN,下面是我的解释。
:focus-within
CSS伪类代表一个已经接收到焦点的元素,或包含一个已经接收到焦点的元素。换句话说,它代表一个元素本身被:focus
伪类匹配的元素,或有一个被:focus
匹配的子元素。
默认情况下隐藏下拉菜单
在显示下拉菜单之前,我们需要将其隐藏起来,所以我们将使用隐藏样式作为默认状态。
你的第一直觉可能是 display: none
,但这使我们无法优雅地为过渡设置动画。
下一步,你可以尝试简单的opacity: 0
,这样可以明显地隐藏它,但会留下 "幽灵链接",因为元素仍然有计算高度。
相反,我们将使用opacity
和transform
:
我们添加opacity
,但不是全部加到0,以便以后能有更平滑的效果。
而且,我们更新了transform
,包括rotateX(-90deg)
,这将使菜单在3D空间中旋转到90度 "后退"。这将有效地去除高度,并在显示时形成一个有趣的过渡。另外,你会注意到我们添加的transform-origin
属性,它是为了更新应用变换的周围点,而不是默认的水平和垂直中心。
在进行显示之前,我们需要添加一个transition
。我们将其添加到.dropdown__menu
中,这样它就可以同时适用于开关hover
/focus
,又称 "向前"和 "向后"。
揭开下拉菜单的神秘面纱
有了这些事先的设置,就可以像简洁地完成hover
和focus
上的下拉菜单:
从本质上说,我们已经将rotateX
重新设置为0,然后将opacity
全部调到1,以达到完全可见。
效果是这样的:
rotateX
属性让菜单的外观从后面摆动进来,opacity
只是让它整体上的过渡更柔和一些。
处理悬停意向
当我刚开始战斗下拉菜单时,我主要是为IE7创建下拉菜单。在一个大项目中,有几个团队成员问我 "如果我只是滚动/鼠标点击菜单,你能阻止菜单出现吗?"。经过一番搜索,我终于找到了一个解决方案,hoverIntent jQuery plugin.
我需要这样设置,因为既然我们使用的是transition
,我们还可以添加一个非常轻微的延迟。这将防止 "驱动式 "鼠标翻转时触发下拉动画。
延迟作为第三个值加在指定哪些属性过渡之后、过渡时序函数之前,所以延迟是作为第三个值加的:
看看结果:
触发菜单需要一个相当悠闲的翻滚,我们可以松散地推断为打开菜单的意图。这个延迟还是很短的,在打开菜单之前不会被人有意识地注意到,所以这会更好!
你仍然可以选择使用javascript来增强,特别是要推出一个 "巨型菜单",会有更大的破坏性,但这仍然是相当好用的。
下拉菜单指示器
有了hover
之后我们需要一个额外的提示,让用户知道这个菜单有额外的选项。一个非常常见的惯例是模仿原生选择元素的指示器的 "caret "或 "down arrow"。
为了添加这个,我们将更新.dropdown__title
样式。我们将把它定义为一个内inline-flex
,然后创建一个:after
元素,使用边框技巧创建一个向下箭头。我们使用了translateY()
的破折号将其与我们的文本进行对齐:
我们还偷偷地加入了pointer-events: none
,它将删除标题本身上的任何光标事件。
触摸设备的处理
如果你在手机上尝试一下我们到目前为止制作的东西,你将无法打开菜单。
由于CSS中没有:click
或:touch
伪类,所以我们需要让.dropdown__title
能够接收focus
。
我们可以把它变成一个按钮,因为那是一个原生的可聚焦元素,但我们也可以把tabindex="-1"
应用到span
中。这也会向浏览器表明该元素能够接收到焦点,这使得我们的:focus-within
选择器能够工作。
最新代码:
关闭手机上的菜单
这里是你最终可能需要用javascript来强化的地方。
为了让它只用CSS,并为非应用型网站所接受,你需要在主体上应用tabindex="-1"
,允许在菜单之外的任何点击都可以将焦点从菜单中移除,并允许它关闭。
这可不太行,所以你可能会用javascript来增强这一点,特别是如果你定义的nav
使用position: sticky
与用户一起使用的时候,你可能想用javascript来隐藏。
最后
这里是最后的结果,有了一些额外的样式,包括一个箭头来更直观地将菜单与链接项连接起来,在所有的导航链接上自定义焦点状态,以及nav
上的position: sticky
演示: CodePen
版权声明: 本文为 InfoQ 作者【寇云】的原创文章。
原文链接:【http://xie.infoq.cn/article/2890c088ab3847f5d6edfd398】。文章转载请联系作者。
评论