通过建立一个导航栏了解盒子模型

在一个机构工作意味着我的工作大部分都是基于项目的。意味着每隔几个月(或更少,如果你明白我的意思)就会开始一个新的设计。有趣的是,一段时间后,我发现了每个设计师的风格和喜好。一个例子就是连续做过的三个项目中,导航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-topmargin-bottom。水平边距(margin-leftmargin-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-11remmargin-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-boxborder-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;
  }
}

总结

不管我在这个特定的导航栏花费了多长时间,我十分享受这个过程。如果没有践行这个实验,我将不能很好的理解盒子模型。万一我的设计师读到这篇文章,要知道无论在什么时候,我都显示一张"开发人员生气的面孔"(就像这个ಠ_ಠ),但是我仍然爱你.

相关阅读

本文根据@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

返回顶部