如果您想构建自己的包含大量项目、图像和列表的大型菜单,那么 flexbox 是适合您的工具。它将允许您创建一个内容感知、多列和多级的巨型菜单,在悬停时下拉——无需任何 javascript 或 css 调整。
在本教程中,我将向您展示如何使用 flexbox 布局模块创建以下下拉式巨型菜单:
这不是一个简单的布局,因此我们将使用以下步骤创建它:
首先,我们标记主菜单栏并设置样式。
然后,我们为大型菜单创建一个弹性框模板,您可以在上面的屏幕截图中看到这些部分:假期类型、假期套餐、我们的服务、最后一分钟优惠和热门目的地。
我们将内容(图像、<ul>列表、图标)插入到模板中。
接下来,我们对齐每个部分内的项目。
我们从 CSS 中删除了模板的辅助样式(背景颜色和边框)。
我们添加了悬停规则,当用户将鼠标悬停在“假期”菜单上时将显示大型菜单。
如果您想在开始之前测试我们的 flexbox 巨型菜单,请查看以下演示:
See the Pen Mega Menu with Flexbox by Envato Tuts+ (@tutsplus) on CodePen.
1. 创建主菜单栏
主菜单栏由五个项目组成:Home、Holidays、About、Blog和Contact。下拉大型菜单将属于Holidays,而其他菜单项将独立存在。这是标记主菜单栏的 html(但还不是超级菜单):
<nav>
<ul class="menu">
<li class="menu-item"><a href="#">Home</a></li>
<li class="menu-item"><a href="#">Holidays</a></li>
<li class="menu-item"><a href="#">About</a></li>
<li class="menu-item"><a href="#">Blog</a></li>
<li class="menu-item"><a href="#">Contact</a></li>
</ul>
</nav>
在 CSS 中,我们现在将定义基本样式和颜色。我们还将把.menu元素变成一个弹性容器,这样菜单项就可以排成一排。通过将center值添加到align-items属性中,我们也在 flex 容器中垂直居中每个项目:
/* General styling */
* {
box-sizing: border-box;
margin: 0;
padding: 0;
font-family: sans-serif;
}
.menu li {
padding: 10px;
}
ul {
list-style-type: none;
}
a {
color: white;
text-decoration: none;
}
a:hover {
text-decoration: underline;
}
img {
max-width: 100%;
}
/* Flexbox rules */
.menu {
background-color: darkslateblue;
display: flex;
align-items: center;
text-align: center;
width: 90vw;
margin: 20px 5vw;
height: 60px;
}
.menu-item {
flex: 1;
}
我们还在flex: 1;菜单项上设置了规则,使它们跨越 flex 容器的整个宽度,并在它们之间平均共享空间。
下面,您可以看到主菜单栏现在的样子:
2. 为超级菜单创建一个模板
为了使巨型菜单的布局正确,我们首先创建一个模板,然后用内容填充它。由于我们的大型菜单由多个级别的无序列表组成,因此从布置主要部分开始会容易得多。这就是我们的 HTML 变化的方式:
<nav>
<ul class="menu">
<li class="menu-item"><a href="#">Home</a></li>
<li class="menu-item"><a href="#">Holidays</a>
<ul class="submenu">
<li class="submenu-item">
<ul class="submenu-top">
<li class="submenu-top-item">Holiday types (links with thumbnail images)</li>
<li class="submenu-top-item">Holiday packages (links with descriptions)</li>
<li class="submenu-top-item">Our services (links with icons)</li>
<li class="submenu-top-item">Last minute offers (links)</li>
</ul><!-- End .submenu-top -->
</li><!-- End .submenu-item-->
<li class="submenu-item">
<ul class="submenu-bottom">
<li class="submenu-bottom-item">Trending destination 1</li>
<li class="submenu-bottom-item">Trending destination 2</li>
<li class="submenu-bottom-item">Trending destination 3</li>
<li class="submenu-bottom-item">Trending destination 4</li>
</ul>
</li><!-- End .submenu-item -->
</ul>
</li>
<li class="menu-item"><a href="#">About</a></li>
<li class="menu-item"><a href="#">Blog</a></li>
<li class="menu-item"><a href="#">Contact</a></li>
</ul>
</nav>
如您所见,.submenu它有两个部分:.submenu-top基于列表的四个部分(假期类型、假期套餐、我们的服务、最后一分钟优惠)和.submenu-bottom基于图像的热门目的地。它们将是两个独立的 flex 容器,因此我们可以对每个容器应用不同的 flexbox 大小和对齐规则。
下面,你可以看到我们想要使用 flexbox 实现的布局。我在模板中添加了颜色和边框,以便您可以看到不同的弹性项目是如何相互关联的(模板使用上面的 HTML):
第一行是主菜单;我们已经在步骤 1 中创建并设置了它的样式。第二行是.submenu-top一个弹性容器,它允许项目根据其内容(内容感知)占用尽可能多的空间。而且,第三行是.submenu-bottom一个弹性容器,它在项目之间平均分配空间。
除此之外,我们使用、和属性进行.submenu相对定位。由于是 60px 高,我们相对于它的位置向下移动 60px。下面的 CSS 还包含属于和的上述样式,但它也添加到。.menupositiontopleft.menu.submenu.menu.menu-itemposition: relative;.menu
.menu {
display: flex;
align-items: center;
text-align: center;
width: 90vw;
margin: 20px 5vw;
height: 60px;
position: relative;
}
.menu-item {
flex: 1;
}
.submenu {
width: 90vw;
position: absolute;
top: 60px;
left: 0;
text-align: left;
display: flex;
flex-direction: column;
}
.submenu-item {
padding: 15px;
}
.submenu-top {
display: flex;
justify-content: space-around;
}
.submenu-bottom {
display: flex;
}
.submenu-bottom-item {
flex: 1;
}
我们通过将规则添加.submenu-top到. 同时,两者都是 基于行的 flex 布局,用于它们自己的子元素。我们没有添加 到代码中,因为这是 的默认值,我们只是将和的属性设置 为。.submenu-bottomflex-direction: column.submenu.submenu-top.submenu-bottomflex-direction: row;flex-directiondisplay.submenu-top.submenu-bottomflex
因为我们想要 .submenu-top 内容感知,我们需要允许弹性项目尽可能多地伸展,所以我们没有在flex这里定义属性(它将使用它的默认值)。但是,我们希望项目正确地彼此相邻排列,所以我们添加 justify-content: space-around; 到.submenu-top. 这样,正空间将在弹性项目之间均匀分布。
的布局.submenu-bottom与主菜单 ( .menu) 的布局相同。因此,我们flex: 1;对项目使用规则以使它们彼此平等地共享 flex 容器的整个宽度。
在下面的演示中,您可以看到我们的大型菜单的不同部分是如何相互比较的:
See the Pen Mega Menu with Flexbox (template) by Envato Tuts+ (@tutsplus) on CodePen.
3. 将内容插入模板
在这一步中,我们不会做任何事情,只是将最终内容插入到我们的大型菜单模板中。在.submenu-top中,每个 flex 项目都有自己的无序列表,而.submenu-bottom对于四个趋势目的地只有一个无序列表:
<nav>
<ul class="menu">
<li class="menu-item"><a href="#">Home</a></li>
<li class="menu-item"><a href="#">Holidays</a>
<ul class="submenu">
<li class="submenu-item">
<ul class="submenu-top">
<li class="submenu-top-item thumb-list">
<a href="#" class="submenu-title">Holiday Types</a>
<ul class="submenu-list">
<li>
<img class="submenu-thumbnail" src="https://picsum.photos/id/1067/32/32" alt="City breaks">
<a href="#">City breaks</a>
</li>
<li>
<img class="submenu-thumbnail" src="https://picsum.photos/id/173/32/32" alt="Adventure tours">
<a href="#">Adventure tours</a>
</li>
<li>
<img class="submenu-thumbnail" src="https://picsum.photos/id/1038/32/32" alt="Cruises">
<a href="#">Cruises</a>
</li>
<li>
<img class="submenu-thumbnail" src="https://picsum.photos/id/1015/32/32" alt="Beach holidays">
<a href="#">Beach holidays</a>
</li>
</ul>
</li>
<li class="submenu-top-item desc-list">
<a href="#" class="submenu-title">Holiday packages</a>
<ul class="submenu-list">
<li>
<a href="#">Families</a>
<span class="submenu-desc">Take advantage of our special holiday packages for families.</span>
</li>
<li>
<a href="#">Students</a>
<span class="submenu-desc">Take advantage of our special holiday packages for students.</span>
</li>
<li>
<a href="#">Couples</a>
<span class="submenu-desc">Take advantage of our special holiday packages for couples.</span>
</li>
</ul>
</li>
<li class="submenu-top-item icon-list">
<a href="#" class="submenu-title">Our services</a>
<ul class="submenu-list">
<li>
<i class="fas fa-plane-departure"></i>
<a href="#">Plane tickets</a>
</li>
<li>
<i class="fas fa-car"></i>
<a href="#">Car rental</a>
</li>
<li>
<i class="fas fa-luggage-cart"></i>
<a href="#">Luggage pickup</a>
</li>
<li>
<i class="fas fa-phone-alt"></i>
<a href="#">24/7 customer service</a>
</li>
<li>
<i class="fas fa-dollar-sign"></i>
<a href="#">30-day cancellation policy</a>
</li>
</ul>
</li>
<li class="submenu-top-item">
<a href="#" class="submenu-title">Last minute offers</a>
<ul class="submenu-list">
<li><a href="#">New York</a></li>
<li><a href="#">Stockholm</a></li>
<li><a href="#">Madrid</a></li>
<li><a href="#">Buenos Aires</a></li>
<li><a href="#">Tokyo</a></li>
</ul>
</li>
</ul><!-- End .submenu-top -->
</li><!-- End .submenu-item-->
<li class="submenu-item">
<a href="#" class="submenu-title">Trending destinations</a>
<ul class="submenu-bottom">
<li class="submenu-bottom-item">
<figure>
<img src="https://picsum.photos/id/1016/640/260">
<figcaption><a href="#">Mountains</a></figcaption>
</figure>
</li>
<li class="submenu-bottom-item">
<figure>
<img src="https://picsum.photos/id/1040/640/260">
<figcaption><a href="#">Castles</a></figcaption>
</figure>
</li>
<li class="submenu-bottom-item">
<figure>
<img src="https://picsum.photos/id/1039/640/260">
<figcaption><a href="#">Waterfalls</a></figcaption>
</figure>
</li>
<li class="submenu-bottom-item">
<figure>
<img src="https://picsum.photos/id/164/640/260">
<figcaption><a href="#">Historical towns</a></figcaption>
</figure>
</li>
</ul>
</li><!-- End .submenu-item -->
</ul>
</li>
<li class="menu-item"><a href="#">About</a></li>
<li class="menu-item"><a href="#">Blog</a></li>
<li class="menu-item"><a href="#">Contact</a></li>
</ul>
</nav>
在上面的 HTML 中,我使用了Font Awesome图标并从Lorem Picsum中提取图像。如果您查看下面的屏幕截图,您将立即理解我们为什么从创建模板开始:
这些项目已经在我们想要的布局中排列。现在我们只需要做一些小的对齐工作,然后删除模板的辅助颜色和边框。
4. 对齐每个部分内的项目
在这一步中,我们不会触及 HTML,只是使用 CSS 修复布局的小问题。当我看到上面的图片时,我首先看到的是假日套餐部分.submenu-top与其他项目相比太宽了。如果我们有更长的描述,这可能会变得更糟。因此,max-width为 flex 项目添加一个值是合理的.submenu-top,例如:
.submenu-top-item {
max-width: 33.333%;
}
通过控制弹性项目的宽度,我们的巨型菜单看起来更漂亮:
现在对 CSS 进行一些微小的调整。我们将第一列和第二列的每个列表项变成一个单独的 flex 容器。这意味着Holiday types部分中有四个微型 flex 容器,Holiday 包中有三个微型 flex 容器。
我们使用此解决方案通过以下方式强制元素彼此相邻或彼此下方体面地排列:
在假日类型部分,我们使用align-items: center;精确地在缩略图的中间行显示文本,
在假期套餐部分,我们使用flex-direction: column;在归属定义(家庭、学生、情侣)下方显示每个描述。
在其他部分中,我们不会使用 flexbox 进行内联对齐,因为它可以通过基本的 CSS 规则来完成,即marginand width:
/* Submenu styling */
.submenu .submenu-title {
text-decoration: none;
font-weight: bold;
}
.submenu-list,
.submenu-bottom {
margin-top: 10px;
}
/* Thumbnail list */
.thumb-list .submenu-list > li {
display: flex;
align-items: center;
}
.submenu-thumbnail {
margin-right: 10px;
}
/* Description list */
.desc-list .submenu-list > li {
display: flex;
flex-direction: column;
}
.submenu-desc {
margin-top: 10px;
color: #555;
}
/* Icon list */
.submenu-icon {
width: 32px;
}
/* Submenu bottom */
.submenu-bottom figcaption {
margin-top: 5px;
font-weight: bold;
}
这就是我们对齐的巨型菜单现在的样子:
5. 删除模板助手
现在,一切都排好了!我们现在必须删除模板的辅助颜色和边框,并将最终颜色添加到链接和标题部分。下面,您可以找到没有悬停规则的最终 CSS:
/* General styling */
* {
box-sizing: border-box;
margin: 0;
padding: 0;
font-family: sans-serif;
}
ul {
list-style-type: none;
}
a {
color: white;
text-decoration: none;
}
.submenu-icon {
color: #111;
}
a:hover {
text-decoration: underline;
}
img {
max-width: 100%;
}
.menu {
background-color: darkslateblue;
}
.submenu {
border: 1px solid #ccc;
}
.menu li {
padding: 10px;
}
.menu-icon {
color: white;
margin-left: 10px;
font-size: 14px;
}
/* Flexbox rules */
.menu {
display: flex;
align-items: center;
text-align: center;
width: 90vw;
margin: 10px 5vw;
height: 60px;
position: relative;
}
.menu-item {
flex: 1;
}
.menu-item > a {
line-height: 40px;
}
.submenu {
width: 90vw;
position: absolute;
top: 60px;
left: 0;
text-align: left;
display: flex;
flex-direction: column;
}
.submenu-item {
padding: 15px;
}
.submenu-top {
display: flex;
justify-content: space-around;
}
.submenu-bottom {
display: flex;
}
.submenu-bottom-item {
flex: 1;
}
/* Submenu general styling */
.submenu-top li {
padding-left: 0;
}
.submenu a {
color: #111;
}
.submenu-top-item {
max-width: 33.333%;
}
.submenu .submenu-title {
font-weight: bold;
color: darkslateblue;
}
.submenu .submenu-title:hover {
text-decoration: none;
}
.submenu-list,
.submenu-bottom {
margin-top: 10px;
}
/* Submenu: Thumbnail list */
.thumb-list .submenu-list > li {
display: flex;
align-items: center;
}
.submenu-thumbnail {
margin-right: 10px;
}
/* Submenu: Description list */
.desc-list .submenu-list > li {
display: flex;
flex-direction: column;
}
.submenu-desc {
margin-top: 10px;
color: #555;
}
/* Submenu: Icon list */
.submenu-icon {
width: 32px;
}
/* Submenu bottom */
.submenu-bottom-title {
padding-left: 10px;
}
.submenu-bottom figcaption {
margin-top: 5px;
font-weight: bold;
}
.submenu-bottom a:hover {
text-decoration: none;
}
HTML 仍然与第 3 步中的相同。但是,我们还在Holidays菜单项中添加了一个来自 Font Awesome 的向下箭头图标,以指示巨型菜单的存在(我们使用.menu-icon上面 CSS 中的句柄对其进行样式设置)。
<li class="menu-item holidays"><a href="#">Holidays</a>
<i class="menu-icon fas fa-angle-down"></i>
...
</li>
在下面,您可以看到一切看起来都符合预期:
6. 添加悬停规则
为了使巨型菜单在悬停时下拉,我们需要隐藏它并仅在.menu-item获得悬停状态时才显示它:
.submenu {
display: none;
}
.menu-item:hover .submenu {
display: flex;
}
.menu-item:hover > a {
text-decoration: underline;
}
现在,我们的 flexbox 超级菜单已经启动并运行了。在下面的演示中,您也可以对其进行测试并查看相应的 HTML 和 CSS 代码:
See the Pen Mega Menu with Flexbox by Envato Tuts+ (@tutsplus) on CodePen.
概括
尽管构建大型菜单是一项复杂的任务,但使用 flexbox 完全可以做到,尤其是如果您首先创建一个模板来帮助您获得正确的布局。
你可能会问,用CSS 网格而不是 flexbox创建大型菜单是否更容易/更好,我说“这取决于”。尽管 CSS 网格允许您创建二维布局,但 flexbox 带有开箱即用的内容感知,这在这种情况下是理想的。
如果你将超级菜单的每一行都变成一个弹性容器,你可以对每行应用不同的对齐、换行和大小规则。这也是 Bootstrap 4 等基于 flexbox 的框架所遵循的设计原则。此外,flexbox 仍然比 CSS 网格有更好的支持(目前 全球使用的所有浏览器中有98.3%与 CSS 网格的92.03%相比,但是这将在未来改变)。
您还可以使用媒体查询和 flexbox 布局使您的大型菜单具有响应性。如果你想知道如何去做,请查看我之前关于如何使用 flexbox 构建响应式导航栏的教程。