概述
在 CSS 中,所有的元素都被一个个的盒子(box)包围着,本文讲述的是如何把你的盒子放在与视口相关的正确位置上。CSS 页面布局技术允许我们拾取网页中的元素,并且控制它们相对正常布局流、周边元素、父容器或者主视口/窗口的位置。
正常布局流
正常布局流 (normal flow) 是指在不对页面进行任何布局控制时,浏览器默认的 HTML 布局方式。
在HTML文档中,默认情况下元素的位置根据它前面正常渲染的元素位置进行计算。元素常见类型分为块级元素(block element)、行内元素(inline element)、行内块元素(inline-block element)等,除了特殊的比如flex规则外,HTML文档将元素根据这些类型从上到下、从左到右地排布,我们把文档将默认元素一一排开的过程形象地想象成一种流体,于是就把这个称之为文档流也就是正常布局流。
定位技术
定位 (positioning) 能够让我们把一个元素从它原本在正常布局流 (normal flow) 中应该在的位置移动到另一个位置。定位 (positioning) 并不是一种用来给你做主要页面布局的方式,它更像是让你去微调页面中的元素到特定位置的技术。
有一些非常有用的技术在特定的布局下依赖于position属性。同时,理解定位 (positioning) 也能够帮助你理解正常布局流 (normal flow),理解把一个元素移出正常布局流 (normal flow) 是怎么一回事。
有五种主要的定位类型需要我们了解:
- 静态定位(Static )是每个元素默认的属性——它表示“将元素放在文档布局流的默认位置——没有什么特殊的地方”。
- 相对定位(Relative )允许我们相对于元素在正常的文档流中的位置移动它——包括将两个元素叠放在页面上。这对于微调和精准设计(design pinpointing)非常有用。
- 绝对定位(Absolute )将元素完全从页面的正常布局流(normal layout flow)中移出,类似将它单独放在一个图层中。默认元素相对于页面的
<html>元素边缘固定,我们也可以将元素相对于该元素的最近的非static定位的祖先元素。绝对定位在创建复杂布局效果时非常有用,例如通过按钮控制滑动到屏幕中的信息面板。 - 固定定位(Fixed )与绝对定位非常类似,但是它是将一个元素相对浏览器视口固定,而不是相对另外一个元素。这在创建类似在整个页面滚动过程中总是处于屏幕的某个位置的导航菜单时非常有用。
- 粘性定位(Sticky )是一种新的定位方式,它会让元素先保持和
position: static一样的定位,当它的相对视口位置(offset from the viewport)达到某一个预设值时,它就会像position: fixed一样定位。
将postion设为absolute或fixed,都会使得元素脱离文档流。
display 属性
在 css 中实现页面布局的主要方法是设定display属性的值。此属性允许我们更改默认的显示方式。正常流中的所有内容都有一个display的值,用作元素的默认行为方式。例如,段落显示在一个段落的下面,这是因为它们的样式是display:block。如果在段落中的某个文本周围创建链接,则该链接将与文本的其余部分保持内联,并且不会打断到新行。这是因为a元素默认为display:inline。
您可以更改此默认显示行为。例如,li元素默认为display:block,这意味着在我们的文档中,列表项显示为一个在另一个之下。如果我们将显示值更改为inline,它们现在将显示在彼此旁边,就像单词在句子中所做的那样。事实上,您可以更改任何元素的display值
除了可以通过将一些内容从block转换为inline(反之亦然)来更改默认表示形式之外,还有一些更大的布局方法以display值开始。但是,在使用这些属性时,通常需要调用其他属性。在讨论布局时,对我们来说最重要的两个值是 display: flex 和 display: grid。
弹性盒子
Flexbox 是 CSS 弹性盒子布局模块(Flexible Box Layout Module)的缩写,它被专门设计出来用于创建横向或是纵向的一维页面布局。要使用 flexbox,你只需要在想要进行 flex 布局的父元素上应用display: flex ,所有直接子元素都将会按照 flex 进行布局。
flex布局改变了默认的流式布局。flex不区分元素是否是块级元素还是行内元素。 默认情况, flex布局总是按照主轴方向,(默认为从左到右)安排各个元素。当且仅当一行排不下的时候,才会另起一行。
长久以来,CSS 布局中唯一可靠且跨浏览器兼容的创建工具只有 floats 和 positioning。这两个工具大部分情况下都很好使,但是在某些方面它们具有一定的局限性,让人难以完成任务。
以下简单的布局需求是难以或不可能用这样的工具( floats 和 positioning)方便且灵活的实现的:
- 在父内容里面垂直居中一个块内容。
- 使容器的所有子项占用等量的可用宽度/高度,而不管有多少宽度/高度可用。
- 使多列布局中的所有列采用相同的高度,即使它们包含的内容量不同。
flex 模型说明
当元素表现为 flex 框时,它们沿着两个轴来布局:
- 主轴(main axis)是沿着 flex 元素放置的方向延伸的轴(比如页面上的横向的行、纵向的列)。该轴的开始和结束被称为 main start 和 main end。
- 交叉轴(cross axis)是垂直于 flex 元素放置方向的轴。该轴的开始和结束被称为 cross start 和 cross end。
- 设置了
display: flex的父元素被称之为 flex 容器(flex container)。 - 在 flex 容器中的元素被称之为 flex 项(flex item)。
我们来看一个例子:
下面这些 HTML 标记描述了一个 class 为wrapper的容器元素,它的内部有三个<div>元素。它们在我们的英文文档当中,会默认地作为块元素从上到下进行显示。
现在,当我们把display: flex添加到它的父元素时,这三个元素就自动按列进行排列。这是由于它们变成了flex 项 (flex items),按照 flex 容器(也就是它们的父元素)的一些 flex 相关的初值进行 flex 布局:它们整整齐齐排成一行,是因为父元素上flex-direction的初值是row。它们全都被拉伸至和最高的元素高度相同,是因为父元素上align-items属性的初值是stretch。这就意味着所有的子元素都会被拉伸到它们的 flex 容器的高度,在这个案例里就是所有 flex 项中最高的一项。所有项目都从容器的开始位置进行排列,排列成一行后,在尾部留下一片空白。
.wrapper {
display: flex;
}
<div class="wrapper">
<div class="box1">One</div>
<div class="box2">Two</div>
<div class="box3">Three</div>
</div>
设置 flex 属性
除了上述可以被应用到 flex 容器的属性以外,还有很多属性可以被应用到 flex 项 (flex items) 上面。这些属性可以改变 flex 项在 flex 布局中占用宽/高的方式,允许它们通过伸缩来适应可用空间。
- flex-grow 属性用于设置弹性盒子的扩展比率
- flex-shrink 属性 指定了 flex 元素的收缩规则。flex 元素仅在默认宽度之和大于容器的时候才会发生收缩,其收缩的大小是依据 flex-shrink 的值。
- flex-basis 属性用于设置弹性盒子伸缩基准值。
推荐一篇讲解这三个属性比较好的文章:深入理解flex布局的flex-grow、flex-shrink、flex-basis
水平和垂直对齐
align-items控制 flex 项在交叉轴上的位置。
默认的值是 stretch,其会使所有 flex 项沿着交叉轴的方向拉伸以填充父容器。如果父容器在交叉轴方向上没有固定宽度(即高度),则所有 flex 项将变得与最长的 flex 项一样长(即高度保持一致)。
justify-content 控制 flex 项在主轴上的位置。
- 默认值是
flex-start,这会使所有 flex 项都位于主轴的开始处。 - 你也可以用
flex-end来让 flex 项到结尾处。 center在justify-content里也是可用的,可以让 flex 项在主轴居中。- 而我们上面用到的值
space-around是很有用的——它会使所有 flex 项沿着主轴均匀地分布,在任意一端都会留有一点空间。 - 还有一个值是
space-between,它和space-around非常相似,只是它不会在两端留下任何空间。
flex 项排序
弹性盒子也有可以改变 flex 项的布局位置的功能,而不会影响到原顺序(即 dom 树里元素的顺序)。这也是传统布局方式很难做到的一点。
代码也很简单,将下面的 CSS 添加到示例代码下面。
button:first-child {
order: 1;
}
刷新下,然后你会看到 "Smile" 按钮移动到了主轴的末尾。下面我们谈下它实现的一些细节:
- 所有 flex 项默认的
order值是 0。 - order 值大的 flex 项比 order 值小的在显示顺序中更靠后。
- 相同 order 值的 flex 项按原顺序显示。所以假如你有四个元素,其 order 值分别是 2,1,1 和 0,那么它们的显示顺序就分别是第四,第二,第三,和第一。
- 第三个元素显示在第二个后面是因为它们的 order 值一样,且第三个元素在原顺序中排在第二个后面。
flex 嵌套
弹性盒子也能创建一些颇为复杂的布局。设置一个元素为 flex 项目,那么他同样成为一个 flex 容器,它的孩子 (直接子节点) 也表现为 flexible box。
浮动
把一个元素“浮动”(float) 起来,会改变该元素本身和在正常布局流(normal flow)中跟随它的其他元素的行为。这一元素会浮动到左侧或右侧,并且从正常布局流 (normal flow) 中移除,这时候其他的周围内容就会在这个被设置浮动 (float) 的元素周围环绕。
要将一个元素设置为浮动元素,用float 属性即可,它有四个可能的值:
left— 将元素浮动到左侧。right— 将元素浮动到右侧。none— 默认值,不浮动。inherit— 继承父元素的浮动属性。
在下面这个例子当中,我们把一个<div>元素浮动到左侧,并且给了他一个右侧的margin把文字推开。这给了我们文字环绕着这个<div>元素的效果。
<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;
}
清除浮动
通过前面我们知道一个浮动元素会被移出正常文档流,且其他元素会显示在它的周围。如果我们不想让剩余元素也受到浮动元素的影响,我们需要 停止浮动它;这是通过添加 clear 属性实现的。
向 CSS 文件中添加以下样式:
.cleared {
clear: left;
}
.cleared元素不会再跟随浮动元素排布了。clear 属性接受下列值:
left:停止任何活动的左浮动right:停止任何活动的右浮动both:停止任何活动的左右浮动
清除浮动元素周围的盒子
先看下问题所在:
改变你的文档结构,使得第一个段落与浮动的盒子共同处于类名为 wrapper 的 div 元素之下。
<body>
<div class="wrapper">
<div class="box">Float</div>
<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nulla luctus aliquam dolor, eu lacinia lorem placerat vulputate.</p>
</div>
<p>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>
<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. 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>
</body>
在你的 CSS 代码中,为 .wrapper 类添加如下规则并重载页面:
.wrapper {
background-color: rgb(79,185,227);
padding: 10px;
color: #fff;
}
如果你想让盒子联合包住浮动的项目以及第一段文字,同时让紧随其后的内容从盒子中清除浮动,这就是一个问题。
有三种方法可以处理这个问题,其中的两种在所有浏览器中均可以奏效(虽然看上去有点“小技巧”),剩下的一种是可以处理问题的较新的解决方案。
- 向包含浮动内容及其本身的盒子后方插入一些生成的内容,并将生成的内容清除浮动。
.wrapper::after {
content: "";
clear: both;
display: block;
}
这与在浮动盒子后手动添加诸如 div 的 HTML 元素,并设置其样式为 clear:both 是等效的。
-
在包裹元素上添加
overflow: auto规则.wrapper { background-color: rgb(79,185,227); padding: 10px; color: #fff; overflow: auto; }
这个之所以能够生效,是因为创建了所谓的 块格式化上下文(BFC)。可以把它看作页面内部包含所需元素的一小块布局区域。如此设置可以让浮动元素包含在 BFC 及其背景之内。大部分情况下这种小技巧都可以奏效,但是可能会出现莫名其妙的滚动条或裁剪阴影,这是使用 overflow 带来的一些副作用。
-
使用
display属性的flow-root值。它可以无需小技巧来创建块格式化上下文(BFC),在使用上没有副作用。.wrapper { background-color: rgb(79,185,227); padding: 10px; color: #fff; display: flow-root; }