CSS 布局介绍
本文将回顾我们在之前的模块中已经接触过的一些 CSS 布局功能,例如不同的 display
值,并介绍一些我们将在本模块中介绍的概念。
CSS 页面布局技术允许我们获取 Web 页面中包含的元素,并控制它们相对于以下因素的位置:它们在正常布局流中的默认位置、周围的其他元素、它们的父容器以及主视口/窗口。我们将在本模块中更详细介绍的页面布局技术有
- 正常流
display
属性- Flexbox
- 网格
- 浮动
- 定位
- 表格布局
- 多列布局
每种技术都有其用途、优势和劣势。没有一种技术旨在孤立使用。通过了解每种布局方法的设计目的,您将能够更好地理解哪种方法最适合每项任务。
正常流
正常流是浏览器在您不控制页面布局时默认如何布局 HTML 页面的方式。让我们来看一个简单的 HTML 示例
<p>I love my cat.</p>
<ul>
<li>Buy cat food</li>
<li>Exercise</li>
<li>Cheer up friend</li>
</ul>
<p>The end!</p>
默认情况下,浏览器将如下显示此代码
请注意,HTML 的显示顺序与其在源代码中出现的顺序完全相同,元素一个叠放在另一个上面——第一段,然后是无序列表,然后是第二段。
一个接一个出现的元素称为块级元素,与内联元素形成对比,内联元素像段落中的单个单词一样并排出现。
注意:块级元素内容的布局方向称为块级方向。块级方向在英语等具有水平书写模式的语言中垂直运行。在日语等任何具有垂直书写模式的语言中,它将水平运行。相应的内联方向是内联内容(如句子)将运行的方向。
对于页面上的许多元素,正常流将创建您所需的布局。但是,对于更复杂的布局,您需要使用 CSS 中提供的一些工具来更改此默认行为。从结构良好的 HTML 文档开始非常重要,因为您可以处理默认的布局方式,而不是与之对抗。
可以在 CSS 中更改元素布局方式的方法有
display
属性——标准值(如block
、inline
或inline-block
)可以更改元素在正常流中的行为,例如,使块级元素的行为像内联级元素(请参阅 CSS 盒子类型 以获取更多信息)。我们还有一些通过特定display
值启用的完整布局方法,例如 CSS 网格 和 Flexbox,它们会更改子元素在其父元素内的布局方式。- 浮动 — 应用像
left
这样的float
值可以导致块级元素沿着某个元素的一侧环绕,就像杂志版式中图片周围有时会有文字环绕一样。 position
属性 — 允许你精确控制盒子在其他盒子内部的位置。static
定位是正常流中的默认值,但你可以使用其他值来改变元素的布局方式,例如,将其固定到浏览器视口顶部。- 表格布局 — 为样式化HTML表格的部分而设计的特性可以使用
display: table
和相关属性应用于非表格元素。 - 多列布局 — 多列布局属性可以使块的内容以多列的形式布局,就像你在报纸上看到的那样。
display 属性
在CSS中实现页面布局的主要方法都涉及为display
属性指定值。此属性允许我们更改某物显示的默认方式。正常流中的所有内容都具有display
的默认值;即元素设置为行为的默认方式。例如,英文段落一个接一个显示的事实是因为它们使用display: block
进行样式化。如果你在一个段落内的一些文本周围创建一个链接,则该链接将与文本的其余部分保持内联,并且不会换行。这是因为<a>
元素默认情况下为display: inline
。
你可以更改此默认显示行为。例如,<li>
元素默认情况下为display: block
,这意味着列表项在我们的英文文档中一个接一个地显示。如果我们将显示值更改为inline
,它们将并排显示,就像句子中的单词一样。你可以为任何元素更改display
的值这一事实意味着你可以根据语义含义选择HTML元素,而无需担心它们的外观。它们的外观是可以更改的。
除了能够通过将项目从block
更改为inline
反之亦然来更改默认显示外,还有一些更复杂的布局方法从display
的值开始。但是,在使用这些方法时,通常需要调用其他属性。在我们讨论布局时最重要的两个值是display: flex
和display: grid
。
Flexbox
Flexbox是弹性盒子布局CSS模块的简称,旨在使我们能够轻松地在一维上布局内容——要么作为一行,要么作为一列。要使用flexbox,你需要将display: flex
应用于要布局的元素的父元素;然后,其所有直接子元素都将成为弹性项目。我们可以在一个简单的示例中看到这一点。
设置display: flex
下面的HTML标记为我们提供了一个具有wrapper
类的容器元素,在其内部是三个<div>
元素。默认情况下,这些元素将显示为块级元素,即在我们的英文文档中一个接一个地显示。
但是,如果我们将display: flex
添加到父元素,这三个项目现在将排列成列。这是因为它们成为了弹性项目,并受到flexbox在弹性容器上设置的一些初始值的影响。它们以行显示,因为父元素的flex-direction
属性的初始值为row
。它们看起来都垂直拉伸,因为其父元素的align-items
属性的初始值为stretch
。这意味着项目会拉伸到弹性容器的高度,在本例中,由最高的项目定义。所有项目都排列在容器的开头,在行的末尾留下任何额外的空间。
.wrapper {
display: flex;
}
<div class="wrapper">
<div class="box1">One</div>
<div class="box2">Two</div>
<div class="box3">Three</div>
</div>
设置flex属性
除了可以应用于弹性容器的属性外,还可以应用于弹性项目的属性。除其他事项外,这些属性可以更改项目的弹性方式,使它们能够根据可用空间进行扩展或收缩。
作为一个简单的例子,我们可以将flex
属性添加到我们所有的子项目中,并为其指定值为1
。这将导致所有项目增长并填充容器,而不是在末尾留下空间。如果有更多空间,则项目将变得更宽;如果空间较小,则项目将变得更窄。此外,如果你向标记中添加另一个元素,则其他所有项目都将变小以腾出空间;所有项目一起继续占据所有空间。
.wrapper {
display: flex;
}
.wrapper > div {
flex: 1;
}
<div class="wrapper">
<div class="box1">One</div>
<div class="box2">Two</div>
<div class="box3">Three</div>
</div>
注意:这只是对flexbox中可能实现的功能的非常简短的介绍。要了解更多信息,请参阅我们的Flexbox文章。
网格布局
虽然flexbox专为一维布局而设计,但网格布局专为二维布局而设计——将内容排列成行和列。
设置display: grid
与flexbox类似,我们使用其特定的显示值——display: grid
启用网格布局。下面的示例使用与flex示例类似的标记,包含一个容器和一些子元素。除了使用display: grid
外,我们还使用grid-template-rows
和grid-template-columns
属性分别为父元素定义了一些行和列轨道。我们定义了三列,每列为1fr
,以及两行,每行100px
。我们不需要对子元素设置任何规则;它们会自动放置到我们创建的网格单元格中。
.wrapper {
display: grid;
grid-template-columns: 1fr 1fr 1fr;
grid-template-rows: 100px 100px;
gap: 10px;
}
<div class="wrapper">
<div class="box1">One</div>
<div class="box2">Two</div>
<div class="box3">Three</div>
<div class="box4">Four</div>
<div class="box5">Five</div>
<div class="box6">Six</div>
</div>
在网格上放置项目
拥有网格后,你可以显式地在网格上放置项目,而不是依赖于上面看到的自动放置行为。在下面的下一个示例中,我们定义了相同的网格,但这次有三个子项目。我们使用grid-column
和grid-row
属性设置了每个项目的开始和结束线。这会导致项目跨越多个轨道。
.wrapper {
display: grid;
grid-template-columns: 1fr 1fr 1fr;
grid-template-rows: 100px 100px;
gap: 10px;
}
.box1 {
grid-column: 2 / 4;
grid-row: 1;
}
.box2 {
grid-column: 1;
grid-row: 1 / 3;
}
.box3 {
grid-row: 2;
grid-column: 3;
}
<div class="wrapper">
<div class="box1">One</div>
<div class="box2">Two</div>
<div class="box3">Three</div>
</div>
注意:这两个示例仅揭示了网格布局强大功能的一小部分。要了解更多信息,请参阅我们的网格布局文章。
本指南的其余部分涵盖其他布局方法,这些方法对于页面的主要布局不太重要,但仍然有助于完成特定任务。通过了解每个布局任务的本质,你很快就会发现,当你查看设计的特定组件时,最适合它的布局类型通常会很清楚。
浮动
使元素浮动会更改该元素及其在正常流中跟随它的块级元素的行为。浮动元素将移动到左侧或右侧并从正常流中移除,并且周围的内容会环绕它。
float
属性有四个可能的值
left
— 将元素浮动到左侧。right
— 将元素浮动到右侧。none
— 指定根本没有浮动。这是默认值。inherit
— 指定应从元素的父元素继承float
属性的值。
在下面的示例中,我们将一个<div>
向左浮动,并在右侧设置margin
以将周围的文本推离它。这使我们能够实现文本环绕方框元素的效果,并且这是你对现代网页设计中使用的浮动需要了解的大部分内容。
<h1>Simple float example</h1>
<div class="box">Float</div>
<p>
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nulla luctus aliquam
dolor, eu lacinia lorem placerat vulputate. Duis felis orci, pulvinar id metus
ut, rutrum luctus orci. Cras porttitor imperdiet nunc, at ultricies tellus
laoreet sit amet. Sed auctor cursus massa at porta. Integer ligula ipsum,
tristique sit amet orci vel, viverra egestas ligula. Curabitur vehicula tellus
neque, ac ornare ex malesuada et. In vitae convallis lacus. Aliquam erat
volutpat. Suspendisse ac imperdiet turpis. Aenean finibus sollicitudin eros
pharetra congue. Duis ornare egestas augue ut luctus. Proin blandit quam nec
lacus varius commodo et a urna. Ut id ornare felis, eget fermentum sapien.
</p>
.box {
float: left;
width: 150px;
height: 150px;
margin-right: 30px;
}
注意:在我们的关于float和clear属性的课程中完整解释了浮动。在flexbox和网格布局等技术之前,浮动被用作创建列布局的方法。你可能仍然会在网络上遇到这些方法;我们将在关于传统布局方法的课程中介绍这些方法。
定位技术
定位允许你将元素从它在正常流中放置的位置移动到另一个位置。定位不是创建页面主要布局的方法;它更多的是关于管理和微调页面上特定项目的位置。
但是,有一些有用的技术可以获得依赖于position
属性的特定布局模式。了解定位也有助于理解正常流及其含义,以及将项目移出正常流的含义。
有五种类型的定位你应该了解
- 静态定位是每个元素获得的默认值。它只是意味着“将元素放置到文档布局流中的正常位置——这里没有什么特别的”。
- 相对定位允许你修改元素在页面上的位置,相对于其在正常流中的位置移动它,以及使其与页面上的其他元素重叠。
- 绝对定位将元素完全移出页面的正常布局流,就像它位于自己的单独图层上一样。从那里,你可以将其固定到相对于其最近的已定位祖先的边缘的位置(如果没有任何其他祖先被定位,则变为
<html>
)。这对于创建复杂的布局效果很有用,例如选项卡式框,其中不同的内容面板彼此叠加,并根据需要显示和隐藏,或者默认情况下位于屏幕外的信息面板,但可以使用控制按钮使其滑入屏幕。 - 固定定位与绝对定位非常相似,只是它相对于浏览器视口而不是另一个元素固定元素。这对于创建效果很有用,例如一个持久导航菜单,该菜单始终停留在屏幕上的相同位置,而其他内容则在滚动。
- 粘性定位是一种更新的定位方法,它使元素的行为类似于
position: relative
,直到它到达视口定义的偏移量,此时它会像position: fixed
一样工作。
简单的定位示例
为了让你熟悉这些页面布局技术,我们将向你展示几个快速示例。我们的示例都将具有相同的HTML结构(标题后跟三个段落),如下所示
<h1>Positioning</h1>
<p>I am a basic block level element.</p>
<p class="positioned">I am a basic block level element.</p>
<p>I am a basic block level element.</p>
此HTML将默认使用以下CSS进行样式化
body {
width: 500px;
margin: 0 auto;
}
p {
background-color: rgb(207 232 220);
border: 2px solid rgb(79 185 227);
padding: 10px;
margin: 10px;
border-radius: 5px;
}
.positioned {
background: rgb(255 84 104 / 30%);
border: 2px solid rgb(255 84 104);
}
呈现的输出如下所示
相对定位
绝对定位
绝对定位用于完全从正常流中移除元素,而是使用相对于包含块边缘的偏移量对其进行定位。
回到我们最初的非定位示例,我们可以添加以下CSS规则来实现绝对定位
.positioned {
position: absolute;
top: 30px;
left: 30px;
}
在这里,我们为中间段落指定了absolute
的position
值,以及与之前相同的top
和left
属性。添加此代码将产生以下结果
这非常不同!已定位的元素现在已完全与页面的其余布局分离,并位于其顶部。其他两个段落现在并排放置,就像它们的已定位同级元素不存在一样。top
和left
属性对绝对定位元素的影响与对相对定位元素的影响不同。在这种情况下,偏移量是从页面的顶部和左侧计算的。可以更改成为此容器的父元素,我们将在关于定位的课程中介绍这一点。
固定定位
固定定位以与绝对定位相同的方式将元素从文档流中移除。但是,偏移量不是从容器应用的,而是从视口应用的。由于项目相对于视口保持固定,因此我们可以创建效果,例如在页面在其下方滚动时保持固定的菜单。
对于此示例,我们的HTML包含三段文本,以便我们能够滚动页面,以及一个具有position: fixed
属性的框。
<h1>Fixed positioning</h1>
<div class="positioned">Fixed</div>
<p>
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nulla luctus aliquam
dolor, eu lacinia lorem placerat vulputate. Duis felis orci, pulvinar id metus
ut, rutrum luctus orci.
</p>
<p>
Cras porttitor imperdiet nunc, at ultricies tellus laoreet sit amet. Sed
auctor cursus massa at porta. Integer ligula ipsum, tristique sit amet orci
vel, viverra egestas ligula. Curabitur vehicula tellus neque, ac ornare ex
malesuada et.
</p>
<p>
In vitae convallis lacus. Aliquam erat volutpat. Suspendisse ac imperdiet
turpis. Aenean finibus sollicitudin eros pharetra congue. Duis ornare egestas
augue ut luctus. Proin blandit quam nec lacus varius commodo et a urna. Ut id
ornare felis, eget fermentum sapien.
</p>
.positioned {
position: fixed;
top: 30px;
left: 30px;
}
粘性定位
粘性定位是我们可用的最终定位方法。它将相对定位与固定定位混合在一起。当项目具有position: sticky
时,它将在正常流中滚动,直到到达我们定义的视口偏移量。此时,它会变得“粘住”,就像应用了position: fixed
一样。
.positioned {
position: sticky;
top: 30px;
left: 30px;
}
注意:要了解更多关于定位的信息,请参阅我们的定位文章。
表格布局
在查看旧网站上的源代码时,你可能会发现表格已用于布局表单。HTML表格应保留用于显示表格数据。将表格用于表格数据以外的任何用途都有很多问题:表格布局不灵活,标记非常繁重,难以调试,并且语义错误(例如,屏幕阅读器用户难以浏览表格布局)。
当你使用表格标记时,网页上表格的外观是由于一组定义其布局的CSS属性。这些相同的属性也可用于布局不是表格的元素,这种用法有时被称为“使用CSS表格”。下面的示例显示了这种用法。
让我们看一个例子。首先,一些简单的标记创建了一个HTML表单。每个输入元素都有一个标签。我们还在一个段落内包含了一个标题;尽管另一个选择是使用带有<legend>
的<fieldset>
。每个标签/输入对都包装在一个<div>
中,用于布局目的。
<form>
<p>First of all, tell us your name and age.</p>
<div>
<label for="fname">First name:</label>
<input type="text" id="fname" />
</div>
<div>
<label for="lname">Last name:</label>
<input type="text" id="lname" />
</div>
<div>
<label for="age">Age:</label>
<input type="text" id="age" />
</div>
</form>
至于CSS,除了display
属性的使用外,大多数都是相当普通的。<form>
、<div>
s以及<label>
s和<input>
s已被告知像表格、表格行和表格单元格一样显示。基本上,它们将像HTML表格标记一样工作,从而导致标签和输入默认情况下很好地对齐。我们所要做的就是添加一些大小调整、边距等,使一切都看起来更漂亮,我们就完成了。
你会注意到标题段落已被赋予display: table-caption;
,这使其像表格<caption>
一样,并且caption-side: bottom;
告诉标题出于样式目的位于表格底部,即使标记在源代码中位于<input>
元素之前。这允许一定的灵活性。
html {
font-family: sans-serif;
}
form {
display: table;
margin: 0 auto;
}
form div {
display: table-row;
}
form label,
form input {
display: table-cell;
margin-bottom: 10px;
}
form label {
width: 200px;
padding-right: 5%;
text-align: right;
}
form input {
width: 300px;
}
form p {
display: table-caption;
caption-side: bottom;
width: 300px;
color: #999;
font-style: italic;
}
这给了我们以下结果
你也可以在css-tables-example.html上查看此示例(也请查看源代码)。
注意:与本页的其他主题不同,表格布局在本模块中不会进一步介绍。请改用网格布局。
多列布局
多列布局 CSS 模块提供了一种将内容布局成多列的方式,类似于报纸中文字的排列方式。虽然在网页环境中上下阅读多列内容由于用户需要上下滚动而不太实用,但将内容排列成多列仍然可以是一种有用的技巧。
要将一个块元素变成多列容器,我们可以使用 column-count
属性,它告诉浏览器我们想要多少列,或者使用 column-width
属性,它告诉浏览器用尽可能多的指定宽度的列填充容器。
在下面的示例中,我们从一个包含在具有 container
类的 <div>
元素中的 HTML 块开始。
<div class="container">
<h1>Multi-column Layout</h1>
<p>
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nulla luctus
aliquam dolor, eu lacinia lorem placerat vulputate. Duis felis orci,
pulvinar id metus ut, rutrum luctus orci. Cras porttitor imperdiet nunc, at
ultricies tellus laoreet sit amet. Sed auctor cursus massa at porta.
</p>
<p>
Nam vulputate diam nec tempor bibendum. Donec luctus augue eget malesuada
ultrices. Phasellus turpis est, posuere sit amet dapibus ut, facilisis sed
est. Nam id risus quis ante semper consectetur eget aliquam lorem.
</p>
<p>
Vivamus tristique elit dolor, sed pretium metus suscipit vel. Mauris
ultricies lectus sed lobortis finibus. Vivamus eu urna eget velit cursus
viverra quis vestibulum sem. Aliquam tincidunt eget purus in interdum. Cum
sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus
mus.
</p>
</div>
我们在这个容器上使用了 200 像素的 column-width
,导致浏览器创建尽可能多的 200 像素宽的列。列之间剩余的空间将被平均分配。
.container {
column-width: 200px;
}