在一个机构工作意味着我的工作大部分都是基于项目的。意味着每隔几个月(或更少,如果你明白我的意思)就会开始一个新的设计。有趣的是,一段时间后,我发现了每个设计师的风格和喜好。一个例子就是连续做过的三个项目中,导航UI的设计都有类似的风格。这种特殊的元素对我来说十分显眼,不仅因为我之前见过两次,而且我发现它涉及了盒子模型的各个方面。
所需导航看起来要像这个样子
这是我们大多数人经常会听到的语句。老实说,我第一次看到这种设计,感觉这再简单不过了。不。这是一个错误的结论ಠ_ಠ.
让我们一点一点的分解这个需求。
- 每个链接应该由一条垂直线均等分隔,保证左右空间相等。同时,第一个链接的左边和最后一个链接的右边不需要分隔符。
- 分隔符应该和文本具有相同的高度。
- 鼠标悬停在链接上的文字时,应该出现下划线,并且文字和下划线之间因该保持一定的空间。
- 悬停效果只适用于单个链接。
- 整个链接盒子应该是可点击的,一旦光标进入链接盒子悬停效果就会自动生效。
- 当链接处于悬停状态时,每一个链接的工作状态应该保持一致。
下面就要开始践行我的盒子模型的思维旅程了。如果你只是想要知道最终结果,直接跳到这里。但是这里只能引用Arthur Ashe的一句话(我能说什么,我也是只是一名运动员¯_ (ツ) _ / ¯):
成功是一个旅程而不是终点。过程通常比结果更重要。-Arthur Ashe
基本的标记和样式
让我们从最基本的标记开始,在nav
元素包裹的无序列表的每一项中,都包裹了一个链接文本的a
标签。
<nav>
<ul>
<li><a href="javascript:void(0)">Link 1</a></li>
<li><a href="javascript:void(0)">Link 2</a></li>
<li><a href="javascript:void(0)">Link 3</a></li>
<li><a href="javascript:void(0)">Link 4</a></li>
</ul>
</nav>
然后,应用一些简单的CSS使该列表处于水平位置。
实现导航设计的样式设置 (尝试 1)
这只是众多变化中的一个步骤。通过处理一些元素,有很多方式可以实现导航的设计。我开始的时候只是设置字体,颜色和一些填充。
nav {
font-family: 'Slim Jim', 'Impact', 'Arial Black', sans-serif;
background: black;
}
li {
display: inline-block;
padding: 1rem; /* not such a good idea */
border-left: 1px solid white; /* neither is this */
&:first-child {
border-left: none;
}
}
a {
color: white;
text-decoration: none;
&:hover {
border-bottom: 2px solid white;
}
}
上面的代码是不是会给你带来这样的感觉,不适合需求。而且,只有当你将鼠标悬停在文本,而不是整个链接盒子时,下划线才会出现。(╯°□°)╯︵ ┻━┻
理解盒模型
你仔细想想,网页也只是盒子中的内容,但是被赋予了无数的模式和设计。浏览器通过我们给出的属性对盒子进行渲染。每个盒子就被描述为基于浏览器的盒模型,它决定一个盒子在页面上占用的空间大小。
该模型由内到外由四个组成:
- 内容(
content
) - 内边距(
padding
) - 边框(
border
) - 外边距(
margin
)
内容(content
)
这一领域涉及元素的实际内容、 文本、 背景、 图片等等。它结束于内容区域的边缘。参照上图,指代蓝色盒子。
内边距(padding
)
内边距延伸到内边距边缘。在内容区域的任何内容(例如背景或图像)将延伸到内边距。内容区域和内边距边缘之间的大小填充由padding
属性控制。请参阅上方图表的绿色区域。
边框(border
)
边框延伸到边框的边缘。默认情况下,背景扩大到边框的下方。边框的厚度由border
属性控制。参阅上方图表的黄色区域。
外边距(margin
)
外边距延伸到外边距的边缘。然而,它是分隔元素及其邻近元素的"空"区域。通过margin
属性控制外边距的大小。这里还有一个我们需要注意的一个特定行为的外边距。它被称为 margin collapsing(外边距叠加).
当相邻块元素的外边距结合为单一的外边距时,就会取两者之间较大的值,这时这种情况就会发生。这种现象只出现垂直方向上,即margin-top
和margin-bottom
。水平边距(margin-left
和margin-right
)永远不会发生叠加。这里有一些常见的外边距叠加的情景。
相邻的块元素
我们从上往下堆叠在一起的两个块元素开始。
<div class="block1"></div>
<div class="block2"></div>
.block1 {
width: 300px;
height: 100px;
background: lawngreen;
}
.block2 {
width: 300px;
height: 100px;
background: orange;
}
鉴于他们没有外边距,他们看起来会像这样:
现在,让我们分别给第一个元素的底部和第二个元素的头部添加外边距。
.block1 {
width: 300px;
height: 100px;
background: lawngreen;
margin-bottom: 2rem;
}
.block2 {
width: 300px;
height: 100px;
background: orange;
margin-top: 1rem;
}
如果第一个元素有2rem
的底部外边距,第二个元素具有1 rem
的上外边距,由此产生的差值将为2rem
。这里你可以理解为较小的外边距(第二个元素)有叠加.
如果是负外边距,正外边距和负外边距的值为相加之和。例如如果顶部元素有2 rem
的下外边距,底部元素具有-1rem
的上外边距,由此产生的外边距将为1rem
。
父/子元素
当父元素和子元素具有相邻的垂直外边距时,也会发生边缘叠加。看看这段代码:
.block2 {
width: 300px;
height: 100px;
background: orange;
margin-top: 2rem;
}
.block2-1 {
width: 268px;
height: 50px;
background: yellow;
margin-top: 1rem;
}
你会认为.block1
和.block2
之间的外边距为2rem
,.block2
和.block2-1
边缘之间会有1rem
的外边距,是不是?不是的。再次的,它会取两者之间较大,将外边距叠加成一个外边距。
如果.block2
没有外边距,子元素.block2-1
有1rem
的margin-top
, .block1
和.block2
之间的大小为1rem
。
.block2 {
width: 300px;
height: 100px;
background: orange;
margin-top: 0;
}
.block2-1 {
width: 268px;
height: 50px;
background: yellow;
margin-top: 1rem;
}
如果你不想叠加父元素和子元素之间的的外边距,你需要阻止他们的外边距相互接触。我们可以通过添加填充或边框。假设盒子尺寸已设置为border-box
.
.block2 {
width: 300px;
height: 100px;
background: orange;
margin-top: 0;
padding: 1px;
}
.block2-1 {
width: 268px;
height: 50px;
background: yellow;
margin-top: 1rem;
}
此外,浮动的元素和绝对定位的元素不会存在外边距的叠加。
box-sizing
属性
我们还需要了解一个称为box-sizing的 CSS 属性。该属性会改变默认盒子的模型,会影响您的浏览器计算元素宽度和高度的方式。此属性接受的两个值:content-box
和border-box
。默认情况下,此属性设置为content-box
.
例如,我们有一个简单div
的一些代码:
div {
margin: 10px;
border: 1px solid green;
padding: 10px;
height: 100px;
width: 100px;
}
默认情况下(意味着box-sizing: content-box;
),此div
只根据内容(content)测量高度和宽度。现在,它也具有边框和一些填充,浏览器会默认这个div
的尺寸为122px*122px
.
如果我们设置box-sizing: border-box;
的属性,此时浏览器看到的事情将稍有不同。它将考虑内容的宽度和高度、内边距和边框,作为div
的尺寸。所以内容盒子的宽度变为78px
,内容盒子的高度也为78px
。
好了,既然我们了解了相关知识,让我们回到预定的编程。
实现导航设计的样式设置 (尝试 2)
我们想要整个链接盒子作为可单击区域。我们还需要有一个相同高度的文本链接的分隔符。我们只想要文本长度的下划线。不幸的是,没有直接可行的方式实现这一点。
整个链接盒子应该是一个可点击区域
让我们再试一次样式设置。关于可点击区域问题,我们将应用填充a
标签而不是li
标签:
li {
display: inline-block;
}
a {
color: white;
text-decoration: none;
display: block;
padding: 1rem;
}
请注意,默认情况下a
元素设置为display: inline
,如果你想要采取垂直内边距,你需要将设置更改为display: block
(或者display:inline-block
)。现在整个链接盒子是可点击的。但是我们的下划线终止于链接盒子的边缘.
当鼠标悬停在点击区域时链接文本出现下划线
为了解决这一问题,我们必须使用span
元素对链接文本进行包裹。我不能使用text-decoration: underline
属性,因为实现效果并不好。相反,将使用一个白色下边框。
<nav>
<ul>
<li><a href="javascript:void(0)"><span>Link 1</span></a></li>
<li><a href="javascript:void(0)"><span>Link 2</span></a></li>
<li><a href="javascript:void(0)"><span>Link 3</span></a></li>
<li><a href="javascript:void(0)"><span>Link 4</span></a></li>
</ul>
</nav>
a {
color: white;
text-decoration: none;
display: block;
padding: 1rem;
&:hover span {
border-bottom: 2px solid white;
}
}
这样做是有优点的,你可以有更多的方式控制下划线的样式。通过使用:hover
伪类,当光标进入链接盒子时,就可以触发span
从而显示白色的下边框。
分隔符应该和文本具有相同的高度,并且水平间距相等
如果你一直阅读到这里,你会意识到,这是相当棘手的。因为我们想要分隔符和文本具有相同的高度,我们不能仅仅应用到链接盒子的右边框就收工。如果我们将样式应用到span
元素上呢?请记住,关于盒子模型,首先设置内容(content),然后设置内边距(padding
),边框(border
),外边距(margin
)。内边距将扩展到内容,但外边距不会。
在span
元素上设置1px
的白色右边框作为文本分隔符。同时,让我们在span
上添加一下右内边距。但是,内边距也意味着下划线将扩展长于文本长度,更不用说它会增加已存在的a
元素现有的内边距了(╯ ° □ °) ╯︵ ┻━┻.
实现导航设计的样式设置 (尝试 3)
第三次尝试成功了(否则我可能将键盘从窗口丢出去......只在开玩笑啦)。从之前的失误中我们学到了很多。我们知道了可能不应该使用span
来处理分隔符。我们也知道了另外一件事情,可以使用:after
伪元素。伪元素主要用于设置目标样式,以更好地适应于现在的情况。
a {
color: white;
text-decoration: none;
display: block;
padding: 1rem 0rem 1rem 1rem;
&:after {
content: '';
display: inline-block;
vertical-align: middle;
width: 1px;
height: 1rem;
border-right: 1px solid white;
margin-left: 1rem;
}
&:hover span {
border-bottom: 2px solid white;
}
}
默认情况下,伪元素是内联元素,所以我们将其设置为display: inline-block
,这样我们才可以设置相应的样式。因为伪元素会在元素内容之前或之后插入,伪元素自身的外边距将成为元素内容的一部分。因此这个额外的空间仍属于可点击区域。
最后一步是要删除a
元素上的右内边距,以弥补伪元素的左外边距。终于,我们大功告成!
更新(2016年2月21日):Carlos Tur Arrom好心指出,我忘了删除最后一个链接上的分隔符。为了做到这一点,我们必须找到列表项的最后一个列表,并在:after
伪元素中删除此内容。取而代之增加一个额外的类,我选择使用:last-child
伪类。
li {
display: inline-block;
&:last-child a:after {
border-right: none;
}
}
在这种情况下,以后我不得不在菜单上增加额外的链接,此代码仍然是有效的。无论如何,感谢Carlos Tur Arrom犀利的察觉!
如果伪元素的代码量吓到了你,你可以使用竖线。无论如何,最终的结果类似。
a {
color: white;
text-decoration: none;
display: block;
padding: 1rem 0rem 1rem 1rem;
&:after {
content: '|';
margin-left: 1rem;
}
}
总结
不管我在这个特定的导航栏花费了多长时间,我十分享受这个过程。如果没有践行这个实验,我将不能很好的理解盒子模型。万一我的设计师读到这篇文章,要知道无论在什么时候,我都显示一张"开发人员生气的面孔"(就像这个ಠ_ಠ),但是我仍然爱你.
相关阅读
- 深入理解BFC和Margin Collapse
- 理解CSS中BFC
- Box sizing
- Introduction to the CSS box modelon MDN
- Collapsing margins section of Box Model Specification
- Single-direction margin declarations by Harry Roberts
本文根据@Hui Jing的《Understanding the box model by building a navigation bar》所译,整个译文带有我们自己的理解与思想,如果译得不好或有不对之处还请同行朋友指点。如需转载此译文,需注明英文出处:http://www.chenhuijing.com/blog/that-navigation-bar-design/。
如需转载,烦请注明出处:http://www.w3cplus.com/css/that-navigation-bar-design.html