在本教程中,我们将创建一个移动优先、可切换、响应迅速的导航栏。它将为手机、平板电脑和桌面屏幕提供不同的布局。
Flexbox 非常适合响应式导航
Flexbox 是一个多功能布局模块。它允许我们创建灵活的一维布局,例如 flexbox 响应式菜单。凭借其排序、对齐和大小调整属性,我们可以构建响应式导航栏设计,使其布局适应视口大小。所有这些都保持导航工具栏 HTML 大纲的逻辑性和可访问性。
在本教程中,我们将研究如何创建 flexbox 响应式导航栏。它将有三种不同的布局,具体取决于视口大小:
- 一种移动布局,默认情况下仅显示徽标和切换按钮,用户可以使用切换打开和关闭响应式菜单,
- 在平板电脑布局中,我们将在徽标和切换之间显示两个号召性用语按钮,默认情况下,菜单的其余部分保持可切换状态,
- 一种桌面布局,其中除切换按钮外的所有菜单项都将在屏幕上可见。
我们将使用媒体查询来检测用户浏览器的视口大小。我们的响应式导航栏将是移动优先的,因此我们将从该布局开始。然后,我们将使用媒体查询添加特定于平板电脑和台式机的 CSS。min-width
导航栏响应式设计还将具有基于 JavaScript 的下拉子菜单。当用户单击父菜单项时,它将打开和关闭。
以下是响应式菜单在移动设备上的外观:
以下是平板电脑版本的导航栏设计:
而且,这是 flexbox 导航栏在桌面上的外观:
1. 为 Flexbox 导航栏创建 HTML
导航工具栏 HTML 是一个简单的列表,如下所示。该类将是 flex 容器,列表项将是 flex 项。他们的顺序将适应用户设备的视口大小。<ul>,
.menu
例如,“登录”和“注册”按钮将首先出现在移动设备上。但它们将显示在桌面菜单的末尾。我们将通过利用 flexbox 的排序属性来实现此效果。
<nav>
<ul class="menu">
<li class="logo"><a href="#">Creative Mind Agency</a></li>
<li class="item"><a href="#">Home</a></li>
<li class="item"><a href="#">About</a></li>
<li class="item has-submenu">
<a tabindex="0">Services</a>
<ul class="submenu">
<li class="subitem"><a href="#">Design</a></li>
<li class="subitem"><a href="#">Development</a></li>
<li class="subitem"><a href="#">SEO</a></li>
<li class="subitem"><a href="#">Copywriting</a></li>
</ul>
</li>
<li class="item has-submenu">
<a tabindex="0">Plans</a>
<ul class="submenu">
<li class="subitem"><a href="#">Freelancer</a></li>
<li class="subitem"><a href="#">Startup</a></li>
<li class="subitem"><a href="#">Enterprise</a></li>
</ul>
</li>
<li class="item"><a href="#">Blog</a></li>
<li class="item"><a href="#">Contact</a>
</li>
<li class="item button"><a href="#">Log In</a></li>
<li class="item button secondary"><a href="#">Sign Up</a></li>
<li class="toggle"><a href="#"><i class="fas fa-bars"></i></a></li>
</ul>
</nav>
您是否注意到带有子菜单(“服务”和“计划”)的菜单项具有没有属性的标记?我们这样做是因为这些“空”父菜单项不会指向任何其他页面——它们只是打开和关闭子菜单。<a>,
href
允许使用不带 href 的锚标记。它可以防止当用户单击空菜单项以打开或关闭子菜单时,页面在小屏幕上跳起。
我们还将属性添加到没有属性的元素中。这是因为默认 Tab 键顺序中省略了空标签。我们需要将它们放回带有属性的 Tab 键顺序,以保持菜单键盘可访问。tabindex="0",<a>,href,<a>,tabindex
如果您想让菜单本地打开,您需要在本地托管 Font Awesome。
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.13.0/css/all.min.css">
2. 为您的 Flexbox 导航栏设计添加一些基本样式
让我们为您的导航栏响应式设计提供一些基本样式。我设置了一些默认值和颜色,但是,您也可以使用自己的任何样式规则:
/* Basic styling */
* {
box-sizing: border-box;
padding: 0;
margin: 0;
}
body {
font-family: sans-serif;
font-size: 16px;
}
nav {
background: #222;
padding: 0 15px;
}
a {
color: white;
text-decoration: none;
}
.menu,
.submenu {
list-style-type: none;
}
.logo {
font-size: 20px;
padding: 7.5px 10px 7.5px 0;
}
.item {
padding: 10px;
}
.item.button {
padding: 9px 5px;
}
.item:not(.button) a:hover,
.item a:hover::after {
color: #ccc;
}
3. 从移动导航开始
由于我们的导航将是移动优先的,因此我们从移动布局开始。大多数响应式 flexbox 导航栏菜单都使用基于列的移动布局。这样,通过将规则添加到弹性容器中,可以快速将菜单项打包到彼此下方。flex-direction: column;
尽管这是一个很好的解决方案,但我们不会在示例中使用它。相反,我们将为移动设备创建一个基于行的包装布局,以在菜单顶部并排显示徽标和切换按钮。
这里的 CSS 诀窍是,我们使用规则使常规菜单项(如 Home 和 About)跨越整个容器。因此,flexbox 将将它们显示在彼此的下方,而徽标和切换开关将保留其自然大小,并位于同一行的响应式导航栏的顶部。width: 100%;
在下面的 CSS 中,我们还使用 and 属性来水平和垂直对齐 flex 项。除此之外,我们还使用规则隐藏元素。菜单项仅在用户单击切换按钮时显示。justify-content,
align-items,
.item,
display: none;
该类不在 HTML 代码中,我们将使用 JavaScript 动态添加它。.active
/* Mobile menu */
.menu {
display: flex;
flex-wrap: wrap;
justify-content: space-between;
align-items: center;
}
.menu li a {
display: block;
padding: 15px 5px;
}
.menu li.subitem a {
padding: 15px;
}
.toggle {
order: 1;
font-size: 20px;
}
.item.button {
order: 2;
}
.item {
order: 3;
width: 100%;
text-align: center;
display: none;
}
.active .item {
display: block;
}
.button.secondary { /* divider between buttons and menu links */
border-bottom: 1px #444 solid;
}
正如您在上面看到的,我们还在属性的帮助下更改了菜单项的顺序。我们的导航工具栏 HTML 大纲遵循逻辑顺序。这就是我们希望屏幕阅读器用户和搜索引擎机器人浏览菜单的方式。order
但是,在移动布局中,我们希望在菜单顶部显示徽标和切换按钮。我们还希望在常规菜单项之前显示两个号召性用语按钮(“登录”和“注册”)。因此,我们设置了以下顺序:
.logo
获取该值,因为它将是第一个项目(但是,由于这是 的默认值,我们不需要将其添加到 CSS 中),order: 0;
order
.toggle
得到 ,因为它紧随其后 ,1,
.logo
.item.button
属于登录和注册按钮得到 ,2
- 属于其余菜单项的 get .,
.item,
3
4. 在响应式导航栏中设置子菜单的样式
由于这是移动优先的导航,因此我们将主要根据移动屏幕来设置子菜单的样式。这是一项很棒的技术,因为通常为小屏幕创建用户友好的子菜单比为大屏幕创建用户友好的子菜单更难。
然后,我们也可以对平板电脑屏幕使用相同的子菜单布局。对于桌面,我们只需要更改子菜单的位置。
默认情况下,子菜单设置为,并且仅在用户单击父菜单项时显示。在接下来的两个步骤中,我们将添加所需的 JavaScript 功能,然后再进入平板电脑菜单。display: none;
/* Submenu up from mobile screens */
.submenu {
display: none;
}
.submenu-active .submenu {
display: block;
}
.has-submenu i {
font-size: 12px;
}
.has-submenu > a::after {
font-family: "Font Awesome 5 Free";
font-size: 12px;
line-height: 16px;
font-weight: 900;
content: "\f078";
color: white;
padding-left: 5px;
}
.subitem a {
padding: 10px 15px;
}
.submenu-active {
background-color: #111;
border-radius: 3px;
}
正如你在上面看到的,现在我们使用CSS而不是HTML添加Font Awesome图标。我们使用伪元素添加的这些图标将是每个具有子菜单的菜单项旁边显示的小向下箭头。::after
如果您还记得,我们在步骤 1 中添加了带有 HTML 的切换按钮的 Font Awesome 图标。这是因为切换按钮将以 JavaScript 为目标,因此它必须存在于 DOM 中。
但是,此处的向下箭头只是指示子菜单存在的样式元素。由于没有功能依赖于它们,因此最好使用 CSS 将它们添加到导航栏中。
5. 将 JavaScript 切换功能添加到 Flexbox 导航栏
我们将通过在移动设备上打开和关闭菜单的切换按钮添加单击事件侦听器来设置切换功能。在 JavaScript 代码中,我们将使用 ES6 语法,该语法使我们能够访问 and 符号和循环,并且已经具有良好的浏览器支持。const,let,for...of
对于自定义 JavaScript,请创建一个空文件,并将其添加到响应式导航栏的 HTML 中,在结束标记之前:script.js,
</body>
<script src="script.js"></script>
下面是负责切换功能的 JavaSript 代码:
const toggle = document.querySelector(".toggle");
const menu = document.querySelector(".menu");
/* Toggle mobile menu */
function toggleMenu() {
if (menu.classList.contains("active")) {
menu.classList.remove("active");
// adds the menu (hamburger) icon
toggle.querySelector("a").innerHTML = "<i class=’fas fa-bars’></i>";
} else {
menu.classList.add("active");
// adds the close (x) icon
toggle.querySelector("a").innerHTML = "<i class=’fas fa-times’></i>";
}
}
/* Event Listener */
toggle.addEventListener("click", toggleMenu, false);
- 首先,我们使用该方法选择菜单和切换按钮,以便我们可以使用 JavaScript 访问它们。
querySelector()
- 然后,我们添加单击切换开关时将调用的自定义函数。
toggleMenu()
- 最后,我们添加事件侦听器,该侦听器将使用该方法侦听 click 事件。
addEventListener()
6. 使用 JavaScript 添加下拉菜单 Flexbox 功能
现在,当用户单击切换按钮时,菜单将被激活和停用,但是,子菜单仍处于隐藏状态。我们将使用以下 JavaScript 添加此功能:
const items = document.querySelectorAll(".item");
/* 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);
item.addEventListener("keypress", toggleItem, false);
}
}
在这里,我们将 .submenu-active
类添加到每个菜单项中,当用户单击子菜单时,它有一个子菜单。
- 首先,我们使用该方法选择所有菜单项。它返回一个节点列表(而不是像 .
querySelectorAll(),
querySelector()
- 在自定义
toggleItem()
函数中,我们在 clicked 元素中添加和删除.submenu-active
。请注意,在else if
块中,我们从之前打开的所有其他 flex 菜单项中删除了该类。这样,就不会发生两个子菜单同时打开的情况,因为它们可以在桌面上相互覆盖。 - 最后,我们使用
for...的
循环。在if
块中,我们将两个事件侦听器添加到具有子菜单的菜单项中:一个用于通过单击或点击访问菜单的普通用户的click
事件,另一个用于键盘用户的按键
事件。
7. 使用 Flexbox 创建平板电脑菜单
我们将使用媒体查询创建平板电脑布局。在平板电脑上,默认情况下将显示四个菜单项:徽标、两个号召性用语按钮(“登录”和“注册”)和切换开关。为了让一切变得漂亮,使用 CSS 增强导航栏将:min-width
- 更改 Flexbox 菜单项以使布局适应平板电脑视口,
order
- 重新对齐项目(请参阅下面的说明),
- 使“登录”和“注册”按钮看起来真实(在移动设备中,它们看起来像链接,因为它们是可切换的 flexbox 响应式下拉列表的一部分)。
在代码中:
/* Tablet menu */
@media all and (min-width: 700px) {
.menu {
justify-content: center;
}
.logo {
flex: 1;
}
.item.button {
width: auto;
order: 1;
display: block;
}
.toggle {
flex: 1;
text-align: right;
order: 2;
}
/* Button up from tablet screen */
.menu li.button a {
padding: 10px 15px;
margin: 5px 0;
}
.button a {
background: #0080ff;
border: 1px royalblue solid;
}
.button.secondary {
border: 0;
}
.button.secondary a {
background: transparent;
border: 1px #0080ff solid;
}
.button a:hover {
text-decoration: none;
}
.button:not(.secondary) a:hover {
background: royalblue;
border-color: darkblue;
}
}
在平板电脑布局中,菜单项的对齐方式不同。查看四个可见的菜单项。您将看到两个按钮居中,而徽标和切换开关被推到容器的左端和右端:
我们可以使用CSS规则来实现这种效果。该属性是 、 和 的简写。flex: 1,
flex,
flex-grow,
flex-shrink,
flex-basis
它可以与许多不同的值组合一起存在。当它仅使用一个值声明时,它属于 ,并保留其默认值。flex-grow,
flex-shrink,
flex-basis
在上面的 CSS 中,我们向 and 元素添加了规则。通过这种方式,我们可以告诉浏览器,如果屏幕上有任何正空间,我们希望在这两个元素之间共享它。由于“登录”和“注册”按钮保留其默认值,因此它们不会从额外的空间中获得任何内容。flex: 1,
.logo,
.toggle,
0,
flex-grow
因此,它们将停留在容器的中心,因为它们遵守 flex 容器上设置的规则。justify-content: center;
8. 创建 Desktop Flex 菜单
桌面 flexbox 导航栏菜单隐藏了切换开关,设置了每个项目的原始顺序和自然宽度,并重新定位了子菜单。
请务必记住,平板电脑特定的规则也适用于桌面菜单。这是因为此处的视口宽度大于两个 和 ,因此两个媒体查询都会生效。因此,保留其属性并将其余项目推到容器的末端。700px,
960px,
.logo,
flex: 1;
/* Desktop menu */
@media all and (min-width: 960px) {
.menu {
align-items: flex-start;
flex-wrap: nowrap;
background: none;
}
.logo {
order: 0;
}
.item {
order: 1;
position: relative;
display: block;
width: auto;
}
.button {
order: 2;
}
.submenu-active .submenu {
display: block;
position: absolute;
left: 0;
top: 68px;
background: #111;
}
.toggle {
display: none;
}
.submenu-active {
border-radius: 0;
}
}
9. 允许用户通过单击页面上的任意位置来关闭子菜单
现在只有退后一步了。由于下拉菜单在单击事件上被激活,因此当用户将鼠标悬停在顶部菜单项上时,它不会自动关闭。这在下拉菜单弹性框可以覆盖内容的桌面上尤其烦人。
因此,最好让用户通过单击屏幕上的任意位置来关闭子菜单。我们可以用 JavaScript 添加该功能:
/* Close Submenu From Anywhere */
function closeSubmenu(e) {
if (menu.querySelector(".submenu-active")) {
let isClickInside = menu
.querySelector(".submenu-active")
.contains(e.target);
if (!isClickInside && menu.querySelector(".submenu-active")) {
menu.querySelector(".submenu-active").classList.remove("submenu-active");
}
}
}
/* Event listener */
document.addEventListener("click", closeSubmenu, false);
自定义 功能检查屏幕上是否有打开的子菜单。如果是,它还会检查用户是否在目标
属性的帮助下单击了它。如果用户单击屏幕上的其他任何位置,则该 类将被删除,并且子菜单将自行关闭。 close,Submenu(),
.submenu-active
我们将事件侦听器添加到文档
对象中,因为我们希望侦听整个页面上的点击。您已经使用 flexbox 和 JavaScript 构建了一个响应式导航栏!
Flexbox 是一个很好的工具,无需任何调整即可实现复杂的布局。通过将对齐、排序和大小调整属性与媒体查询相结合,您将为不同的视口创建布局。无需操作 HTML 源代码。