网页开发入门|10 综合练习二:导航栏

又到了综合练习时间!这次我们将一起做一个导航栏。

网页开发入门|10 综合练习二:导航栏

又到了综合练习时间!

现在的我们不但了解了很多常见的HTML的标签,也了解很多常用的CSS样式属性。

接下来,让我们启动一个新的实战项目,一方面综合练习一下已经掌握的知识,另一方面也为未来要学习到的内容埋下线索。

需求分析

这次我们将一起做一个导航栏Navigator

导航栏通常位于网页的顶部,方便用户快速的找到需要前往的板块。

QQ首页的导航栏
QQ首页的导航栏
百度首页的导航栏
百度首页的导航栏
B站的导航栏
B站的导航栏

可以看到,根据不同网站的特性,有些导航栏设计的比较简约,还有一些比较复杂。

这次综合练习,让我们挑战一个相对复杂的任务,来复刻一下B站的二级导航栏:

B站的二级导航栏
B站的二级导航栏

与上次综合练习的图文展示页面不同,这次的页面主要呈现出左右排列的样子,而不是上下排列。

主要分为左、中、右三个部分:

图标区域TabZone

图标区域
图标区域

左边的内容尺寸较大,配有icon,且颜色也比较突出,另外根据人们从左到右的阅读习惯,左边的内容通常也更容易被优先关注到,看来这一部分是网页设计者比较希望用户更关注的内容。

菜单区域MenuZone

菜单区域
菜单区域

中间部分,略列了一些主要的频道,并且在频道的一旁列出了近期的更新数量。其实对一个比较成熟的平台来说,列出这个数字的意义并不是很大,很多是用于营造一种火热的气氛。

推广链接区域LinkZone

推广链接区域
推广链接区域

最右侧,列出一些近期在推广的功能的入口,同样为了吸引点击,使用了icon并且颜色较为突出。

接下来,让我们先在workspace中创建一个新文件navigator.html,然后逐一实现本页面。

图标区域的实现

图标区域的样式,可以有两种理解方式:

一种是看做两排,一排是icon,另一排是文字;

还有一种,可以看做共有4组图标,每一组图标由上方的icon和下方文字组成,然后整体成横向排列。

相对来说,后一种理解更加符合本页面的设计逻辑,因为icon和文字是一定是成对出现的。

我们也采用后面这种理解方式,尝试写出第一组图标。

图片和文字都是行内元素,默认是水平排列的,为了让他们可以垂直排列,我们分别使用一个div标签将其包围起来。

<div class="tab-icon"><img src="assets/bili-shouye.png"></div>
<div class="tab-text">首页</div>

通过CSS调整一下图片的尺寸和文字的样式,还有文字与图片的间距。

.tab-icon img{
  width: 36px;
}
.tab-text {
  color: #212121;
  font-size: 14px;
  margin: 4px 0;
}

经过调整后,我们发现整体上和谐了很多,但是文字却是歪的。这是因为文字默认是居左对齐,我们可以通过设定文字的对齐方向text-align: center来将文字居中。

.tab-text {
  color: #212121;
  font-size: 14px;
  margin: 4px 0;
  text-align: center;
}
文字在整个窗口里居中了
文字在整个窗口里居中了

效果并不尽如人意,文字直接基于整个浏览器窗口居中了,而不是与上方图片的中间对齐。

这是因为div是块元素,块元素会独占一行,所以对块元素内的内容居中,将会成为整行的中心。

我们可以通过明确的设定div的宽度,让它与图片的宽度一致,以实现与图片中心的对齐。

.tab-text {
  color: #212121;
  font-size: 14px;
  margin: 4px 0;
  text-align: center;
  width: 36px;
}
将文字部分的宽度与图片设为一致,实现文字居中
将文字部分的宽度与图片设为一致,实现文字居中

实现了一组图标,我们就可以很容易的实现另外的三组。

CSS的类选择器的帮助下,我们只需要添加HTML代码即可,不再需要重复写样式了。

<div class="tab-icon"><img src="assets/bili-shouye.png" /></div>
<div class="tab-text">首页</div>

<div class="tab-icon"><img src="assets/bili-dongtai.png" /></div>
<div class="tab-text">动态</div>

<div class="tab-icon"><img src="assets/bili-remen.png" /></div>
<div class="tab-text">热门</div>

<div class="tab-icon"><img src="assets/bili-pindao.png" /></div>
<div class="tab-text">频道</div>

但这时我们遇到了一个新的问题,因为块级元素的独占一行的特性,这四组内容形成了纵向排列,而不是水平排列。

当我们既想用与块级元素可以设定宽度的能力(没有宽度就没办法居中对齐了),又想向行内元素一样可以不用自动换行,我们需要使用一种新的元素类型:行内块级元素inline-block

实际上img就是一种典型的行内块级元素,只不过我们之前没有特别强调它的这个特点。

不幸运的是并没有一个行内块级元素,是类似div一样的容器型元素,允许我们放入任何子元素。

不过,可以通过修改某个元素的样式display: inline-block,强行将一个元素的类型转换为行内块级元素。

让我们把每一组图标都使用一个新的div包围起来,并设定其class方便进行样式设定。

<div class="tab-group">
    <div class="tab-icon"><img src="assets/bili-shouye.png" /></div>
    <div class="tab-text">首页</div>
</div>
<!-- 以下省略类似代码 -->

设定样式,并顺便调整一下水平方向的间距:

.tab-group {
    display: inline-block;
    margin: 8px;
}
图标组变成了水平排列
图标组变成了水平排列

菜单区域的实现

这个区域整体是由多个类似的部分组合而成,每个部分由一些文字和后面的数字组成。

但为什么出现了两排,也有两种理解方式:一种我们可以理解为原本就是设计了两排;还有一种是因为这个区域的宽度不足,导致的换行,当高度也容纳不下了,更多的内容被折叠进了「更多」里面。

后一种理解方式相对更灵活一些,当以后增加或删除菜单项时基本不会影响页面的布局。我们也采用这种理解方式,但暂时先不考虑如何将多的内容折叠进「更多」里。

首先,我们来实现其中一组菜单项的效果。

<div class="menu-item">
    <span class="menu-text">动画</span>
    <span class="menu-number">999+</span>
</div>

span是与div类似的行内容器型元素,可以包围任何其他元素。

对应的样式代码:

.menu-item {
    display: inline-block;
}

.menu-text {
    color: #212121;
    font-size: 14px;
}

.menu-number {
    display: inline-block;
    font-size: 12px;
    background: #73c9e5;
    border-radius: 2px;
    color: #fff;
    margin-left: 1px;
}

接下来把其余的组也加上,同样只需要添加HTML代码。

<div class="menu-item">
    <span class="menu-text">动画</span>
    <span class="menu-number">999+</span>
</div>
<div class="menu-item">
    <span class="menu-text">音乐</span>
    <span class="menu-number">999+</span>
</div>
<div class="menu-item">
    <span class="menu-text">舞蹈</span>
    <span class="menu-number">999+</span>
</div>
<!-- 以下省略类似代码 -->

所有内容都列进了一行,但我们希望内容在「放映厅」后面自动换行,所以还需把这些内容装进一个宽度合适的父元素里:

<div class="menu-zone">
    <div class="menu-item">
        <span class="menu-text">动画</span>
        <span class="menu-number">999+</span>
    </div>
    <div class="menu-item">
        <span class="menu-text">音乐</span>
        <span class="menu-number">999+</span>
    </div>
    <div class="menu-item">
        <span class="menu-text">舞蹈</span>
        <span class="menu-number">999+</span>
    </div>
<!-- 以下省略类似代码 -->
</div>
.menu-zone {
    display: inline-block;
    width: 600px;
}

设定了公共父元素之后,换行没问题了,但是又出现对齐的问题,这主要是由于每个数字的宽度不一样而导致的。

数字的宽度也会不一样吗?是的!影响字符宽度的除了字号以外还有字体。在不同的字体下每个字符的宽度会有些许的区别,但有一类字体里所有字符的宽度是一样的,这类字体称作「等宽字体monospaced」。等宽字体常用于代码编辑器等,对「对齐」要求比较高的场景下。

我们需要将数字部分的宽度设为固定值,所以行内块级元素又要出场了。这次我们需要将原本是行内元素的span设定为行内块级元素,使它拥有被设定宽度的能力。

同时,还需要将数字部分设为居中,以避免内容偏到一边去。

.menu-number {
	display: inline-block;
	font-size: 12px;
	background: #73c9e5;
	border-radius: 2px;
	color: #fff;
	margin-left: 1px;
    width: 32px;
    text-align: center;
}

推广链接区的实现

这部分的整体结构与上个部分几乎完全一样,一共有6组内容成两排排列。每组内容都有一个icon和对应的文字成左右排列。

直接上代码。

<div class="link-zone">
    <div class="link-item">
        <span class="link-icon"><img src="assets/bili-read.svg"></span>
        <span class="link-text">专栏</span>
    </div>
    <div class="link-item">
        <span class="link-icon"><img src="assets/bili-activity.svg"></span>
        <span class="link-text">活动</span>
    </div>
    <!-- 以下省略类似代码 -->
</div>
.link-zone {
    display: inline-block;
    width: 260px;
}

.link-item {
    display: inline-block;
    height: 32px;
}

.link-icon img {
    width: 1.8em;
    height: 1.8em;
}

.link-text {
    display: inline-block;
    color: #212121;
    font-size: 14px;
}

我们发现这里出现了icon和文字没有对齐的问题,也就是垂直方向的居中问题。

造成这个问题的原因有两个,其一是因为icon资源本身就有白边,其二是因为在垂直方向上默认的对齐位置是所在行的基线Baseline

在英文字母中,基线的定义比较明确,即小写的x下边沿两点之间的连线就是基线。

图中英语本上每行红线就是基线
图中英语本上每行红线就是基线

汉字与英文字母不同,没有基线的概念,我们简单理解为距离田字格的下边沿约1/4的位置,如下图所示。

img元素的基线就是图片的下底,所以它默认会与同行小写字母x的下边沿对齐。

如果同一排是汉字的话,会是这样的效果:

可想而知,三种行内元素有三种不同的基线,默认对齐的效果一定不会尽如人意。

通过样式属性vertical-align,可以修改元素在垂直方向上的对齐线。但它与text-align不同,vertical-align是用于子元素的,而不是父元素,也就是说允许在行里的每个元素都有自己的对齐方法。

在代码里,将图片和文字都与本行的中线对齐:

.link-icon img {
    width: 1.8em;
    height: 1.8em;
    vertical-align: middle;
}

.link-text {
    display: inline-block;
    color: #212121;
    font-size: 14px;
    vertical-align: middle;
}

分割线的实现

在三个主要部分之间,还有垂直分割线。

原稿中的垂直分割线
原稿中的垂直分割线

我们在上一次综合练习时,了解到hr元素可以创造出一个水平分割线。但不幸的是HTML的标签中,没有定义垂直分割线。

我们可以使用盒模型来模拟一根分割线,此处依然需要的是一个行内块级元素:

<div class="vertical-separator"></div>

以及CSS样式:

.vertical-separator {
    display: inline-block;
    width: 1px;
    height: 46px;
    background-color: #e7e7e7;
    margin: 8px;
}

小结

对比成稿和原稿,相似度已经非常高了,但是仍在很多细节处出现了问题。

成稿
成稿
原稿
原稿

首先,依然是对齐和间距问题,尽管可以通过盒模型,以及相应对齐的样式属性进行调整,但如果亲自试一试会发现实现难度还是很大的。

对齐和间距等问题,都是布局Layout的问题,即如何在页面上更好更舒服的排列各个元素的位置。

然后,还有一处细节问题,动态的右上角有时会有一个小红点,用来提醒用户这里有一些新内容可以看。但我们还不知道如何将两个元素重叠在一起。

动态右上角的小红点
动态右上角的小红点

最后,还有一些需要通过动图才能看出来的效果。

菜单项在鼠标划过时会弹出更多的内容
菜单项在鼠标划过时会弹出更多的内容

网页相比别的媒介来说,最大的优点就是可以对用户的操作进行响应,这也是我们目前所有页面所欠缺的能力。

让我们带上这些问题,迈向下一阶段的旅途。

我将本课程所有的代码托管在gitee上了,你可以点击这个链接,或到gitee.com上搜索弦五 网页开发入门,查看和获取本课程的全部代码。