哪里说起呢?就从一道老生常谈的前端面试题开始吧。
块级元素与行内元素
问:块级元素和行内元素有什么区别?
估计各位同学都在各种前端面试中被问到过,当然也在各种网站上搜寻过答案。要真正罗列两者的区别可以找到很多,然而从本质上讲,就是这样的区别:
- 块级(
block
)元素可以控制它的大小 - 行内(
inline
)元素将其排到父级的行系统中
我更愿意把这两者称作为元素的两个特性。因为行内元素具有 inline
特性,所以多个行内元素会排在一行;但它们不具有 block
特性,所以无法使用 width
、height
指定它们的宽高。因为块级元素具有 block
特性,所以可以直接指定元素的宽高;因为块级元素不具有 inline
特性,所以它们会独占一行。
这两个特性还可以叠加:同时具有 inline
和 block
特性的就是行内块(inline-block
)元素,既可以设定宽高,也会排到行系统中。如果两个特性都不具备,那么就是 display: none
。
注意这里所说的 block
和 inline
都是相对于同级元素而言的,对于元素的子级又有不同的布局方式。如果指定不同子节点的布局方式与 block
、inline
特性叠加,又有了 table
(具有 block
特性、子元素使用 table
方式布局)、inline-table
(具有 inline
、block
特性,子元素使用 table
方式布局),flex
、inline-flex
,grid
、inline-grid
等等。
有了这样的概念,就可以解释一些特殊的现象。比如 text-align
对块级元素不起作用:因为块级元素所在的行独立于父级行系统之外,一个设置行内位置排布的 CSS 属性当然不会起作用。再比如 margin: 0 auto
只对块级元素起作用:因为只有块级元素独占一行,它才有充足的空间自由的在行中排布。
请再次注意:text-align
排的是元素的子节点,而 margin
排的是元素本身。
HTML 页面默认采用流式布局,水平方向和竖直方向是不对等的。在默认情况下,HTML 页面中的文字总是会按书写顺序(writing-mode
)横着从左往右排,一行占满之后会在这行的下面另起一行。text-align
不是不能对竖直方向其作用,只需要当前的行系统是竖排的(writing-mode: vertical-lr
)。margin: auto 0
默认情况下不会将一个块级元素竖直居中,因为缺少 inline
特性只是让元素独占一行,而不会让他独占一列。
想让水平方向和竖直方向对等也很简单:position: absolute
,如果再使用 top: 0; bottom 0;
将元素竖直占满一列,就可以使用 margin: auto 0
将元素竖直居中。
行系统
那块级元素和表格元素有什么区别?区别是:块级(block
)元素使用行系统布局子节点。
所谓行系统就是基于行的子节点排布方式,默认情况下从左往右依次排列,超出一行时就折行。每个块级元素都有自己的行系统,block
元素如此,inline-block
元素也如此。不同的块级元素的行系统各自独立,就算它们有父子关系。你完全可以把某个块元素左对齐,然后把其子级的行内块元素右对齐,没有任何问题。
既然有了行的概念,那么每行占多高呢?这就有了行高的概念,由 CSS 属性 line-height
控制,默认值为 normal
,不同的浏览器对 normal
的处理不一致,你也可以手工指定其值。可以是一个数字,表示当前元素的 font-size
的倍数,也可以是一个绝对值,多少像素等。
元素的行高可以很大,也可以很小,也许放下行中的元素后上下还有空余,也许刚刚好,也许根本放不下。控制行中节点位置的 CSS 属性是 vertical-align
,默认是 baseline
。
行中最常存放的节点是文本。由于各个字符的高度各不相同,为了显示的整齐,不能通过简单的对齐上下两边或中心排列行中的字符。每个字符都需要有一个基准去做对齐,这样就有了基线(baseline
)的概念。
可以看到图片中的后几个字母 hinx
下边都有一两个短横,它们叫做衬线,带有衬线的字体叫做衬线字体。它们在同一条直线上,这条直线所在的位置就是基线。文本默认按照基线对齐,基线的位置跟当前字体有关,不同的字体位置不同。当行高超出最大字符高度时,基线会往行中间移动,这就是为什么把 line-height
的值设置为父元素高度时可以将文本竖直居中的原因。
vertical-align
还有几个可选值,其中最常用的应该是 middle
了。各位同学应该遇到过这样的问题:给一个内联块元素设置了 vertical-align: middle
,但这个元素并非在正中,总会偏离一点点。vertical-align: middle
其实并不是把元素绝对的居于父元素中间(不要看 w3school 等上面的初学者向文档),MDN) 上给的官方说明为:
元素中垂线与父元素的基线加上小写x一半的高度值对齐。
vertical-align: middle
的对齐方式与父元素的基线和字号有关,然而这个值未必就是父元素的中线。那么解决的方法也很简单,设置父元素 font-size: 0
,字号为 0 时基线必然在元素正中,这个 小写x一半的高度值
也必然是 0。
额对了,font-size: 0
还有一个用处是干掉多余的空格,这在使用 display: inline-block
做横向布局时非常有用。
再次注意 vertical-align
控制的是内联元素在其所在行的行内居中,它只对带有 inline
特性的元素起作用,包括内联块元素。另外如果你用 vertical-align: middle
使某元素竖直居中于父元素中间,记得同时设置 white-space: nowrap
,防止多个元素折行。
前面提到了 text-align
,除了常用的 left
、center
、right
取值以外,还有一个很有用的取值:justify
——两端对齐。在只有一行时,两端对齐就相当于左对齐;但是有多行时,两端对齐会将除最后一行外的前面所有行按单词(空白字符)拆分均匀分散对齐。其实这个值对于行中的内联块元素同样有效。
为了使 text-align: justify
生效,你必须保证有多行(只对最后一行之前的行生效),必须保证相邻内联块元素中间有空白字符(并且字号不能为 0)。前者可以通过一个 display:inline-block; width: 100%
的伪元素实现(伪元素作最后一行);后者如果是手写的 HTML 基本一般通常都会有,如果是用 ng-repeat
、v-for
生成的元素就要非一些力气手工制造出来。
text-align: justify
这个相对 hack 的用法有很高的浏览器兼容性,需要兼容 IE8 的同学或多或少应该都有使用过,可能也遇到过各种各样的问题。后来的伸缩盒(flex
)布局中有更加好用的 justify-content: space-between
是此属性的绝佳替代品,这是后话。
还有一些 CSS 属性控制行系统的布局方式。除了前面的提到的 text-align
和 white-space
,还有 word-wrap
(控制折行时是否允许拆分单词)、text-overflow
(控制超出一行时的处理方式)等等。除此之外,常用的 margin
、padding
对于行内元素的效果也有不同之处,留给各位同学自己发现。
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。