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和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,稀疏排布.

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

原文地址:https://www.cnblogs.com/BigJ/p/layouts.html