沉着前进 2019-06-28
前端css布局知识繁杂,实现方式多种多样。想写出高效、合理的布局,必须以深厚的css基础为前提。为了方便记忆和复习,将css布局要点记录如下。内容较多,应用方面说的不太详细,但都是很实用的点。
所谓布局,就是指将元素设置为我们想要的大小,放置于我们想要的位置,位置
、尺寸
是核心两要素。这些元素其实就是一些方块,页面就是由各种方块拼凑而成。现在布局方式主要分为三种:
使用哪种布局方式,看项目具体要求,如果不需要兼容IE,建议使用flex或者grid,这两种是未来趋势。如果要考虑兼容,则最好使用传统css布局方案。
前面提过,一个html元素,就是一个方块,讲究一点的话就是盒模型。盒模型长这样:
我们可以在chrome开发者工具的styles中查看:
css盒模型,分为4部分:
无规矩不成方圆。所有的html元素都是按照一定规则去排列渲染的。在html中,不只有一种规则,是多种规则的混合。了解这些渲染规则和这些规则生效的条件对于我们理解css有很大帮助。
文档流可以理解为所有元素的默认渲染规则。它的规则很简单:元素按照自己的类型
的布局特性从左到右,从上往下依次排列。
元素在布局上分为三种类型:
上面内联元素其实说的不太正确,这里需要特殊说一下,内联元素又分为两种:
有两种方式可以脱离文档流,绝对定位和浮动,我们下面再详细说。
BFC全称是Block Formatting Context,块级格式化上下文。在BFC环境中的元素按照如下规则渲染:
类型
的布局特性从左到右,从上往下依次排列。那么什么样的渲染区域是一个BFC哪?下列几种方式可以显示声明一个BFC的渲染区域:
元素的尺寸有两种情况:
我们先来明确一下元素的尺寸概念,我们都知道元素的盒模型,元素由content、padding、border、margin四个区域组成。那么尺寸具体是指什么?我们可以把尺寸分为三类:
而我们在css中设置的width、height代表哪部分区域是有歧义的,我们通过设置box-sizing,可以切换width、height所代表的区域。
box-sizing,可以切换width、height所代表的区域。box-sizing主流浏览器有两个值:
box-sizing的这两个属性,造成width、height的二义性,而且很多情况下是全局设置的,隐蔽性很强,对于新手来说很容易懵。这两个属性的产生有一定的历史原因,最开始的盒模型默认采用的border-box,早期的IE就是这种,也叫IE盒模型。后来W3C觉得content-box更好,又把盒模型默认改为content-box模式。后来的后来,随着弹性布局的流行,border-box的优势越来越明显,大家都更愿意使用border-box来布局,W3C又想把border-box搞回来。但是已经有好多基于content-box的网站,为了兼容性也不能随便改。于是,W3C就想出了box-sizing这种方式,支持了border-box,但是默认还是content-box。使用box-sizing应注意的几点:
设置所有元素的box-sizing属性为inherit:
*, *:before, *:after { box-sizing: inherit; }
这样我们可以设置父元素的box-sizing,就可以控制所有子元素的box-sizing属性,对于我们封装组件,设置整个组件环境很方便,只有我们在封装组件时强设置box-sizing,就不怕全局的样式侵入了。
我们知道width, height都是可以设置百分比,那这个百分比的参照物是谁?这里引出一个概念,叫做包含块(CB, Contanining Block),一个元素的包含块就是该元素的width、height百分比的参照物。
很多新手同学,在设置宽高百分比的时候,有时候觉得莫名其妙,各种奇怪现象,怎么设置都不起作用。其实就是没有包含块的概念。一个元素的包含块是谁,主要取决于该元素的position属性,总结如下:
position为absolute和fixed时,包含块也可能是由满足以下条件的最近父级元素的padding-box:
这条比较特殊,遇到的情况比较少,单独拿出来
除了width, height的百分比相对于包含块设置外,margin、padding的百分比也是相对于包含块设置,只不过margin、padding百分比相对于包括块的宽度设置(水平模式下)。绝对定位的偏移属性top、left、right、bottom也是相对于包含块设置,后面再详细说。
元素的包含块都是自己的祖先元素,那hmtl
元素没有祖先元素,它的百分比设置相对于谁那?就是初始包含块,根元素(hmtl
)所在的包含块是一个被称为初始包含块的矩形。这个矩形的大小就是浏览器视口的大小。有一点需要注意,那些没有定位非static祖先元素的参照物是初始包含块,而非html元素。
因为margin在布局中有一些重要特性和特殊情况,所以单独拿出来讲一下。
我们知道,块级元素即使设置了宽度,也会占满一行,为什么会这样?剩余的空间被谁占了?
这里要明确一点,块级元素占据一行,是指块级元素的外部尺寸占据一行,就是margin-box。当margin设置为auto的时候,margin会自动占满剩余空间。
当margin-left和margin-right同时为auto,就会平分剩余空间,这就是margin:auto
会使元素水平居中的原因。然后margin: auto
却不能使元素垂直居中,这是因为在垂直方向上,块级元素不会自动扩充,它的外部尺寸没有自动充满父元素,也没有剩余空间可说。如果我们在父元素上设置writing-mode: vertical-lr
,这时margin:auto
就会使子元素垂直居中,而水平居中无效。
那有没有什么办法使用margin:auto
让元素同时水平垂直居中?答案是有的,就是绝对定位的情况下。
.father { width: 300px; height:150px; position: relative; } .son { position: absolute; top: 0; right: 0; bottom: 0; left: 0; width: 200px; height: 200px; margin: auto; }
此时的son的外部尺寸,就会自动充满它的包含块,效果和块级元素类似。这时候设置.son元素的宽高,就会有剩余空间出来,margin:auto
会自动平分剩余空间,使.son水平垂直居中。
margin对元素的影响有2个:
块级元素这种宽度方向自动充满空间的布局特性,当没有设置宽度,设置margin-left和margin-right为对元素的内部空间有影响。margin为正值时,元素尺寸
缩小;margin为负值时,元素尺寸
增大。因为元素的外部尺寸大小已经定了,就是其包含块的尺寸,而外部尺寸
等于元素尺寸
加上margin,如果margin为负,元素尺寸
自然就增大了。利用内部尺寸增大、外部尺寸不变这个特点,我们可以进行等间隔列表的布局。比如我们实现一个一行三列,两侧无间隙,中间间隙为20px的布局:
.father{ margin-right: -20px; } .son{ width: calc( calc(100% - 20px * 3) / 3); margin-right: 20px; }
在元素宽高固定的时候,相当于元素尺寸
固定了,margin开始影响元素的外部尺寸
。正margin会使元素元素的外部尺寸
增大,负margin会使元素元素的外部尺寸
缩小。正值很好理解,就不说了。主要说一下负值,这时候margin-top、margin-left和margin-right、margin-bottom的表现看起来是有区别的,实质是一样。margin-top、margin-left为负值时,表现为元素向上或者向左移动,margin-right、margin-bottom为负值时,表现为其右侧元素或者下面元素都会相应地向左或者向上移动。仔细想想,这都是元素外部尺寸
缩小的表现。margin-top、margin-left使左侧上上侧尺寸缩小,就会自然向左或者向上移动。margin-right、margin-bottom使右侧和下侧尺寸缩小,旁边和下面的元素自然就会往左或者往上占位。
块级元素的上边距和下边距在某些场景下会发生合并行为。需要注意的是浮动元素和绝对定位元素不会发生合并,且合并只发生在垂直方向。有以下3中合并场景
margin合并的计算规则
阻止margin合并的方法
主要想说line-height
,vertical-align
两个属性,这两个属性对高度有重要的影响,而且如果没有理解这两个属性的作用,会经常碰到一些不好解决的诡异的布局问题。
这个名字是张鑫旭大神定义的,在W3C规范中也是可以找到根据的。在《CSS世界》这本书中,为了证明幽灵空白节点的存在,举了一个例子:
div { background-color: #cd0000; } span { display: inline-block; } <div><span></span></div>
你可能猜到了,div的高度不为0,我在chrome下试了试高度为21px,这个高度猜测应该和div的字体、字体大小、行高有关。这个幽灵空白节点的存在,会引起一些怪异现象,比如:
<div><img></img></div>
div的高度总是比img高一些,img下总有一个间距。这也是一个幽灵空白节点在起作用。
line-height翻译过来就是行高。它指的是指行框
的高。那什么是行框
那?
行框是由内联元素或者内联块级元素组成的一行。通过尝试,发现一行形成行框的两个前提是,要么有文字内容,要么有内联块级元素,如果其中是一堆空的内联元素,比如span,是没有形成行框的。
line-height就是指定行框占据的高度。只有形成了行框,line-height才会起作用。
这里可能有一些容易误解的情况,比如:
.father{ line-height: 300px; } <div class="father"> <div class="son"> 你好 </div> </div>
这时候.fahter高度是300px,这里注意的是,不是line-height对.son起作用了,是.son内部形成了一个行框,line-height对这个行框起作用了,.son的高度是300px,撑开了.father。
vertical-align用来设置内联元素或者内联块级元素在行框内的对齐方式或者说垂直位置。verticle-align有四类值,我们在这里直说其中对布局比较重要的两类:
先来看一张图:
vertical-align默认是基线对齐的,基线的位置很重要。而基线的定义是:字母x的下边缘
。中线的位置是基线往上 1/2 x-height 高度,就是x交叉的地方。这里有一个需要注意的地方是,纯文字的中线是x交叉的地方,但是内联元素的中线却是内联元素的正中间位置。我们来看一个例子:
div{ line-height: 100px; background: yellow; } <div> <span class="world">xhello</span> </div>
效果如下:
一切正常,div高度100px,文字也是近似垂直居中。但是如果我们在.world元素上加vertical-align: middle
,我们会发现文字向下偏移,且div高度变成了102px,效果如下:
这是怎么回事那,vertical-align设置的到底跟谁对齐?答案是幽灵空白节点,幽灵空白节点是个很神奇的存在,看不见,但却实实在在地在起作用,它的作用就相当于一个x字符的作用,用来提供对齐基准。我们上面说到的,div元素中有一个图片的例子,img是可替换内联元素,它的baseline就是它的底部,默认是baseline对齐,所以图片对齐的是幽灵空白节点x的基线,x基线位置往下是一段空白的,这就是这段空白的来源。为了更直观,我们在上面这个例子的span元素前面加一个x字符,当做幽灵空白节点,看一下效果:
本来,x
和xhello
中的x
是对齐的。设置vertical-align之后,我们可以看到xhello字符向下挪了一段位置,这是因为span元素的垂直中线位置,比xhello
中x
交叉点要高,现在要和前面的x
交叉点对齐,就往下移动了。又因为x
和xhello
的行高都是100px,但是现在因为由于错位,造成整体高度多了2px。
利用line-height和vertical-align可以设置多种场景下的垂直居中。
div{ line-height: 任意值 } <div>hello world</div>
.box { line-height: 120px; background-color: #f0f3f9; } .content { display: inline-block; line-height: 20px; margin: 0 20px; vertical-align: middle; } <div class="box"> <div class="content">多行文字...</div> </div>
.container { position: fixed; top: 0; right: 0; bottom: 0; left: 0; background-color: rgba(0,0,0,.5); } .container:after { content: ''; display: inline-block; height: 100%; vertical-align: middle; } .dialog { display: inline-block; vertical-align: middle; } <div class="container"> <div class="dialog"></dialog> </div>
元素的位置由position属性和float属性决定。
position有4个值:
需要注意的地方有:
float属性可以使元素脱离文档流,向左或者向右移动,直至碰到包含块的padding或者碰到另一个浮动元素。注意:
float最著名的就是它的高度坍塌,之所以有高度坍塌,和float设计的初衷有关。float属性最初是设计用来实现文字环绕效果的。
<div class="img-wrap"> <img style="float: left"/> </div> <p>我是文字,我要环绕你</p>
.img-wrap因为高度塌陷,实际上不识别float元素,高度为0,p元素与其重合,从而实现文字环绕效果。
高度塌陷特性在实现布局的时候是不合理的,而现在文字环绕的场景很少,float基本都用来布局使用了。这现在实际上已经成为了一个“bug”的存在。所以css又提供了一个属性用来清除浮动:clearfix。
clearfix在mdn上的解释为指定一个元素是否可以在它之前的浮动元素旁边(我觉得这里用重合更贴切),或者必须向下移动(清除浮动) 在它的下面。
注意上面这句话,指定一个元素
表示clearfix的作用目标是元素自身,在它之前的浮动元素
表示clearfix的作用条件是它前面有浮动元素,而clearfix的作用效果是让元素换行显示。所以clearfix的作用是,如果元素前面有浮动元素,可以指定该元素换行显示,或者说是识别前面的浮动元素。clearfix有3个值:
一般情况下采用clearfix: both。清除浮动一个重要应用就是解决float高度塌陷的问题。通用的解决方案是设置.clearfix类:
.clearfix { display: block; zoom: 1; &:after { content: ""; display: block; font-size: 0; height: 0; clear: both; visibility: hidden; } }
在包含浮动元素的父元素上设置该类即可。
background-color: blue;background-color: yellow;<input type="button" value="变蓝" @click="changeColorT