CSS 中的行

哪里说起呢?就从一道老生常谈的前端面试题开始吧。

块级元素与行内元素

问:块级元素和行内元素有什么区别?

估计各位同学都在各种前端面试中被问到过,当然也在各种网站上搜寻过答案。要真正罗列两者的区别可以找到很多,然而从本质上讲,就是这样的区别:

  • 块级(block)元素可以控制它的大小
  • 行内(inline)元素将其排到父级的行系统中

我更愿意把这两者称作为元素的两个特性。因为行内元素具有 inline 特性,所以多个行内元素会排在一行;但它们不具有 block 特性,所以无法使用 widthheight 指定它们的宽高。因为块级元素具有 block 特性,所以可以直接指定元素的宽高;因为块级元素不具有 inline 特性,所以它们会独占一行。

这两个特性还可以叠加:同时具有 inlineblock 特性的就是行内块(inline-block)元素,既可以设定宽高,也会排到行系统中。如果两个特性都不具备,那么就是 display: none

注意这里所说的 blockinline 都是相对于同级元素而言的,对于元素的子级又有不同的布局方式。如果指定不同子节点的布局方式与 blockinline 特性叠加,又有了 table(具有 block 特性、子元素使用 table 方式布局)、inline-table(具有 inlineblock 特性,子元素使用 table 方式布局),flexinline-flexgridinline-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)的概念。

《CSS 中的行》

可以看到图片中的后几个字母 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,除了常用的 leftcenterright 取值以外,还有一个很有用的取值:justify——两端对齐。在只有一行时,两端对齐就相当于左对齐;但是有多行时,两端对齐会将除最后一行外的前面所有行按单词(空白字符)拆分均匀分散对齐。其实这个值对于行中的内联块元素同样有效。

为了使 text-align: justify 生效,你必须保证有多行(只对最后一行之前的行生效),必须保证相邻内联块元素中间有空白字符(并且字号不能为 0)。前者可以通过一个 display:inline-block; width: 100% 的伪元素实现(伪元素作最后一行);后者如果是手写的 HTML 基本一般通常都会有,如果是用 ng-repeatv-for 生成的元素就要非一些力气手工制造出来。

text-align: justify 这个相对 hack 的用法有很高的浏览器兼容性,需要兼容 IE8 的同学或多或少应该都有使用过,可能也遇到过各种各样的问题。后来的伸缩盒(flex)布局中有更加好用的 justify-content: space-between 是此属性的绝佳替代品,这是后话。

还有一些 CSS 属性控制行系统的布局方式。除了前面的提到的 text-alignwhite-space,还有 word-wrap(控制折行时是否允许拆分单词)、text-overflow(控制超出一行时的处理方式)等等。除此之外,常用的 marginpadding 对于行内元素的效果也有不同之处,留给各位同学自己发现。

    原文作者:CarterLi
    原文地址: https://segmentfault.com/a/1190000010811938
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞