本文已参与「新人创作礼」活动,一起开启掘金创作之路。
1. 效果展示
导航菜单区是网站最重要的组成部分之一,对于一个新闻门户网站来说更是这样。网易首页的导航菜单采用平铺展开的两级菜单,最终效果如下。
2. 页面结构分析与实现
我们首先对设计图的结构进行分析:
-
垂直分隔线将菜单区水平划分成6个部分。
-
每个部分分成2行。
-
每行均匀分布3-4个超链接,其中第一个链接设置为粗体。
根据上面的结构分析,可以有多种HTML结构进行实现。通常我们采用无序列表(ul)表示逻辑上有并列关系的内容。下面是其中一种HTML结构:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>网易新闻</title>
</head>
<body>
<nav>
<ul>
<li>
<ul>
<li>新闻</li>
<li>军事</li>
<li>国内</li>
<li>航空</li>
</ul>
<ul>
<li>娱乐</li>
<li>电影</li>
<li>音乐</li>
<li>经典</li>
</ul>
</li>
<li>
<ul>
<li>体育</li>
<li>红彩</li>
<li>NBA</li>
<li>中超</li>
</ul>
<ul>
<li>财经</li>
<li>股票</li>
<li>基金</li>
<li>商业</li>
</ul>
</li>
<li>
<ul>
<li>科技</li>
<li>手机</li>
<li>智能</li>
<li>科学</li>
</ul>
<ul>
<li>时尚</li>
<li>教育</li>
<li>高考</li>
<li>艺术</li>
</ul>
</li>
<li>
<ul>
<li>直播</li>
<li>游戏</li>
<li>公开课</li>
</ul>
<ul>
<li>房产</li>
<li>家居</li>
<li>二手房</li>
</ul>
</li>
<li>
<ul>
<li>汽车</li>
<li>购车</li>
<li>选车</li>
</ul>
<ul>
<li>健康</li>
<li>酒香</li>
<li>身体课</li>
</ul>
</li>
<li>
<ul>
<li>江苏</li>
<li>政务</li>
<li>网易号</li>
</ul>
<ul>
<li>公益</li>
<li>旅游</li>
<li>严选</li>
</ul>
</li>
</ul>
</nav>
</body>
</html>
该结构由两层无序列表(ul)嵌套组成。其中外层有6个列表项(li)。每个子项又由两个无序列表组成。显示效果如下:
这显然不会是我们想要的样子:
-
页面内容居中显示在960像素的区域,字体和字体大小需要设置。
-
不需要无序列表默认为每个列表项显示的小圆点以及缩进。
-
同一个列表中,各列表项水平排列。
-
在第一级列表项间添加垂直分隔线。
-
每个第二级列表的第一个列表项显示粗体。
3. 样式分析
首先解决上面所提出的问题。前一节中已经介绍过通过设置块标签(block)的宽度(width)和外边距(margin)使内容居中的方式。我们在这里直接模仿一下。
nav {
width: 960px;
margin: 0 auto;
font-size: 12px;
font-family: '微软雅黑';
}
font-size将nav标签及其子标签的字体设置为设计稿所要求的12像素。font-family设置字体为微软雅黑。
列表(无序列表或有序列表)的每一个列表项都会默认显示一个指示器并且进行缩进。其中列表项指示器由list-style或者list-style-type设置。列表项的缩进是由ul的左内边距(padding-left)带来的。因此我们引入一下样式消除这些影响:
ul {
list-style-type: none;
padding-left: 0;
}
list-style和list-style-type属性的值为none时,会取消显示列表项前的指示标记(如小圆点)。ul选择器将选取页面里所有的无序列表,并应用属性。
列表项(li)属于块标签(block),默认独占一行,利用浮动属性(float)能够实现在同一行水平排列多个列表项的目的。设计稿中两级列表均为水平排列列表项,因此可以为每一个li标签都添加浮动属性,同时添加必要的内边距。
li {
float: left;
padding: 6px 7px; /* 上下内边距6px,左右内边距7px */
}
网易新闻在每两个一级列表项之间添加了修饰用的垂直分隔线。我们可以通过为列表项添加左边框或右边框就能够增加一条垂直线,但是边框的高度与所属元素的至少是所属元素的内容区高度与上下内边距高度之和。根据前面创建好的结构和样式,如果直接为第一级列表项添加右边框,分隔线的的高度会超出文字显示范围,与设计稿不一致。效果如下:
因此我们需要一个显示在第一级列表项的左侧,并且能够自由设置高度的内容(块元素)。最直接的想法就是在每个列表项后再添加一个空白的列表项,或者在第一级列表项里添加一个空白块元素。结构如下:
<nav>
<ul>
<li>
<ul>
<li>新闻</li>
<li>军事</li>
<li>国内</li>
<li>航空</li>
</ul>
<ul>
<li>娱乐</li>
<li>电影</li>
<li>音乐</li>
<li>经典</li>
</ul>
<div></div>
</li>
</ul>
</nav>
空白div标签不会占据空间,需要添加样式将其打造为一条分隔线:
nav > ul > li > div {
width: 100%;
height: 40px;
padding-right: 7px;
margin-top: 10px;
border-right: 1px #e5e5e5 solid;
}
div标签属于块标签,默认独占一行,可以单独设置宽度和高度。
最后一步就是为每个二级列表的第一个子项添加粗体。CSS 3提供了选择子标签的伪类选择器,能够准确的选取指定子标签。
li > ul > li:first-child {
font-weight: bold;
}
:first-child属于CSS 3提供的伪类选择器,它能够将所选中的范围缩小为第一个子元素。在这个例子中,li > ul > li会选中所有第二级列表的全部列表项,但是加上:first-child后,会从中挑选出每一个第二级列表的第一个列表项。
4. 结构优化
可能有读者会有疑惑:在该结构中,div标签与两个ul标签属于同一个li的子标签,并且都是块标签,应该显示在ul标签的文字之下,如图所示:
这里要考虑到我们为每一个li标签添加了float: left属性。它会使得这些元素脱离原本的文档流,也就是不再占据父标签的高度。因此所有第二级列表ul的高度都为0。如图所示:
这些列表中的文字虽然在视觉上还是正常显示,但实际上在浏览器的计算中,它们已经位于所在列表的外面了。如果在列表下还有其它内容,就会与这些内容发生重叠,例如这里的分隔线。因此在使用浮动属性时,一定要注意消除它对后续内容的影响。消除浮动的方式有很多种,这里我们采用最简单的:为父标签添加overflow: hidden属性。
ul {
overflow: hidden;
list-style-type: none;
padding-left: 0;
}
overflow: hidden会消除子标签使用浮动带来的影响,使父标签的高度能够包裹全部子标签。
4.1 负外边框
上图的阴影部分可以看出,ul标签的高度完全覆盖了它全部浮动li标签。它同时带来了一个副作用,就是垂直分隔线被挤到了下面。CSS提供了多种移动元素位置的方法,比如采用负值的外边框。
nav > ul > li > div {
width: 100%;
height: 40px;
padding-right: 7px;
margin-top: -48px;
border-right: 1px #e5e5e5 solid;
}
外边框(
margin)可以使用负值。它会使该元素朝相反方向移动。合理利用负值外边框,能够形成元素重叠效果。
4.2 绝对定位
HTML标签的position属性能够实现多种元素定位方式,默认为static类型。当标签的position属性被设置为absolute时,配合将父标签的position属性设置为非static值,可以实现相对父标签定位。
li {
position: relative;
float: left;
padding: 6px 7px;
}
nav > ul > li > div {
position: absolute;
height: 40px;
top: 16px;
right: 0;
border-right: 1px #e5e5e5 solid;
}
其中right表示当前标签的右侧与父标签右侧的距离为40像素,top表示当前标签的顶部到父标签顶部的距离为16像素。
4.3 使用伪元素
通过上面的代码,我们基本实现了网易首页的导航菜单区域(没有添加链接),但垂直分隔线是通过增加单独的空白div标签实现的。这个做法会在HTML结构中增加一个没有具体含义的空标签,需要侵入式地修改HTML代码,不符合结构与显示相分离的原则。::after伪元素能够在选中元素的最后添加一个inline类型的虚拟子元素。我们可以利用这个特性来避免增加空白标签。
nav > ul > li::after {
display: block;
position: absolute;
top: 15px;
right: 0;
width: 0;
height: 40px;
content: '';
border-right: 1px #e5e5e5 solid;
}
nav > ul > li:last-child::after {
border-right: none;
}
nav > ul > li::after可以选中第一级列表每个列表项的::after伪元素。因为不需要具体显示内容,所以将content属性设置为空。将display改为block,方便设置该伪元素的宽度和高度。
由于最后一个列表项的右侧不需要垂直分隔线,因此通过nav > ul > li:last-child::after选中最后一个列表项的::after伪元素,并将它的边框取消。
最终代码:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>网易新闻</title>
<style>
nav {
width: 960px;
margin: 0 auto;
font-size: 12px;
font-family: '微软雅黑';
}
ul {
list-style-type: none;
padding-left: 0;
}
li {
position: relative;;
float: left;
padding: 6px 7px;
}
nav > ul > li::after {
display: block;
position: absolute;
top: 16px;
right: 0;
width: 0;
height: 40px;
content: '';
border-right: 1px #e5e5e5 solid;
}
nav > ul > li:last-child::after {
border-right: none;
}
li > ul > li:first-child {
font-weight: bold;
}
</style>
</head>
<body>
<nav>
<ul>
<li>
<ul>
<li>新闻</li>
<li>军事</li>
<li>国内</li>
<li>航空</li>
</ul>
<ul>
<li>娱乐</li>
<li>电影</li>
<li>音乐</li>
<li>经典</li>
</ul>
</li>
<li>
<ul>
<li>体育</li>
<li>红彩</li>
<li>NBA</li>
<li>中超</li>
</ul>
<ul>
<li>财经</li>
<li>股票</li>
<li>基金</li>
<li>商业</li>
</ul>
</li>
<li>
<ul>
<li>科技</li>
<li>手机</li>
<li>智能</li>
<li>科学</li>
</ul>
<ul>
<li>时尚</li>
<li>教育</li>
<li>高考</li>
<li>艺术</li>
</ul>
</li>
<li>
<ul>
<li>直播</li>
<li>游戏</li>
<li>公开课</li>
</ul>
<ul>
<li>房产</li>
<li>家居</li>
<li>二手房</li>
</ul>
</li>
<li>
<ul>
<li>汽车</li>
<li>购车</li>
<li>选车</li>
</ul>
<ul>
<li>健康</li>
<li>酒香</li>
<li>身体课</li>
</ul>
</li>
<li>
<ul>
<li>江苏</li>
<li>政务</li>
<li>网易号</li>
</ul>
<ul>
<li>公益</li>
<li>旅游</li>
<li>严选</li>
</ul>
</li>
</ul>
</nav>
</body>
</html>