响应式下拉菜单栏
创建响应式下拉菜单是一项棘手的任务。 在设计和开发阶段,您必须做出许多决定。 推荐的解决方案通常取决于您正在构建的网站或应用程序的特征。 但是,对于所有类型的响应下拉菜单,也推荐一些常规最佳实践。
1.使用移动优先设计
问题
尽管先为台式机屏幕设计菜单然后将其适应较小的视口似乎比较容易,但通常情况并非如此,尤其是在菜单具有下拉菜单的情况下。
如果您首先设计用于台式机的菜单,它可能会很快变得复杂,并且最终可能会在移动设备上出现一个非常长的菜单,用户仅需不断滚动就可以滚动,而没有到达终点!
解
您可以使用min-width媒体查询而不是max-width min-width媒体查询来实现移动优先设计:
@media all and (min-width: 960px) {
// ...
}
使用min-width媒体查询时,移动设计将是默认设置,您将通过先移动到平板电脑然后移动到台式机屏幕来添加规则(或者在某些情况下,甚至不需要为平板电脑单独设计) 。
从性能的角度来看,这也很重要。 如果使用移动优先设计,则智能手机(经常通过计量数据连接或不连续的wifi访问您的网站)必须评估较少的样式规则,因为它们可以跳过媒体查询块。
2.重新排序菜单项
问题
在许多情况下,您可能希望在移动设备,平板电脑和台式机屏幕上以不同的顺序显示菜单和子菜单项,但希望保持HTML大纲的逻辑性和可访问性。
解
使用order属性,可以更改菜单项的可视顺序,同时保持DOM不变。
根据经验,HTML大纲中的菜单项应遵循最适合屏幕阅读器和其他辅助技术用户的顺序。 不要忘记,搜索引擎机器人也只类似于HTML阅读器,而看不到视觉顺序,因此它也类似于屏幕阅读器。
这是一个由号召性用语按钮和菜单链接组成的菜单示例。 我们希望号召性用语(CTA)出现在移动设备和平板电脑上的菜单开头,以及桌面设备上的菜单结尾。 在HTML中,菜单链接将排在最前面(但是,这不是必须的,因为在某些情况下,您可能想将号召性用语按钮放在屏幕阅读器用户的前面)。
<nav>
<ul>
<li class="mlink">Menu Link 1</li>
<li class="mlink">Menu Link 2</li>
<li class="mlink">Menu Link 3</li>
<li class="cta">CTA 1</li>
<li class="cta">CTA 2</li>
</ul>
</nav>
由于order的默认值为0,因此您不必为第一个元素更改它。 对于视觉顺序中的第二个元素,其值为1。对于第三个元素,其值为2,依此类推。 请注意, order也可以取负值。 因此,CSS如下所示:
ul {
display: flex;
}
.mlink {
order: 1;
}
@media all and (min-width: 960px) {
.mlink {
order: 0;
}
}
注意 : order属性仅适用于flexbox和CSS网格布局,因此,如果要使用它,则需要添加以下任一display: flex; 或display: grid; 到父菜单项。
3.在手机和平板电脑上使用事件监听器
问题
在桌面上,通常使用:hover伪类激活子菜单。 当用户将鼠标悬停在父菜单项上时,关联的子菜单会在屏幕上弹出。 当他们将鼠标移开时,子菜单消失。 但是,在移动设备,平板电脑和其他触摸设备上没有:hover 。
解
通过使用JavaScript事件侦听器,只要用户单击/触摸父菜单项,就可以打开和关闭子菜单。 addEventListener()方法是DOM API的一部分,并从IE9内置到每个现代浏览器中。
以下代码示例摘自上述我的flexbox菜单教程 ,每当用户单击父菜单项时,它就会添加或删除.submenu-active类。 可以使用CSS添加.submenu-active的display样式。
/* Activate Submenu */
function toggleItem() {
if (this.classList.contains("submenu-active")) {
this.classList.remove("submenu-active");
} else if (menu.querySelector(".submenu-active")) {
menu.querySelector(".submenu-active").classList.remove("submenu-active");
this.classList.add("submenu-active");
} else {
this.classList.add("submenu-active");
}
}
/* Event listeners */
for (let item of items) {
if (item.querySelector(".submenu")) {
item.addEventListener("click", toggleItem, false);
}
}
for...of循环遍历所有菜单项,然后if块选择具有子菜单的对象,并向其添加click事件监听器。 每当用户单击/点按带有子菜单的菜单项时,都会调用自定义toggleItem()函数。
可以使用事件侦听器定位DOM API中的许多事件 。 对于触摸设备, click和touchstart是最常用的设备。 上面,我们使用了click因为它同时适用于click和touch,而touchstart仅适用于touch而不适用于click。
注意 :使用addEventListener()方法只能定位一种事件类型。 与jQuery相比,这是一个重要的区别,在jQuery中,您可以向用于事件监听器的on()方法添加多个事件。
4.在桌面上单击和悬停之间选择
问题
现在您的子菜单可以在移动设备和平板电脑上使用,这是另一个问题:在台式机上,您可能希望使用户能够将鼠标悬停在该子菜单上而不是单击。 但是,如果您已经添加了单击事件侦听器以在触摸设备上显示子菜单,则桌面菜单将同时对单击和悬停做出React。
这两个用户操作很容易相互干扰,这可能导致混乱,甚至在某些情况下甚至破坏菜单的布局。
解
在桌面视口上,您需要确定子菜单是在悬停还是单击时显示。 通常,我想说的是,如果您要构建一个Web应用程序,请选择click,因为这是大多数Web应用程序用户所希望的行为。 而且,如果您正在构建网站,请选择悬停,因为它在网站上感觉更自然。
如果选择单击
如果您决定将click事件处理程序保留在桌面上,则需要解决一个小的UX问题。 当用户离开菜单区域导航时,子菜单不会自行关闭(这是悬停时的正常行为)。 要关闭子菜单,用户将不得不导航回到父菜单项并再次单击它,这不是理想的用户体验。
要解决此问题,您可以使用以下脚本,该脚本使用户可以通过单击页面上的任意位置来关闭子菜单。
/* Close Submenu From Anywhere */
function closeSubmenu(e) {
let isClickInside = menu.contains(e.target);
if (!isClickInside && menu.querySelector(".submenu-active")) {
menu.querySelector(".submenu-active").classList.remove("submenu-active");
}
}
/* Event listener */
document.addEventListener("click", closeSubmenu, false);
每当在文档内部但在菜单区域之外单击时,它将事件监听器添加到document对象,并删除.submenu-active类。
如果您选择悬停
如果选择悬停,则需要使用JavaScript检测视口的大小,并向事件侦听器添加一个额外的if语句,以检查视口的宽度是否小于媒体查询断点。 您可以使用document对象的clientWidth属性来检测视口的宽度(不包括任何滚动条)。
因此,在上面的代码中,调用自定义toggleItem()函数的事件侦听器循环将更改为:
if (document.documentElement.clientWidth < 960) {
for (let item of items) {
if (item.querySelector(".submenu")) {
item.addEventListener("click", toggleItem, false);
}
}
}
在CSS中,您需要将负责桌面子菜单布局的规则添加到:hover伪类中。
5.对空的父菜单项使用不带href属性的<a>标记
问题
很多时候,父菜单项仅用于打开和关闭所属子菜单,但它们没有链接到任何地方。 但是,如果要保持HTML轮廓一致和CSS样式表简单,则还需要向这些“空”菜单项添加<a href="#">锚标记。
但是,在这种情况下,当用户单击父菜单项打开或关闭子菜单时,页面将重新加载并跳至移动设备的顶部。 如果菜单较长,这尤其糟糕。
解
解决此问题的常见方法是添加"javascript: void(0);" href属性的值。 但是,这是一种不好的做法 ,因为它使用伪URL,该伪URL返回undefined作为值,这可能导致不同的错误和意外的行为。
解决此问题的最简单方法是对空菜单项使用没有href属性的<a>标记,这是一种有效的解决方案,因为href不是必需的属性。 根据W3C文档 :
“a和area元素上的href属性不是必需的; 当这些元素没有href属性时,它们将不会创建超链接。”
但是,空<a>标记有两个问题:
- 它们无法通过键盘访问,因为默认选项卡顺序将其省略。 您可以通过将
tabindex="0"属性添加到每个没有href属性的<a>标签来解决此问题。 - 即使选项卡顺序中包含空的
<a>元素,用户也无法通过按键盘上的Enter来打开/关闭关联的子菜单。 这可以通过为keypress事件创建事件侦听器来解决。
因此,这就是HTML的结构方式:
<ul class="menu">
<li class="item"><a href="home.html">Home</a></li>
<li class="item"><a href="about.html">About</a></li>
<li class="item has-submenu">
<a tabindex="0">Services</a>
<ul class="submenu">
<li class="subitem"><a href="service1.html">Service 1</a></li>
<li class="subitem"><a href="service2.html">Service 2</a></li>
<li class="subitem"><a href="service3.html">Service 3</a></li>
</ul>
</li>
<li class="item has-submenu">
<a tabindex="0">Plans</a>
<ul class="submenu">
<li class="subitem"><a href="plan1.html">Plan 1</a></li>
<li class="subitem"><a href="plan2.html">Plan 2</a></li>
<li class="subitem"><a href="plan3.html">Plan 3</a></li>
</ul>
</li>
<li class="item">li><a href="blog.html">Blog</a></li>
<li class="item"><a href="contact.html">Contact</a></li>
</ul>
对于事件侦听器,您可以使用我们创建的用于打开/关闭子菜单的相同自定义toggleItem()函数。 您只需按以下方式向上述for...of循环添加一个keypress事件侦听器:
for (let item of items) {
if ( item.querySelector(".submenu")) {
item.addEventListener("click", toggleItem, false);
item.addEventListener("keypress", toggleItem, false);
}
}
6.使图标脱机可用
问题
如果您使用在线图标字体库(例如Font Awesome)将图标添加到下拉子菜单(例如常用的向下箭头),则当离线访问网站/应用程序时,图标将消失。 请注意,脱机可用性也是可访问性问题。
解
解决该问题的方法相对容易。 您也可以从本地加载图标库,而不是从CDN加载图标库。 例如,您可以按以下方法亲自托管Font Awesome 。 如果仅加载您在站点上使用过的部分库,甚至可以节省页面加载,即使这还取决于特定图标库的结构。
响应式下拉菜单最佳做法就是这样!
建立响应式下拉菜单似乎很容易,但是,您需要注意许多细节。 它需要在不同的设备上工作,对不同的事件做出正确的React,键盘用户可以访问,可以离线使用等等。
如果您认为通过使用案例通常可以访问您的网站或应用程序,则可以更轻松地决定如何解决用户尝试使用您的子菜单时可能出现的所有问题。
您认为我们没有提及其他最佳做法吗? 让我们知道!
分叉此:响应下拉菜单解决方案
在CodePen上查看我的自适应下拉菜单,该菜单使用了本指南中介绍的所有最佳实践。 分叉它,并对其进行更改以适合您自己的项目!
进一步了解Tuts +上的导航设计
翻译自: https://webdesign.tutsplus.com/articles/best-practices-for-responsive-dropdown-menus--cms-35212
响应式下拉菜单栏
本文介绍了响应式下拉菜单的6个最佳实践,包括移动优先设计、菜单项重新排序、在触摸设备上使用事件监听器、选择桌面设备上的单击或悬停、处理空的父菜单项以及确保图标离线可用。这些实践旨在确保菜单在不同设备上良好工作,兼顾可访问性和性能。
1841

被折叠的 条评论
为什么被折叠?



