本文已参与「新人创作礼」活动,一起开启掘金创作之路。
1. 开发目标
对于栏目众多的门户网站来说,站点栏目导航能够方便用户浏览整个网站。今天我们通过实现网易首页的栏目导航部分来继续熟悉前端开发。
2. 结构分析
网易首页的栏目导航是在一个通栏的浅灰色背景上以网格的形式均匀地分布12个矩形(Logo部分可以看作一个背景透明会浅灰色的矩形)。矩形内部采用一级栏目作为标题,二级栏目按两列分布形成列表。在矩形块下方,还有一个右对齐的站点地图超链接。根据设计图,我们可以创建如下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>
<div class="container">
<nav class="sitemap">
<ul class="sitemap-list">
<li class="sitemap-item">
<a href="/" target="_blank"></a>
</li>
<li class="sitemap-item">
<h3>新闻</h3>
<ul>
<li>国内</li>
<li>国际</li>
<li>军事</li>
<li>王三三</li>
</ul>
</li>
<li class="sitemap-item">
<h3>体育</h3>
<ul>
<li>NBA</li>
<li>CBA</li>
<li>综合</li>
<li>中超</li>
<li>国际足球</li>
<li>英超</li>
<li>西甲</li>
<li>意甲</li>
</ul>
</li>
<li class="sitemap-item">
<h3>娱乐</h3>
<ul>
<li>明星</li>
<li>图片</li>
<li>电影</li>
<li>电视</li>
<li>音乐</li>
<li>稿事编辑部</li>
<li>娱乐FOCUS</li>
</ul>
</li>
<li class="sitemap-item">
<h3>财经</h3>
<ul>
<li>股票</li>
<li>行情</li>
<li>新股</li>
<li>基金</li>
</ul>
</li>
<li class="sitemap-item">
<h3>汽车</h3>
<ul>
<li>购车</li>
<li>行情</li>
<li>车型库</li>
<li>行业</li>
<li>新能源</li>
<li>降价</li>
</ul>
</li>
<li class="sitemap-item">
<h3>科技</h3>
<ul>
<li>智能</li>
<li>5G</li>
<li>互联网</li>
<li>通信</li>
<li>IT</li>
<li>科学</li>
<li>区块链</li>
<li>原创精选</li>
</ul>
</li>
<li class="sitemap-item">
<h3>时尚</h3>
<ul>
<li>亲子</li>
<li>艺术</li>
<li>有颜有品</li>
<li>beauty鉴定团</li>
</ul>
</li>
<li class="sitemap-item">
<h3>手机/数码</h3>
<ul>
<li>易评机</li>
<li>电脑</li>
<li>惊奇科技</li>
<li>家电</li>
<li>相机</li>
</ul>
</li>
<li class="sitemap-item">
<h3>房产/家居</h3>
<ul>
<li>北京房产</li>
<li>上海房产</li>
<li>广州房产</li>
<li>全部分站</li>
<li>楼盘库</li>
<li>家装案例</li>
<li>卫浴</li>
</ul>
</li>
<li class="sitemap-item">
<h3>旅游</h3>
<ul>
<li>户外</li>
<li>美食</li>
<li>专题</li>
</ul>
</li>
<li class="sitemap-item">
<h3>教育</h3>
<ul>
<li>移民</li>
<li>留学</li>
<li>外语</li>
<li>高考</li>
</ul>
</li>
</ul>
<div class="go-sitemap">
<a href="/" target="_blank">查看网易站点地图</a>
</div>
</nav>
</div>
</body>
</html>
3. 样式分析
我们采取“先整后零,从上到下,由外而内”的基本方针为网页添加样式。该模块与本系列一开始的导航区不相同,灰色背景占据了整个浏览器可视区的宽度。
.container {
font-size: 12px;
width: 100%;
background-color: #f6f6f6;
border-top: 1px solid #e5e5e5;
}
3.1 样式重置(Reset)
水平方向上的结果与预期出现了一些差别,元素左右两侧出现了空隙:
在Firefox、Chrome、Edge等浏览器中,鼠标右击后选择“查看”,在“查看器”中依次检查html、body等元素后,发现是body元素默认有8像素的外边距,如图。
部分HTML标签默认样式具有外边距或内边距,但许多情况下都不符合我们的设计要求。在前端开发中经常会用到一种叫Reset的技术,将这些默认样式取消。
* {
margin: 0;
padding: 0;
}
上面的样式利用通配符选择器,将所有元素的内外边框都设置为0。在有道词典(dict.youdao.com/)的样式中就能看到类似…
html, body, div, h3, li, ul {
margin: 0;
padding: 0;
}
网页中其它不需要的默认样式,也可以采用类似方法进行重置。
ul {
list-style: none;
}
3.2 内容居中
从设计图看,虽然本模块的背景色平铺了整个可视区,但实际的内容区的宽度还是与标题区或者导航区相同,只占据了中间960像素的宽度。
.sitemap {
width: 960px;
margin: 0 auto;
}
当块标签的左右
margin为auto时,它的左右外边距会根据容器的宽度自动平均分配,从而实现元素内容居中显示。
3.3 一级栏目显示
站点地图的一级栏目与站点Logo一起,分成12个大小相同的方块,均匀分布在两行。我们使用的ul无序列表组织的一级栏目,默认每行显示一个栏目。在前面的文章中,我们都是使用float属性来实现元素的水平排列。这次将尝试使用CSS 3引入的Flexbox布局。
与前面所学过的布局方式不同,Flexbox需要涉及到两层元素的属性:容器和子元素。父元素作为容器,拥有一些从整体上控制子元素布局的属性,而子元素具有单独设置自身布局的属性。父元素(容器)必须设置display属性的值为flex才能启用Flexbox布局。
.sitemap-list {
display: flex;
flex-wrap: wrap;
}
默认情况下,启用Flexbox布局后,子元素会水平分布在同一行,可能会超出父元素(容器)。flex-wrap属性控制子元素是否可以在超出父元素宽度后进行换行,默认为nowrap不可换行,设置wrap允许换行。
子元素的宽度默认由它的内容宽度决定,但也可以单独设置宽度。子元素的宽度设置有两种方式。
- Flex弹性布局属性。
width属性。
.sitemap-item {
flex-basis: 16.6666666667%;
/* width: 16.6666666666667%; */
}
Flexbox弹性布局通过三个属性控制子元素在主轴方向上的大小。
flex-basis:指定子元素初始大小,默认只决定内容区的大小。与box-sizing结合使用,可以包括padding和border。flex-grow:指定子元素在主轴方向扩张时,占据的可扩展区域的比例,详情见参考资料5。flex-shrink:指定子元素在主轴方向上空间不够时,需要缩小的份额,详情见参考资料5。
在这一节中,我们只使用flex-basis或者width设置子元素的宽度,后面再通过专题了解Flexbox弹性布局的详情。每一行都是6个等宽的元素,因此设置单个元素宽度为容器的1/6。CSS不支持使用分数属性,因此需要将它计算成百分比小数,对于不能尽除的分数,应该适当长地保留小数点后的数字位数(Bootstrap框架一般保留了小数点后8位)。
部分文献认为只需要保留2-4位即可,考虑到现在的设备分辨率越来越高,可能导致布局出现些许误差。
3.4 calc函数
上面我们提到过使用flex-basis或width可以设置子元素的宽度,使每行平均分布6个子元素。接下来设置每个子元素的背景色以及元素间的间隙。background-color设置的背景色会填充padding和内容区域。一般通过设置margin来实现元素间的间隙。我们为每个元素添加6像素的外边距。
.sitemap-item {
flex-basis: 16.6666666666667%;
/* width: 16.6666666666667%; */
margin: 6px;
background-color: #e8e8e8;
}
前面提到过,元素实际占据的空间在没有使用box-sizing的情况下,由内容区、内边距、边框和外边距一起组成。而默认情况下,元素的width仅指内容区的大小。因此为每个元素添加外边距后,每行无法再容纳6个元素。在这种情况下,我们需要将外边距的宽度从width中扣除。最简单的办法就是利用CSS提供的calc函数。
.sitemap-item {
flex-basis: calc(16.6666666667% - 12px);
/* width: calc(16.6666666667% - 12px); */
margin: 6px;
background-color: #e8e8e8;
}
3.5 重逢box-sizing
紧贴色块边界的文字会让人产生拥挤的感觉。我们一般利用内边距让内容区与边界形成一定的距离。
.sitemap-item {
flex-basis: calc(16.6666666667% - 12px);
/* width: calc(16.6666666667% - 12px); */
padding: 6px 6px 12px;
margin: 6px;
background-color: #e8e8e8;
}
与margin类似,默认情况下增加元素的padding同样会使它所占的空间变大。不过前面已经介绍过,可以通过box-sizing属性将padding纳入width的计算中。
.sitemap-item {
box-sizing: border-box;
}
3.6 细节样式
现在我们距离完成设计目标十分接近了。首先来完成位于第一个色块的Logo图标。第一个色块的背景色与整个模块的背景色相同。从前面讲的“视觉假象”来讲,我们有两种方法实现:
- 色块背景透明。
- 色块背景与模块背景相同。
.sitemap-item:first-child {
/* background-color: transparent; */
background-color: #f6f6f6;
}
<a>标签默认无法设置宽度,将其转化成inline-block后,再利用背景图片将Logo显示出来。
.sitemap-item a {
display: inline-block;
width: 120px;
height: 25px;
margin: 0 auto;
background-image: url(../image/netease_logo.png);
background-position: 0 -8px;
background-repeat: no-repeat;
}
line-height是一个继承属性,能够让容器内每行文字具有相同的行高。
.sitemap-item {
line-height: 25px;
}
二级栏目的排列与一级栏目一样可以通过Flexbox布局轻易实现。
.sitemap-item ul {
display: flex;
flex-wrap: wrap;
}
.sitemap-item ul > li {
flex-basis: 50%;
}
3.7 text-align右对齐
在前面的内容中,我们几乎都是利用浮动实现内容的左右对齐。其实对于行内元素(inline或者inline-block),我们完全可以利用它所在块元素容器的text-align属性实现水平方向的对齐样式。
.go-sitemap {
text-align: right;
}
.go-sitemap a {
font-size: 12px;
line-height: 30px;
margin-right: 10px;
padding-right: 20px;
color: #404040;
text-decoration: none;
background-image: url(../image/netease_more_sprites.png);
background-position: 100% -537px;
background-repeat: no-repeat;
}
4. 完整样式
html, body, div, h3, li, ul {
margin: 0;
padding: 0;
}
ul {
list-style: none;
}
.container {
font-size: 12px;
width: 100%;
background-color: #f6f6f6;
border-top: 1px solid #e5e5e5;
}
.sitemap {
width: 960px;
margin: 0 auto;
}
.sitemap-list {
display: flex;
flex-wrap: wrap;
}
.sitemap-item {
flex-basis: calc(16.6666666667% - 12px);
/* width: calc(16.6666666667% - 12px); */
padding: 6px 6px 12px;
margin: 6px;
box-sizing: border-box;
line-height: 25px;
background-color: #e8e8e8;
}
.sitemap-item:first-child {
/* background-color: transparent; */
background-color: #f6f6f6;
}
.sitemap-item a {
display: inline-block;
width: 100%;
height: 25px;
background-image: url(../image/netease_logo.png);
background-position: 0 -8px;
background-repeat: no-repeat;
}
.sitemap-item ul {
display: flex;
flex-wrap: wrap;
}
.sitemap-item ul > li {
flex-basis: 50%;
}
.go-sitemap {
text-align: right;
}
.go-sitemap a {
font-size: 12px;
line-height: 30px;
margin-right: 10px;
padding-right: 20px;
color: #404040;
text-decoration: none;
background-image: url(../image/netease_more_sprites.png);
background-position: 100% -537px;
background-repeat: no-repeat;
}
.go-sitemap a:hover {
color: #ff3333;
}