CSS最大的用处之一就是可以将内容和元素定位到任何我们想要的位置,使我们的设计具有结构,使内容更加易懂。
CSS有好几种不同的定位属性,每种都有自己的使用场景。在这节课中我们会通过不同的案例——可复用的布局和针对单元素的布局——来介绍每种方法。
浮动
定位的其中一种方法就是使用float
属性。float
属性非常好用,可以用在很多地方。
本质来说,float
属性是使元素脱离正常的流式布局,并将它放置在父元素的左侧或右侧。然后页面中所有的元素都会环绕浮动元素布局。例如我们将段落间的一张图片设置为浮动,那么这些段落都会围绕图片换行。
当我们将多个元素同时设置为浮动元素,那么这些元素将呈现相邻或相对布局,如多列布局。
float
有好几个值,最常用的值是left
和right
。使元素浮动到父级元素的左侧或右侧。
img {
float: left;
}
浮动练习
我们先创建一个通用页面,含有页头,页脚,中间有两列。最理想的情况就是在<body>
元素内创建第二节课"了解HTML"中提到的<header>
,<section>
,<aside>
和<footer>
元素。
<header>...</header>
<section>...</section>
<aside>...</aside>
<footer>...</footer>
<section>
和<aside>
元素都是块状元素,它们默认上下布局。但我们实际上想要它们并排显示。所以在此,我们就可以将<section>
元素设为左浮动,将<aside>
元素设为右浮动,使它们呈现彼此相对的两列。
CSS代码如下:
section {
float: left;
}
aside {
float: right;
}
在此提一下,元素设为浮动后,会浮动到父级元素的边缘。如果没有父级元素,那么它会浮动到页面的边缘。
当我们设置一个元素为浮动,我们就将它从HTML文档的正常流式布局中取出,这就导致元素的默认宽度取决于其内容的宽度。有时我们用可复用的布局创建多列的时候并不希望如此,那就可以为列设置一个固定宽度。另外,为了防止浮动元素紧靠在一起,使内容彼此紧贴,可以使用margin
属性来创建元素间的空隙。
如下所示:
section {
float: left;
margin: 0 1.5%;
width: 63%;
}
aside {
float: right;
margin: 0 1.5%;
width: 30%;
}
浮动会改变元素display的状态
有一个重要的点需要记住:将元素设为浮动就是将它从正常的流式布局中脱离,而这会改变元素默认的display
值。float
属性依赖于display
属性为block
的元素。如果一个元素的默认display
值不是block
,那么它会将默认值改成block
。
例如,span
是一个内联元素,它默认的display
值为inline
,width
和height
值是不生效的。然而,如果我们将这个内联元素设置为浮动,那么它的默认display
值会变成block
,这时候width
和height
值就生效了。
当我们设置元素为浮动时,都需要注意display
值的变化。
若只有两列,我们可以将其中一个元素设置为左浮动,另一个元素设置为右浮动,但若是多列的时候就需要换一种方法。例如,我们希望在<header>
和<footer>
元素之间添加一行三列的结构,可以先删掉之前写的<aside>
元素并使用三个<section>
元素,如下所示:
<header>...</header>
<section>...</section>
<section>...</section>
<section>...</section>
<footer>...</footer>
要将三个<section>
元素并排显示,我们不再用之前的左浮动加右浮动的模式,而是将三个元素都设置为左浮动。同时也要为元素设置宽度使其排列更合理。
section {
float: left;
margin: 0 1.5%;
width: 30%;
}
现在呈现在我们眼前的就是三列宽度和外边距一致,都向左浮动的元素。
清除浮动
float
元素最初的设置是为了在内容中插入图片,使内容自然地环绕图片显示。虽然它在图片中使用非常完美,但是float
属性并不是为了定位和布局设计的,所以在这方面存在一些缺陷。
其中一个缺陷就是浮动元素的下一个兄弟元素和父元素的样式可能会渲染异常。当元素浮动时它脱离了正常布局,导致围绕在浮动元素周围的元素的样式都受到了影响。
最常见的就是margin
和padding
属性值渲染异常,因为它们shi与浮动元素融为一体。当然,也有一些其它的样式也受到了影响。
另一个缺陷是有时我们不想内容围绕环绕浮动元素显示。浮动元素会从文件流中脱离,其他元素会完全占有它周围的空间,但很多时候我们并不希望这样。
在上述两个元素并排显示的例子中, 我们将<section>
元素和<aside>
元素设为浮动。在设置元素的width
属性之前,我们可以看到<footer>
元素环绕两个浮动元素显示,占用所有可用空间。如此,<footer>
元素就显示在两个浮动元素之间的凹槽中。如下所示:
为了避免这种情况,我们就需要将浮动元素包裹起来或者清除浮动,使它们回到正常文件流中。接下来,我们先着眼于如何清除浮动,再来介绍怎么包裹浮动元素。
清除浮动
清除浮动需要用到clear
属性,它最常用的属性值是:left
,right
,both
。
div {
clear: left;
}
属性值left
清除左浮动,right
清除右浮动。both
清除同时左浮动和右浮动,这也是我们最推荐使用的属性值。
回到上例中,我们为<footer>
元素添加clear:both
属性就可以清除浮动了。清除浮动可以使页面回归正常文件流,所以非常重要。
footer {
clear: both;
}
包裹浮动元素
除了清除浮动,还可以包裹浮动元素。这两种方式页面呈现的效果是差不多的,但包裹元素的方式还有助于确保样式都渲染正常。
包裹元素就是将浮动元素置于父元素中,父元素作为容器会置于正常的文件流中。我们为父元素添加一个class
属性group
,并为其设置CSS:
.group:before,
.group:after {
content: "";
display: table;
}
.group:after {
clear: both;
}
.group {
clear: both;
*zoom: 1;
}
上述写了很多代码,但实际上就是为class值为group
元素中的浮动元素清除浮动,使其回归正常文件流。
再具体点,代码中使用了在第四节中有提到过的:before
和:after
两个伪类,它们在class为group
的元素前后分别添加了一个动态元素。这些元素不包含任何内容,并以与块状元素block
很相似的表格元素table
的形式呈现。动态生成的元素:after
清除了class为group
的元素内的元素浮动。在.group
中设置的清除浮动则清除了排版在它之上的兄弟元素的浮动。老版本的浏览器也可以很好的兼容。
clear:both
单独声明会很合适。
回到示例,我们可以将<section>
和<aside>
元素放置在一个父元素内,如下所示:
<header>...</header>
<div class="group">
<section>...</section>
<aside>...</aside>
</div>
<footer>...</footer>
.group:before,
.group:after {
content: "";
display: table;
}
.group:after {
clear: both;
}
.group {
clear: both;
*zoom: 1;
}
section {
float: left;
margin: 0 1.5%;
width: 63%;
}
aside {
float: right;
margin: 0 1.5%;
width: 30%;
}
这种包裹元素清除浮动的方式被称为clearfix
,我们可以很容易在其它网站中找到定义它的class名clearfix
或cf
。我们示例中使用了group
,它代表一组元素,更好地表达了内容的意义。
设置浮动元素时我们要时刻注意它是否影响了页面的布局,并确认是否已根据需要清除浮动。不清除浮动会导致很多头疼的问题,尤其当页面多行多列排版的时候。
练习
接下来,我们打开“样式讨论会”网站来尝试将一部分内容浮动。
- 在设置元素浮动之前,我们先在
main.css
中的grid样式下添加清除浮动的样式group
,如下所示:
/*
========================================
Clearfix
========================================
*/
.group:before,
.group:after {
content: "";
display: table;
}
.group:after {
clear: both;
}
.group {
clear: both;
*zoom: 1;
}
-现在我们要将<header>
元素内的<h1>
设置为左浮动,其他元素环绕这个元素显示。
先为<h1>
添加一个class属性,值为logo
,并在CSS中将其设置为左浮动:
<h1 class="logo">
<a href="index.html">Styles Conference</a>
</h1>
/*
========================================
Primary header
========================================
*/
.logo {
float: left;
}
- 这步完成之后,我们为logo添加更多细节。先在单词"Style"和"Conference"之间添加一个
<br/>
元素,使两个单词分两行显示。
在CSS中我们为logo添加上边框和纵向的padding
。
<h1 class="logo">
<a href="index.html">Styles <br> Conference</a>
</h1>
.logo {
border-top: 4px solid #648880;
padding: 40px 0 22px 0;
float: left;
}
- 由于我们将
<h1>
设置为浮动,那么就需要在最靠近它的父级元素<header>
上添加class属性值group
来清除浮动:
<header class="container group">
...
</header>
-
<header>
元素上的操作已经完成,接着我们来处理<footer>
元素。与<header>
元素设置相似,我们将包含版权信息的<small>
设置为左浮动,其他元素围绕这个元素显示。
不过与<header>
不同的是,我们不直接在浮动元素上添加class属性设置浮动。我们先为其父元素添加一个class属性值,并且使用这唯一的选择器选中元素设置浮动。首先在<footer>
上添加class属性值primary-footer
。由于我们已事先知道了要将<footer>
元素中的某个元素设为浮动,所以在<footer>
中添加class属性值group
。
<footer class="primary-footer container group">
...
</footer>
- 现在我们已在
<footer>
上添加了class属性值primary-footer
。我们可以以其为预选择器选中<small>
元素并添加左浮动。如下所示:
/*
========================================
Primary footer
========================================
*/
.primary-footer small {
float: left;
}
- 上述代码中我们选中的是class属性值为
primary-footer
的元素内的<small>
元素,例子中对应的就是<footer>
元素。 - 最后一步,我们为
<footer>
元素设置纵向padding
,使<footer>
的内容与其它内容分离。我们可以直接在primary-footer
上添加样式:
.primary-footer {
padding-bottom: 44px;
padding-top: 44px;
}
- 完成了
<header>
和<footer>
的设置之后,我们要将这些修改应用到每个页面中。
Inline-Block
除了浮动,我们也可以通过display:inline-block
来布局定位。它主要是帮助我们做页面布局或同行元素间的的布局。
在此回顾一下,display:inline-block
使元素同行显示外,接受所有盒子模型的属性,包括height
,width
,padding
,border
和margin
。inline-block
可以充分使用盒子模型的所有属性,并且不需要清除浮动。
Inline-Block练习
我们来看上述三列并排显示的例子,HTML结构如下:
<header>...</header>
<section>...</section>
<section>...</section>
<section>...</section>
<footer>...</footer>
之前我们为<section>
元素设置了浮动,现在我们以display:inline-block
来代替,并保留之前设置的margin
和width
。 CSS呈现如下:
section {
display: inline-block;
margin: 0 1.5%;
width: 30%;
}
此时,代码并没有很好的解决问题,最后一个<section>
元素掉到了下一行。行内块元素是行内一个接着一个布局,但它们之间有一个空格。当我们将空格占用的空间和所有元素的width
、margin
相加,那么占用宽度会非常大,会将最后一个<section>
挤到下一行。为了让所有<section>
显示在同一行,我们就需要把这些空格移除。
移除inline-block
元素之间的空格
有好几种方法可以移除掉空格,但有些方法会相对复杂一些。我们将介绍两种最简单的方法,都是应用在HTML中的。
第一中解决方案是将<section>
的开始标签和前一个<section>
的结束标签写在同一行。一行内的代码以开始标签为结尾,如下所示:
<header>...</header>
<section>
...
</section><section>
...
</section><section>
...
</section>
<footer>...</footer>
用这种方法要确保HTML在书写时元素之间时没有空格,那么最终渲染时页面也不会呈现出空格。
另一种方法是在元素之间添加注释。结束标签和开始标签之间以注释连接,注释必须紧跟元素。这么写可以允许HTML元素在书写时换行,代码如下:
<header>...</header>
<section>
...
</section><!--
--><section>
...
</section><!--
--><section>
...
</section>
<footer>...</footer>
这两种方法没有一种是非常完美的,但它们都很好用。我(作者)更倾向于添加注释的方案,因为它更利于排版,但你完全可以根据你的喜好来选择。
创建可复用的布局
在构建网站时,编写可复用的模块是非常有益的,而可复用的布局就是可复用代码中非常重要的一部分。布局可以使用float
或者inline-block
元素的方法实现,但那一种方法更好呢?
这是值得商榷。我(作者)的做法是使用inline-block
元素实现页面的网格或布局,而当我想让某部分内容环绕某个元素显示时会使用float
(float
本来是用于处理图片的)。而且我也发现inline-block
元素的布局方式更简单,更易处理。
和前面说的一样,你可以根据自己的需要选择最合适的方案,如果你对其中一种方法更满意,那你就可以选择它。
目前,新的CSS规范提到了flex
和grid-
开头的属性——它们有助于实现页面的最佳布局。要时常关注留意这些新的方法。
练习
对可复用的布局有所了解之后,我们将其实施到我们的Styles Conference
网站。
- 我们将使用
inline-block
元素来实现一个一排三列的可复用布局。可以将其分为等宽的三列,或者分为两列,一列占2/3宽,一列占1/3宽。
首先,我们使用class
选择器定义列宽width
,在main.css
文件中定义占1/3宽的选择器col-1-3
,占2/3宽的选择器col-2-3
。如下所示:
.col-1-3 {
width: 33.33%;
}
.col-2-3 {
width: 66.66%;
}
- 我们想使这些列都以
inline-block
元素的方式呈现,我们就需要确保它们垂直对齐为顶部对齐。
代码如下所示,创建由两个选择器共享的样式:
.col-1-3,
.col-2-3 {
display: inline-block;
vertical-align: top;
}
- 这段css代码中,
col-1-3
,col-2-3
之间用逗号,
隔开。逗号表示第一个选择器结尾,后面跟着第二个选择器。第二个选择器后跟随大括号{
标识样式开始申明。用逗号隔开选择器可以让共用的样式同时绑定到多个选择器上。 - 接下来我们想在每列之间存设置空隙,使内容隔开。我们可以在选择器中添加横向的
padding
。
设置之后我们可以看到,当两列元素挨着排列时,它们之间的空隙会是行开始和末尾空隙的两倍,为了使空隙一致,我们使用一个元素将这些列包裹起来,并为它设置相同的padding
属性。 - 我们为这个包裹元素设置一个名为
grid
的class
选择器,将其添加到共用的padding
定义中。代码如下所示,选择器之前同样用逗号隔开:
.grid,
.col-1-3,
.col-2-3 {
padding-left: 15px;
padding-right: 15px;
}
- 当我们设置横向
padding
后,我们需要小心。上节课中,我们创建了一个宽度为960px
,class名为container
的元素,并相对于页面左右居中。现在如果我们将grid
元素嵌入container
元素中,那么它们的横向padding
就会相加,我们的列与其他的部分呈现出的宽度会不一致。
我们不希望发生这样的事,所以我们要让container
和grid
选择器共享一部分样式。具体的来说,就是共享宽度width
(确保我们的页面固定在960px
宽)和外边距margin
(使grid
元素居中),代码如下所示:
.container,
.grid {
margin: 0 auto;
width: 960px;
}
.container {
padding-left: 30px;
padding-right: 30px;
}
- 现在每个
class
属性包含有container
或者grid
的元素宽度都为960px
,并相对于页面居中。此外,我们也将container
选择器的横向padding
分离写到了另一个样式集中。 - 到目前为止,所有繁重的可复用网格布局的样式已经写完了。现在我们要将其添加到HTML中,看看它们呈现的结果。
我们从index.html
主页的三个宣传栏开始,将它们设置为三列。现在它们由一个class名为container
的<section>
包裹。我们要将这个container
替换为grid
,如下所示:
<section class="grid">
...
</section>
- 随后我们将
class
属性col-1-3
添加到每个gird
元素内的<section>
元素上:
<section class="grid">
<section class="col-1-3">
...
</section>
<section class="col-1-3">
...
</section>
<section class="col-1-3">
...
</section>
</section>
- 最后,因为我们的列都是
inline-block
元素,我们要确保我们移除了它们之间的间隙。我们用注释来完成这一工作,并在每块中添加一些模块说明,以便更好的组织我们的代码。
<section class="grid">
<!-- Speakers -->
<section class="col-1-3">
...
</section><!--
Schedule
--><section class="col-1-3">
...
</section><!--
Venue
--><section class="col-1-3">
...
</section>
</section>
- 上述代码中,我们在第3行的中添加了用以标识的注释“Speakers”第7行我们在结束标签
<\section>
后紧跟了注释,在第9行添加了用以标识的注释“Schedule”,并在11行开始标签<section>
前闭合了注释。相同的注释结构贯穿第13行和第17行,标识内容替换为Venue
。总的来说我们已经将所有的间隙都注释掉了并为每一块<section>
添加了标识。
现在我们有了可复用的三列网格,可以使用1/3宽的列和2/3宽的列。主页中我们用它来布局了宣传栏,最终结果如图所示:
演示源代码
这是练习的源代码。点击下载
独特的定位元素
我们常常想要定位一个元素,但float
和inline-block
并不能满足这种需求。浮动将元素从正常的文档流中移除,经常出现我们不希望得到的环绕浮动元素显示的布局。inline-block
元素,除非我们创建多列,否则不易实现我们想要的定位。针对这些情况,我们可以使用position
属性来做位置偏移。
position
属性表示元素应该怎样定位在页面中,是否按正常文档流显示。这需要结合盒子的位移属性——top
,right
,bottom
和left
——通过定义位移值设置不同方向的位p移来定位元素在什么位置显示。
position
属性的默认值为static
,他表示元素以正常文档流呈现,并且不接受盒子位移属性。static
常常会被relative
和absolute
这两个值复写。这就是我们接下来要讲的内容。
相对定位
position:relative
在允许元素以正常文档流呈现,保留元素的空间不被其他元素占用外,也允许元素通过修改位移属性来定位,如下所示:
<div>...</div>
<div class="offset">...</div>
<div>...</div>
div {
height: 100px;
width: 100px;
}
.offset {
left: 20px;
position: relative;
top: 20px;
}
例子中第二个<div>
元素设置了class属性为offset
,offset
选择器设置了position:relative
和两个位移属性left
和top
。这保留了元素原来的位置,不会被其他元素占用。另外位移属性重新定位了元素的位置,使它向右偏移20px
,向下偏移20px
。
使用相对定位有一个重要的点要知道:盒子位移属性是根据元素本身的位置来进行位移的。所以设置left:20px
就是将元素从左向右位移20px
。top:20px
就是元素至上向下位移20px
。
当使用position
对元素进位移时,该元素会与其他元素重叠,而不是将其他元素像使用了margin
和padding
一样移到下面。
绝对定位
position:absolute
为绝对定位,它与相对定位不同,绝对定位脱离了文档流,而它原有的位置不会被保留。
另外,绝对定位的元素位移与离他最近的设置了相对定位的父级元素有关。如果没有相对定位的父级元素,那么绝对定位会根据<body>
来定位(译者:根据我的实践,浏览器在绝对定位的元素没有相对定位的父级元素时,并不是根据<body>定位,而是通过视窗的位置定位,有兴趣的读者可以尝试一下)
。这里的信息量比较多,我们通过例子来看一下:
HTML
<section>
<div class="offset">...</div>
</section>
section {
position: relative;
}
div {
position: absolute;
right: 20px;
top: 20px;
}
在这个例子中,<section>
元素是不包含位移属性的相对定位的元素,所以它没有位移。div
元素设置了包含position: absolute
的class
属性offset
。由于<section>
是离它最近的设置了相对定位的父级元素,所以div
依赖<section>
元素的位置进行偏移
由于设置了right
和top
位移,<div>
根据<section>
元素从右向左位移20px
,从上向下位移20px
。
由于<div>
元素是绝对定位,它不在页面的正常文档流中,会与它周围的元素重叠。另外,<div>
元素原来的位置也没有被保留,其他元素可以占据这个位置。
通常情况下,大多数的定位都不需要通过定位和位移属性来处理,但在某些情况下它们确实非常有用。
总结
学习如何通过HTML和CSS来定位内容是掌握这两门语言非常重要的一步。再加上盒子模型,我们正走在成为前端开发者的路上。
回顾一下,在这节课中我们所碰到的知识点:
- 什么是浮动,怎么通过浮动定位内容
- 怎么清除和包裹浮动元素。
- 怎么通过
inline-block
元素定位内容。 - 怎么移除
inline-block
元素间的间隙。 - 怎么使用相对定位和绝对定位定位内容。
我们每节课都在学习新的技能,所以让我们继续。接下来,排版!