HTML布局四剑客-Flex,Grid,Table,Float
前言
在HTML布局中有很多的选择,同一种表现方式可以使用不同的方法来实现.下面来对四种最常见的布局方式进行阐述和解释,它们分别是Float,Table,Grid和Flex
Float
第一位出场的就是最年老的Float,"老骥伏枥,志在千里".作为最早出现的定位方式,为元素赋予了"浮动显示"的技能,从此,元素可以不跟着文档的方向随波逐流,而可以拥有自己的"浮动方向",可以说是CSS里面最常出现的熟人了.
float不仅仅出现在网页上,事实上它借鉴了印刷行业的特性,参见下图
图片下方有文字,此时的图片就是浮动的,可以想象整个文档流就是一条河,图片是一条船,文字是水上漂浮的花瓣,当这艘船放在水里的时候,花瓣是会围绕在这艘船而不会被这艘船压在下边,因为都是"浮动"的.
如上图所示,当头像被设置为浮动的时候,介绍的文字是根据头像大小"可响应"(responsive)的,如果设置为非浮动,也就是绝对定位,当头像变大了之后,文字便会被遮盖,就像花瓣感知不到船的存在一样.
如果float只能用于图片文字的排版那就太小看它了,float可以很轻松的实现整个页面的布局
float有四种可选值:
1 none(default):没有浮动
2 left:向左浮动
3 right:向右浮动
4 inherit:继承祖上的浮动方式.
还有一个孪生的属性clear,用于清除float的属性.
如上图所示,当SideBar不足以占满右边所有内容的时候,Footer便会浮动到图示的位置,但是Footer并不想这样放在内容的右边,它向单独向下另起一行,如果将其clear属性设置为both,就可以满足他的心愿.
clear还有别的选择吗?当然有,跟float一样,有四种选择以上的both是清除左右浮动,自成一行,还有清除 左浮动(left), 右浮动(right), 移除clear属性(none:该值为默认值),其实还有一个inherit:继承,IE不兼容该属性(在IE11上进行测试依然不兼容).
如果父元素只包含属性为float的子元素,那么该父元素的的高度将会为0,这时候需要使用到clear
方法1 将文字元素设为block
这样可以避免父元素的高度为0的尴尬,但是,为每个文本设置块太"贵"了,并且还需要调整两个文本间的margin来使段落看起来间隔自然
方法2 在浮动子元素之后,在父元素闭合标签之前清除浮动属性.
1 添加一个空div
<div class="container"> <image src="http://via.placeholder.com/350x150"></image> <image src="http://via.placeholder.com/150x100"><image> <div class="clearfloat"></div> </div>
将其属性设置为clear:both
img { float:left; } .container { width:500px; } .clearfloat { clear:both; }
最后可以看到container已经有了宽高,为子元素宽高的和
2 使用:after伪类选择器
<div class="container">
<image src="http://via.placeholder.com/350x150"></image>
<image src="http://via.placeholder.com/150x100"><image>
</div>
img { float:left; } .container { width:500px; } .container:after { content:'.'; visibility:hidden; display:block; height:0; clear:both; }
3 使用overflow属性
值 | 描述 |
---|---|
visible | 默认值。内容不会被修剪,会呈现在元素框之外。 |
hidden | 内容会被修剪,并且其余内容是不可见的。 |
scroll | 内容会被修剪,但是浏览器会显示滚动条以便查看其余的内容。 |
auto | 如果内容被修剪,则浏览器会显示滚动条以便查看其余的内容。 |
inherit | 规定应该从父元素继承 overflow 属性的值。 |
在使用的时候需注意,虽然overflow不需要引入新的空标签,但是会出现子元素内容被裁减或显示滚动条.
结果是父元素有了宽高,高是子元素高的和,但是宽是继承来的宽(如果没有显式设置width,默认为100%)
<div class="container"> <img src="http://via.placeholder.com/350x150"></img> <img src="http://via.placeholder.com/150x100"></img> </div>
img { float:left; } .container { overflow: hidden; }
如果想让浮动元素进行分组有什么办法呢?比如将下列不同颜色的分为一组
方法1 在需要分割元素后添加一个空元素,然后清除float属性
img { float:left; } .container { overflow: hidden; } .clearfloat { clear:both; }
方法2 将同一个颜色的包裹在同一个group中,清除group的浮动的属性
img { float:left; } .container { overflow: hidden; } .group { clear:both }
在使用float属性的时候有一些bug传说:
1 overflow:如果图片超出浮动容器,会影响其他浮动容器的浮动布局.在IE11上进行了测试,已修复,会将Image裁切到容器宽度
2 双边距,在具有浮动属性的元素设置margin,值会翻倍,在IE11已修复.
3 3px Jog:text与具有浮动属性的元素之间有3px的左间隔 在IE7被修复
4 底边距bug 父元素会无视子元素的margin-bottom属性,IE11已修复
* 参考网址及截图来源CSS Tricks
Table
Structure:
表结构: <thead><tfoot><tbody>
thead和tfoot(如果有)需要尽早的列在前面,而不是将tfoot放在table标签的末尾,这么做的理由是让table的结构一目了然.tbody就是表数据内容.
单元格:<th> --> tabular headers <td> --> tabular data <tr> -->table row
<th>虽然有header,但是完全不影响它的位置,可以放在表结构的任何地方,只要你认为这个单元格重要到可以成为"头".比如这样:
附属物: <caption> <col>一个没有内容,定义列属性的标签(对齐align valign,宽度,颜色等) <colgroup> 列集合,跟<col>联合使用的定义表格的展现方式
<col>和<colgroup>必须在<caption>之后,任意的表结构或<tr>之前定义好.不过该属性在HTML5中已经不支持了.
<colgroup> <col span="2" style="background-color:red"> <col style="background-color:yellow"> </colgroup>
以上代码的意义是该列表集合由两部分组成,前两列的背景颜色为红色,后一列的颜色为黄色
Style:
默认表中每一行都有2px的间隔:
collapse属性去除间隔
table { border-collapse: collapse; }
"合并单元格"是excel中很常见的操作,那么在table布局中如何实现呢?
<th colspan="2"> 表示占两列 <td rowspan="2"> 表示占两行,结合这两个属性就可以实现需要的布局.
可以将其拆分成以下的元素(colspan,rowspan)进行设置
表格的宽度有三种情况,
1 表格宽度为内容宽度 (内容宽度小于容器宽度)
2 刚好占满整个容器 (内容宽度等于容器宽度)
3 超出容器大小 (内容宽度大于容器宽度)
使用 white-space:nowrap 设置文字超过容器大小后是否换行(默认换行)
Table的用武之地:
需要表格化展示数据的时候(tables are for tabular data),常见的有:日程表,价格表,属性表,得分表,员工表,财务数据,日历,营养表.
但是 将Table用于布局是不符合语义的,是一种hack的方法
1 HTML标签必须有意义,就像之前说的,table只能用于表格化展示数据.
2 确保网站的可访问性,其中一个便是屏幕阅读,屏幕阅读是从左往右,从上到下,意味着整个网站变成了视觉上的支配而不是可访问性的支配.而且还会宣布表格的开始,比不用更糟糕
3 源代码的顺序依赖, 为了实现Table布局,会在更重要的内容之前声明Table,导致SEO的情况并不好.
再一次但是有时候还是会使用Table来布局,比如HTML Email,他需要在各种的设备上运行,包括老旧的设备,一些更现代的布局方式会造成兼容性的问题,通过表格被视为最安全的方式.
Tables are for and only for tabular data
扩展:还有一个table的崇拜者模仿table的表现方式
display: table /* <table> */ display: table-cell /* <td> */ display: table-row /* <tr> */ display: table-column /* <col> */ display: table-column-group /* <colgroup> */ display: table-footer-group /* <tfoot> */ display: table-header-group /* <thead> */
注意,没有<th>的替代,且以上都是语义值,并不是实际的标签
Flex
这是我研究最多的布局方式,真是特别的灵活,可以很快速的设置我想要的方式,而且自适应也做的比较好,响应式布局也能用它来实现,不得不说是四剑客当中实力很强的选手了.
Characteristics:
1 可以从任何方向布局-->flow direction,包括leftwards,rightwards,downwards, upwards(请注意,只能选择一个方向)
2 可以重建视觉顺序(visual order)而不影响文档中的顺序, 包括 Reverse: row-reverse, column-reverse Rearrangement: order属性,这样的好处是可以保持网页的可读性(accessibility).
3 两种方式布局 1 线性的(no-wrap),只有一条主轴(main axis) 2 折叠换行的(wrapped),包括主轴和交叉轴(cross axis)
4 弹性的size,可以充分利用剩余空间
5 能够分别设置主轴和副轴的对齐方式
6 在保留副轴的情况下在主轴下动态的折叠或展开
Concept:
最核心的就是下面这一张图,可以将flex的规则包括80%.(图片来自W3C Candidate Recommendation)
在container上设置 dispaly: flex; ,那么所有属于他的items(所有的in-flow children elements)都会按照相同的高度进行flex布局.
Container
1 display: flex或者inline-flex 设置为"块"级还是行内级容器,需要注意的是,这里的"块"只针对container,至于它的item只是flex-level,不是block不能设置以下属性(设置了无效):
1 float和clear
2 vertical-align,可以用align-self代替
3 ::first-line和::first-letter伪类选择器,flex布局没有第一行或第一个letter的概念
2 flex-flow:包含1 flex-direction: row | row-reverse | column | column-reverse
, flex-wrap: nowrap | wrap | wrap-reverse
3 justify-content: flex-start | flex-end | center | space-between | space-around | space-evenly
这里重点说一下space-between,space-around和space-evenly.一张图表示(修改自 CSS-Tricks)
4 align-items: flex-start | flex-end | center | baseline | stretch
跟justify-content的对齐是相对的,前者对应猪猪,此对应副轴,这里有一个baseline的概念,先按下不表
5 align-content: flex-start | flex-end | center | space-between | space-around | stretch;
如果有多行(flex-wrap设置为wrap时)每一行的对齐方式,如果只有一行则失效
Item:
item就是弹性容器内容的内容流,只要在流里(in-flow)的都是item,都能受flex控制
1 flex:包括三个属性,每个属性都值得一讲
1 flex-grow: 如果一行之后还有剩余空间,那么会按照每个item设置的grow进行扩展,此属性无单位,表示所占的权重,如果三个item设置的flex-grow均为1,1,1且未wrap,也没有其他item了,那么每一个会在除开自己的宽度之后的剩余空间每个占1/3.默认为0
2 flex-shrink: 在必要时收缩,默认为1,可以缩小至最小值
3 flex-basis: grow和shrink之前的基础值,默认为auto<length> | auto
以上三个属性不要分开用,因为会有部分属性未设置的隐患,使用flex:统一设置,未设置的为默认值.
flex基本值有4个:
flex:initial | flex:0 1 auto |
flex:auto | flex:1 1 auto |
flex:none | flex:0 0 auto |
flex:一个正值 | flex:正值 1 0 |
2 align-self: 表示对齐方式,跟在container上设置align-items
是一样的, auto | flex-start | flex-end | center | baseline | stretch
3 order: 表示显示的顺序,可以在不改变文档书写顺序的情况下改变视觉呈现顺序,使页面具有更好的"可访问性"(Accessible),默认值为0 可以为负值.
到目前为止所有的属性已经介绍完了,下面补充一点与flex有关的知识
1 什么是匿名item?
<div style="display:flex"> <!-- flex item: block child --> <div id="item1">block</div> <!-- flex item: floated element; floating is ignored --> <div id="item2" style="float: left;">float</div> <!-- flex item: anonymous block box around inline content --> anonymous item 3 <!-- flex item: inline child --> <span> item 4 <!-- flex items do not split around blocks --> <q style="display: block" id=not-an-item>item 4</q> item 4 </span> </div>
注意打引号的item4,它是一个"block",flex布局不会在block进行item的拆分(不属于item了),所以"item4"换了行,如果不是"block"的话,"item 4"会接着在第一个item 4右面出现.
2 flex布局是如何确定宽度和高度的?( Line Length Determination )
1 definite size: 定义为不根据所在的布局而改变的size值,例如显式设置height和width:100px.
2 max(min):最大最小内容约束,这也是item使用grow和shrink的基础.
3 如果都没有的话,除去container的margin,padding,border后的值为可用的值(这个值可能是无限大的"infinite",比如高度)
3 container的baseline基线是什么东西?
在之前讲align-items对齐方式的时候讲过baseline,定义为:
1如果有多行flex lines,那么baseline就是多个lines共享的集合
2 如果container只有一行,包含超过一个item,首尾item中定义了对齐方式,默认是content,text为字的底边的方式对齐
Grid
/** * 定义grid item空间(space) */ #grid { /** * 两列 * 1. 第一列宽度为内容的大小 * 2. 第二列占据剩下的空间 * * 三行 * 3. 第一行高度为内容大小 * 4. 中间行占据剩下的空间 * 5. 最后一行高度为内容大小 */ display: grid; grid-template-columns: /* 1 */ auto /* 2 这个1fr是不是很像flex布局中的flex-grow?*/ 1fr; grid-template-rows: /* 3 */ auto /* 4 */ 1fr /* 5 */ auto; } /* 定义每个grid item所在的位置 */ #title { grid-column: 1; grid-row: 1; } #score { grid-column: 1; grid-row: 3; } #stats { grid-column: 1; grid-row: 2; align-self: start; } #board { grid-column: 2; grid-row: 1 / span 2; } #controls { grid-column: 2; grid-row: 3; justify-self: center; }
HTML代码
<div id="grid"> <div id="title">Game Title</div> <div id="score">Score</div> <div id="stats">Stats</div> <div id="board">Board</div> <div id="controls">Controls</div> </div>
结果示意图:图片来自W3C Candidate Recommendation
表格布局就是将内容变成表格一样的东西,二维,包含水平和垂直(可以与flex中的main,cross对应理解)
Characteristics:
1 固定的,灵活的和基于内容的追踪size功能
2 使用正(向前),负(向后)的坐标值来显式设置item位置.
3 自动放置项目到空白区域(包括重新排序时)
4 空间敏感的重复跟踪(track),自动添加行列适应内容
5 通过margin,gutter(row&column gap),对齐属性控制spacing和alignment
Concept:
1 block axis 列所(column axis)在的line1,2,3就是在block axis上
2 in-line axis 行(row axis)所在的1,2,3,4就是在in-line axis上
3 grid line,水平和垂直分割线,上图中的line1,2,3,4和line1,2,3,索引值自动生成,可以在grid item中进行引用,定位.下例表示占据H(右)B空间的item1
用line(线)的概念而不是row和column
#item1 { grid-column: 2; /*从列的line2到line3(默认占据一个单位)*/ grid-row-start: 1; grid-row-end: 2; } /*从行的line1到line2*/
named line:可以通过设置[item-start][item-end]来进行与上例相同的定位,但是需要在container中进行对应的修改
#grid { display: grid; grid-template-columns: 150px [item1-start] 1fr [item1-end]; grid-template-rows: [item1-start] 50px 1fr 50px [item1-end]; } #item1 { grid-column: item1-start / item1-end; grid-row: item1-start / item1-end; }
4 grid track:相邻grid lines之间的距离,每一个grid track会交给一个sizing函数,会计算出行列的宽高.邻近的grid track会被gutter(column&row gap)打断.
5 grid cell:grid item能引用的最小单位(单元格)
6 grid-area是一个逻辑空间(如上例的HHABFF),用来存放一个或多个items的.但是必须是连续的,只能用4条lines来划出这个area,可以在grid container的grid-template-area中显式命名,也可以隐式的与line的值绑定,grid item通过grid-property(grid-area)属性来将item放到对应的area中
#item1 { grid-area: A } #item2 { grid-area: B;align-self: start;} #item3 { grid-area: B;justify-self:end;align-self:end}
item1放入A中,item2和item3都放入B中,两者根据不同的align和justify位置有所区分.
Grid Container:
main {
grid: "H H " 50px
"A B " 1fr
"F F " 50px
/ 150px 1fr;
}
这是最简洁和形象的方式,上图表示了三行两列区域名分别为ABHF的内容,第一列150px,第二列占据剩余空间,第一行和第三行分别为50px,第二行占据剩余空间.(fr: fraction 分数)
1 display:跟flex一样,有grid和inline-grid的区分,以及item不能使用的属性也跟flex item一致
2 grid-template-columns, grid-template-rows
grid-template-columns: 100px 1fr max-content minmax(min-content, 1fr) repeat(2, 1fr); /* 5条lines创建了, * 1 起始位置 * 2 距离起始位置宽度为100px * 3 距离第二条line1/4剩余宽度 * 4 距离第三条最大item宽度 * 5 距离第四条不小于最小item宽度,不大于1/4剩余宽度
* 6 距离第五条分别为1/4,1/2的两条lines * 为了避免超出container的宽度,最好在item中设置弹性值,比如fr.
* 也可根据上面提到的named line来自定义lines名称创建行列 */
有一个神奇的clamp a grid area:当grid-area部分超过了grid的限制,那么span会被压缩到最后一个line,如果全部都超过了,那么会在已有的限制上加1,并都压缩至那个1中
/**如果grid只支持1000个tracks/ .grid-item { grid-row: 500 / 1500;/*部分超出*/ grid-column: 2000 / 3000;/*全部超出*/ } /*保留未超过部分,将超过的部分加1;如果全部超过,则都压缩至最后一个1中*/ .grid-item { grid-row: 500 / 1001; grid-column: 1000/1001 }
3 grid-template-area: 将自定义命名的item按顺序放置
#grid { display: grid; grid-template-areas: "head head" "nav main" "foot ...." } #grid > header { grid-area: head; } #grid > nav { grid-area: nav; } #grid > main { grid-area: main; } #grid > footer { grid-area: foot; }
4 grid-template将2,3均包括
grid-template: auto 1fr / auto 1fr auto;/*设置了row和column,template-area为none*/
grid-template: [header-top] "a a a" [header-bottom]
[main-top] "b b b" 1fr [main-bottom]
/ auto 1fr auto;
上面代码的结果为:(图片来自W3C Candidate Recommendation)
5 在grid-column&row属性中还有一个比较重要的就是grid-auto-column&row,设置隐式的大小
#grid { /*template只包含了一行一列,超出的部分则按照auto来设置大小*/ display: grid; grid-template-columns: 20px; grid-auto-columns: 40px; grid-template-rows: 20px; grid-auto-rows: 40px; } #A { grid-column: 1; grid-row: 1; } #B { grid-column: 2; grid-row: 1; } #C { grid-column: 1; grid-row: 2; } #D { grid-column: 2; grid-row: 2; }
图片来自(W3C Candidate Recommendation)
6 grid-auto-flow:定义自动的流向,[ row | column ] || dense
row和column就是定义那个方向自动"流",dense代表密集排布,即如果后面的item足够小能够将前面留下的空白(hole)给补上,那么会将后面的item提到前面,实现密集型排布(可能会影响顺序),如果不设置,默认为sparse,稀疏排布.
7 grid: grid-template | grid-template-rows / [auto-flow &&dense?] grid-auto-column | [auto-flow &&dense?] grid-auto-rows / grid-template-columns, 尽量使用grid而不是分别使用各自的属性.
grid: auto-flow 1fr / 100px; /*相当于*/ grid-template: none / 100px; grid-auto-flow: row; grid-auto-rows: 1fr; grid-auto-columns: auto;
8 间距和对齐: 对齐属性包括align-items justify-content align-content都跟flex一样.间距属性: row-gap|column-gap|gap,当然在对齐中的 space-around|space-between|space-evenly对gap有影响.
Grid-Item
1 grid-placement-priorities: 要注意这里line(线的概念)
.one { grid-area: main;/*命名main,放在container定义的template对应位置*/; } .two { grid-column: 2; grid-row: 3; /*相当于grid-area: 3/2 放在行3列2的位置,默认span为1*/ } .three { grid-row: 2 / span 5;/*从第二行开始span5行到第七行结束*/ } .four { grid-row: span 5 / 7;/*跨5行,到第七行为止(可以推导出从第二行开始)*/ } .five { grid-column: first / middle;/*从first列到middle列*/ } .six { grid-area: auto;/*自动布局*/ } .seven { grid-area:span 3/ span 2;/*跨3行2列,这是隐式的自动布局,受container的grid-auto-flow控制*/ }
有一个绝对定位值得一提,在container中设置了position:relative,那么在item中使用position:absolute,可以对在所占的grid-area中进行绝对定位:
.abspos { grid-row-start: 1; grid-row-end: span 2; grid-column-start: 3; grid-column-end: auto; /* 直到最右边的line即5th line */ position: absolute;/*设置absolute*/ /*设置相对定位*/ top: 70px; bottom: 40px; left: 100px; right: 30px; }
2 对齐: column轴:align-self(相当于container中的align-items)跟flex布局的概念一样
Grid和Flex的联系:
看到这两者之间有很多共同的属性(名字都一模一样),其实,Grid是为了更好的使用Flex,Flex只能在一个方向上具有弹性,row或者column但是Grid可以在row和column上都具有弹性.两者结合起来就可以实现任何方向任何颗粒度的弹性化.比如,想要设置整个Grid的背景图为拉伸的,但是所有的item垂直居中,那么可以这样来实现:
.grid { align-items: stretch; } .griditem { display: flex; align-items: center; }
Grid做layout,Flex做component