一般而言,一个静态web页面的呈现需要通过html和css配合实现。html相当于页面的骨架,规定了文档的结构。css相当于皮肤,使得页面能够按一定的方式进行呈现。
- HTML使用语义化的标签,规定了页面的结构:页面里面有什么元素,以及各个元素之间的关系。元素之间的关系包括在文档流中的先后关系,以及嵌套关系(DOM树中的父子)等等。
- css规定了页面元素的呈现,如文字的对齐方式,字体,块的背景色等等。此外,css的一个重要作用是实现页面的布局。
目前,web页面的布局是通过css实现的。css实现页面的布局也有两种主要方式:
- 传统布局:使用float属性以及position定位进行布局,局限性在于难以实现垂直居中等效果。
- flex布局:一种简单灵活的布局方式,适合创建针对屏幕尺寸不同的设备的响应式页面,但是不适合于很复杂的布局。
本文使用百度前端学院的两个题目作为实例,简单描述这两种布局方式的使用,并对它们做一个简单的比较。
1. 传统布局
1.1 传统的布局要素
传统布局主要使用了两个要素:浮动(float)和定位(position)。
1.1.1 浮动
浮动的框可以向左或向右移动,直到它的外边缘碰到包含框或另一个浮动框的边框为止。由于浮动框不在文档的普通流中,所以文档的普通流中的块框表现得就像浮动框不存在一样。但是浮动框可能会覆盖住普通流中的框,布局的时候需要对普通流中的元素设置一定的外边距,避免被浮动的框盖住。
使用浮动的方法很简单,只需要在需要浮动的元素的css里面作如下设置:
float : left / right ;
浮动会带来一个副作用:因为浮动元素脱离了文档流,所以包围图片和文本的 div 不占据空间,导致无法撑开包含浮动的元素(如div)。解决该问题的一个方案是在包含浮动元素的元素最后面加上一个空的div,该div的clear属性设为"both"。
1.1.2 定位
定位的基本思想很简单,它允许你定义元素框相对于其正常位置应该出现的位置,或者相对于父元素、另一个元素甚至浏览器窗口本身的位置。
CSS 有三种基本的定位机制:普通流、浮动和绝对定位。除非专门指定,否则所有框都在普通流中定位。也就是说,普通流中的元素的位置由元素在 HTML 文档中的位置决定。
通过使用position属性,我们可以选择 4 种不同类型的定位:
- static:元素框正常生成。块级元素生成一个矩形框,作为文档流的一部分,行内元素则会创建一个或多个行框,置于其父元素中。
- relative:元素框偏移某个距离。元素仍保持其未定位前的形状,它原本所占的空间仍保留。
- absolute:元素框从文档流完全删除,并相对于其包含块定位。包含块可能是文档中的另一个元素或者是初始包含块。元素原先在正常文档流中所占的空间会关闭,就好像元素原来不存在一样。元素定位后生成一个块级框,而不论原来它在正常流中生成何种类型的框。
- fixed:元素框的表现类似于将 position 设置为 absolute,不过其包含块是视窗本身。
提示:相对定位实际上被看作普通流定位模型的一部分,因为元素的位置相对于它在普通流中的位置。
注意:行框和行内框不是一回事,行框的高度总是能包括它包含的所有行内框。
1.2 传统布局方式实例
1.2.1案例一
这是百度前端学院的一道题目:
任务描述
- 使用 HTML 与 CSS 按照 示例图(点击查看) 实现三栏式布局。
- 左右两栏宽度固定,中间一栏根据父元素宽度填充满,最外面的框应理解为浏览器。背景色为 #eee 区域的高度取决于三个子元素中最高的高度。
任务注意事项 - 尝试 position 和 float 的效果,思考它们的异同和应用场景。
- 注意测试不同情况,尤其是极端情况下的效果。
- 图片和文字内容请自行替换,尽可能体现团队的特色。
- 调节浏览器宽度,固定宽度和自适应宽度的效果始终符合预期。
- 改变中间一栏的内容长度,以确保在中间一栏较高和右边一栏较高时,父元素的高度始终为子元素中最高的高度。
- 其他效果图中给出的标识均被正确地实现。
要实现的效果如图所示:
实现此效果的代码如下:
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<title>This is the title~~</title>
<style>
body
{
margin:0;
padding:0;
}
.container
{
margin:20px;
padding:20px;
background-color:#eee;
border:2px solid #999;
}
.left
{
margin:0;
padding:20px;
background-color: white;
border:2px solid #999;
width:160px;
float:left;
}
.right
{
margin:0;
padding:0 20px;
background-color: white;
border:2px solid #999;
width:80px;
float:right;
}
.middle
{
margin:0 140px 0 220px;
padding:20px;
background-color: white;
border:2px solid #999;
}
.img
{
margin:0;
float:left;
text-align: center;
background-color:gray;
color:white;
width:80px;
height:80px;
}
.right-img
{
margin:20px 0 20px 0;
float:left;
text-align: center;
background-color:gray;
color:white;
width:80px;
height:80px;
}
.left > p
{
float:left;
margin:0 20px;
}
.clear
{
clear:both;
}
</style>
</head>
<body>
<div class="container">
<div class="left">
<div class="img">
<p>logo</p>
</div>
<p>队名</p>
<div class="clear"></div>
</div>
<div class="right">
<div class="right-img">
<div>
<p>王尼玛的logo</p>
</div>
</div>
<div class="right-img">
<div>
<p>王尼玛的logo</p>
</div>
</div>
<div class="right-img">
<div>
<p>王尼玛的logo</p>
</div>
</div>
<div class="right-img">
<div>
<p>王尼玛的logo</p>
</div>
</div>
<div class="clear"></div>
</div>
<div class="middle">
<p>前端工程师的职责是制作标准优化的代码,并增加交互动态功能,开发JavaScript以及Flash模块,同时结合后台开发技术模拟整体效果,进行丰富互联网的Web开发,致力于通过技术改善用户体验。前端工程师,属于IT技术职业的一种,是近5年发展起来的职业,旧的体系将其定义为Web前端工程师,主要的技术包含:HTML、JavaScript、CSS。但IT技术属于变化比较快的领域,最近发生了很大的变革,新的体系下,前端工程师技术又增加了:nodejs、Hybrid App。Node.js是一个Javascript运行环境(runtime)。实际上它是对Google V8引擎进行了封装。V8引 擎执行Javascript的速度非常快,性能非常好。Node.js对一些特殊用例进行了优化,提供了替代的API,使得V8在非浏览器环境下运行得更好。Hybrid App(混合模式移动应用)是指介于web-app、native-app这两者之间的app,兼具“Native App良好用户交互体验的优势”和“Web App跨平台开发的优势”。主要采用JavaScript语言作为中间语言开发。因此,前端工程师所涵盖的职责范围,已经包含后端、跨平台等多种职能,不能简单地认为只是Web前端工程师。</p>
</div>
<div class="clear"></div>
</div>
</body>
</html>
1.2.1案例二
这是百度前端学院的另一道题目,用绝对定位来实现布局。值得注意的是,绝对定位的位置值是相对于最近的已定位的祖先元素,而不一定是body本身。
任务描述
- 实现如 示例图(点击打开) 的效果
- 灰色元素水平垂直居中,有两个四分之一圆位于其左上角和右下角。
任务注意事项 - 思考不同情况下(如灰色高度是根据内容动态变化的)水平垂直居中的解决方案。
- 动手试一试各种情况的组合,父元素和子元素分别取不同的 position 值。思考 position 属性各种取值的真正含义,尤其是 absolute 究竟是相对谁而言的。
- 注意测试不同情况,尤其是极端情况下的效果。
- 调节浏览器宽度,灰色元素始终水平居中。
- 调节浏览器高度,灰色元素始终垂直居中。
- 调节浏览器高度和宽度,黄色扇形的定位始终准确。
要实现的效果如图所示:
实现此效果的代码如下:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Task1-4 demo</title>
<style>
body
{
margin:0;
padding:0;
}
.parent
{
margin:-100px 0 0 -200px;
padding:0;
width:400px;
height:200px;
background-color:lightgray;
border:2px solid gray;
position:absolute;
left:50%;
top:50%;
}
.left
{
margin:0;
padding:0;
width:50px;
height:50px;
background-color:yellow;
border:2px solid yellow;
border-bottom-right-radius:50px;
position:absolute;
top:0;
left:0;
}
.right
{
margin:0;
padding:0;
width:50px;
height:50px;
background-color:yellow;
border:2px solid yellow;
border-top-left-radius:50px;
position:absolute;
top:150px;
left:350px;
}
</style>
</head>
<body>
<div class="parent">
<div class="left"></div>
<div class="right"></div>
</div>
</body>
</html>
2. flex布局
2.1 flex布局要素
因为Flexbox是整个模块,而不是一个属性,它涉及很多东西,包括其整个组属性。他们当中一部分是容器(父元素,称为“伸缩容器”),另一部分是子元素(称为“伸缩项目”)。
常规布局是基于块和内联流方向,而Flex布局是基于flex-flow流。请看看来自w3c规范中的这张图,解释了flex布局的主要思想。
根据伸缩项目排列方式不同,主轴和侧轴方向也有所变化
基本上,伸缩项目是沿着主轴(main axis),从主轴起点(main-start)到主轴终点(main-end)或者沿着侧轴(cross axis),从侧轴起点(cross-start)到侧轴终点(cross-end)排列。
主轴(main axis):伸缩容器的主轴,伸缩项目主要沿着这条轴进行排列布局。小心,它不一定是水平的;这主要取决于“justify-content”属性(详细见下文)。
主轴起点(main-start)和主轴终点(main-end):伸缩项目放置在伸缩容器内从主轴起点(main-start)向主轴终点(main-start)方向。
主轴尺寸(main size):伸缩项目在主轴方向的宽度或高度就是主轴的尺寸。伸缩项目主要的大小属性要么是宽度,要么是高度属性,由哪一个对着主轴方向决定。
侧轴(cross axis):垂直于主轴称为侧轴。它的方向主要取决于主轴方向。
侧轴起点(cross-start)和侧轴终点(cross-end):伸缩行的配置从容器的侧轴起点边开始,往侧轴终点边结束。
侧轴尺寸(cross size):伸缩项目的在侧轴方向的宽度或高度就是项目的侧轴长度,伸缩项目的侧轴长度属性是「width」或「height」属性,由哪一个对着侧轴方向决定。
2.2 flex布局语法
分伸缩容器和伸缩项目两部分进行介绍。
2.1.1 伸缩容器属性
display:flex | inline-flex(还可以取none/block/inline等值,此时用途就不是flex布局)
这个是用来定义伸缩容器,是内联还是块取决于设置的值。这个时候,他的所有子元素将变成flex文档流,称为伸缩项目。flex-direction:row | row-reverse | column | column-reverse
这个主要用来创建主轴,从而定义了伸缩项目放置在伸缩容器的方向。row(默认值):在“ltr”排版方式下从左向右排列;在“rtl”排版方式下从右向左排列。
row-reverse:与row排列方向相反,在“ltr”排版方式下从右向左排列;在“rtl”排版方式下从左向右排列。
column:类似 于row,不过是从上到下排列
column-reverse:类似于row-reverse,不过是从下到上排列。
flex-wrap: nowrap | wrap | wrap-reverse
这个主要用来定义伸缩容器里是单行还是多行显示,侧轴的方向决定了新行堆放的方向。nowrap(默认值):伸缩容器单行显示,“ltr”排版下,伸缩项目从左到右排列;“rtl”排版上伸缩项目从右向左排列。
wrap:伸缩容器多行显示,“ltr”排版下,伸缩项目从左到右排列;“rtl”排版上伸缩项目从右向左排列。
wrap-reverse:伸缩容器多行显示,“ltr”排版下,伸缩项目从右向左排列;“rtl”排版下,伸缩项目从左到右排列。(和wrap相反)
flex-flow:这个是“flex-direction”和“flex-wrap”属性的缩写版本。同时定义了伸缩容器的主轴和侧轴。其默认值为“row nowrap”。
justify-content:flex-start | flex-end | center | space-between | space-around
这个是用来定义伸缩项目沿着主轴线的对齐方式。当一行上的所有伸缩项目都不能伸缩或可伸缩但是已经达到其最大长度时,这一属性才会对多余的空间进行分配。当项目溢出某一行时,这一属性也会在项目的对齐上施加一些控制。flex-start(默认值):伸缩项目向一行的起始位置靠齐。
flex-end:伸缩项目向一行的结束位置靠齐。
center:伸缩项目向一行的中间位置靠齐。
space-between:伸缩项目会平均地分布在行里。第一个伸缩项目一行中的最开始位置,最后一个伸缩项目在一行中最终点位置。
space-around:伸缩项目会平均地分布在行里,两端保留一半的空间。
-
align-items: flex-start | flex-end | center | baseline | stretch
这个主要用来定义伸缩项目可以在伸缩容器的当前行的侧轴上对齐方式。可以把他想像成侧轴(垂直于主轴)的“justify-content”。 - flex-start:伸缩项目在侧轴起点边的外边距紧靠住该行在侧轴起始的边。
- flex-end:伸缩项目在侧轴终点边的外边距靠住该行在侧轴终点的边 。
- center:伸缩项目的外边距盒在该行的侧轴上居中放置。
- baseline:伸缩项目根据他们的基线对齐。
- stretch(默认值):伸缩项目拉伸填充整个伸缩容器。此值会使项目的外边距盒的尺寸在遵照「min/max-width/height」属性的限制下尽可能接近所在行的尺寸。
-
align-content: flex-start | flex-end | center | space-between | space-around | stretch
这个属性主要用来调准伸缩行在伸缩容器里的对齐方式。类似于伸缩项目在主轴上使用“justify-content”一样。请注意本属性在只有一行的伸缩容器上没有效果。 - flex-start:各行向伸缩容器的起点位置堆叠。
- flex-end:各行向伸缩容器的结束位置堆叠。
- center:各行向伸缩容器的中间位置堆叠。
- space-between:各行在伸缩容器中平均分布。
-
space-around:各行在伸缩容器中平均分布,在两边各有一半的空间。
stretch(默认值):各行将会伸展以占用剩余的空间。
2.2.2 伸缩项目属性
注意:float、clear和vertical-align在伸缩项目上没有效果。
order:<integer>
默认情况下,伸缩项目是按照文档流出现先后顺序排列。然而,“order”属性可以控制伸缩项目在他们的伸缩容器出现的顺序。flex-grow:<number> (默认值为: 0)
根据需要用来定义伸缩项目的扩展能力。它接受一个不带单位的值做为一个比例。主要用来决定伸缩容器剩余空间按比例应扩展多少空间。如果所有伸缩项目的“flex-grow”设置了“1”,那么每个伸缩项目将设置为一个大小相等的剩余空间。如果你给其中一个伸缩项目设置了“flex-grow”值为“2”,那么这个伸缩项目所占的剩余空间是其他伸缩项目所占剩余空间的两倍。
负值同样生效。**flex-shrink **:<number> (默认值为: 1)
根据需要用来定义伸缩项目收缩的能力。负值同样生效。flex-basis:<length> | auto (默认值为: auto)
这个用来设置伸缩基准值,剩余的空间按比率进行伸缩。负值不合法。flex: none | [ <'flex-grow'> <'flex-shrink'>? || <'flex-basis'> ]
这是“flex-grow”、“flex-shrink”和“flex-basis”三个属性的缩写。其中第二个和第三个参数(flex-shrink、flex-basis)是可选参数。默认值为“0 1 auto”。align-self:auto | flex-start | flex-end | center | baseline | stretch
用来在单独的伸缩项目上覆写默认的对齐方式。属性值的介绍请参阅“align-items”的属性值。
2.3 flex布局实例
这是百度前端学院的一道题目:
任务目的
学习如何flex进行布局,学习如何根据屏幕宽度调整布局策略。
任务描述
需要实现的效果如效果图(点击打开)所示,调整浏览器宽度查看响应式效果,红色的文字是说明,不需要写在 HTML 中。
任务注意事项
- 只需要完成HTML,CSS代码编写,不需要写JavaScript
- 屏幕宽度小于 640px 时,调整 Flexbox 的属性以实现第四个元素移动到最前面的效果,而不要改动第一个元素的边框颜色与高度实现效果图。
- 思考 Flexbox 布局和网格布局的异同,以及分别适用于什么样的场景。可以搜索一下别人的结论,不过要保持思辨的态度,不可直接接受别人的观点。
- HTML 及 CSS 代码结构清晰、规范
要实现的效果如图所示:
实现此效果的代码如下:
<!DOCTYPE html>
<html>
<head>
<title>Flex布局</title>
<meta charset="utf-8">
<style>
body
{
margin:0;
padding:0;
}
div#container
{
margin:0;
padding:20px;
display:flex;
flex-direction: row;
flex-wrap:wrap;
align-content: space-between;
}
div#box1 { width:150px; height:120px; border: 1px solid #f00;}
div#box2 { width:150px; height: 100px; border: 1px solid #f00; }
div#box3 { width:150px; height:40px; border: 1px solid #f00; }
div#box4 {width:150px; height:200px;border: 1px solid #00f; }
@media all and (min-width: 640px)/*针对大屏幕设备(宽度大于640px)的样式*/
{
div#container
{
justify-content:space-between;
align-items:center;
}
}
@media all and (max-width: 640px)/*针对小屏幕设备(宽度小于640px)的样式*/
{
div#container
{
justify-content:space-between;
align-items:flex-start;
}
div#box1 { order:2; }
div#box2 { order:3; }
div#box3 { order:4; }
div#box4 { order:1;}
}
</style>
</head>
<body>
<div id="container">
<div id="box1"></div>
<div id="box2"></div>
<div id="box3"></div>
<div id="box4"></div>
</div>
</body>
</html>
3. 两种布局方式的比较
Flexbox布局(Flexible Box)模块旨在提供一个更加有效的方式制定、调整和分布一个容器里的项目布局,即使他们的大小是未知或者是动态的。(这里我们称为Flex)。Flex布局主要思想是让容器有能力让其子项目能够改变其宽度、高度(甚至顺序),以最佳方式填充可用空间(主要是为了适应所有类型的显示设备和屏幕大小)。Flex容器会使子项目(伸缩项目)扩展来填满可用空间,或缩小他们以防止溢出容器。最重要的是,Flexbox布局方向不可预知,他不像常规的布局(块就是从上到下,内联就从左到右)。而那些常规的适合页面布局,但对于支持大型或者杂的应用程序(特别是当他涉及到取向改变、缩放、拉伸和收缩等)就缺乏灵活性。
注:Flexbox布局最适合应用程序的组件和小规模的布局,而网格布局更适合那些更大规模的布局。
附录:参考教程
- <a href="http://www.w3cplus.com/css3/a-guide-to-flexbox.html">一个完整的Flexbox指南</a>
- <a href="http://www.ruanyifeng.com/blog/2015/07/flex-grammar.html">Flex 布局教程:语法篇</a>
- <a href="http://www.ruanyifeng.com/blog/2015/07/flex-examples.html">Flex 布局教程:实例篇</a>