跳到主要内容

vertical-align

2023年02月28日
柏拉文
越努力,越幸运

一、认识


vertical-align用来指定行内元素(inline)或表格单元格(table-cell)元素的垂直对齐方式。vertical-align属性可被用于两种环境:

  • 使行内元素盒模型与其行内元素容器垂直对齐。例如,用于垂直对齐一行文本内的图片<img>

  • 垂直对齐表格单元内容

注意: vertical-align只对行内元素行内块元素表格单元格元素生效:不能用它垂直对齐块级元素

二、语法


/* Keyword values */
vertical-align: baseline;
vertical-align: sub;
vertical-align: super;
vertical-align: text-top;
vertical-align: text-bottom;
vertical-align: middle;
vertical-align: top;
vertical-align: bottom;

/* <length> values */
vertical-align: 10em;
vertical-align: 4px;

/* <percentage> values */
vertical-align: 20%;

/* Global values */
vertical-align: inherit;
vertical-align: initial;
vertical-align: unset;

三、取值


3.1 相对行的值

下列值使元素相对整行垂直对齐:

  • top: 使元素及其后代元素的顶部与整行的顶部对齐。

  • bottom: 使元素及其后代元素的底部与整行的底部对齐

没有基线的元素,使用外边距的下边缘替代。

3.2 表格单元格的值

  • baseline: 使单元格的基线,与该行中所有以基线对齐的其它单元格的基线对齐。

  • top: 使单元格内边距的上边缘与该行顶部对齐。

  • middle: 使单元格内边距盒模型在该行内居中对齐。

  • bottom: 使单元格内边距的下边缘与该行底部对齐。

可以是负数。

3.3 相对父元素的值

  • baseline: 使元素的基线与父元素的基线对齐。HTML 规范没有详细说明部分可替换元素的基线,如<textarea> ,这意味着这些元素使用此值的表现因浏览器而异。

  • sub: 使元素的基线与父元素的下标基线对齐。

  • super: 使元素的基线与父元素的上标基线对齐。

  • text-top: 使元素的顶部与父元素的字体顶部对齐。

  • text-bottom: 使元素的底部与父元素的字体底部对齐。

  • middle: 使元素的中部与父元素的基线加上父元素 x-height(译注:x 高度)的一半对齐。

  • <length>: 使元素的基线对齐到父元素的基线之上的给定长度。可以是负数。

  • <percentage>: 使元素的基线对齐到父元素的基线之上的给定百分比,该百分比是line-height属性的百分比。可以是负数。

四、原理


Preview

4.1 什么是 baseline ?

字母xbaseline的关系: 字母x的下边缘(线)就是基线baseline。下图是英文字母在四线格中,每条线的含义(常用的英文本就是四线格的)。

Preview

4.2 如何确定 父元素(块级盒子)的 baseline 位置

由于行盒子的baseline是不可见的,无法直观的确定,如果所有子元素都设置对齐方式了,可以通过在父元素内容最后添加一个字符x来确定父元素的baseline。如果想直观的看下行盒子baseline的位置,可以通过在行盒子css类上添加一个after伪元素选择器,内容为x字符,那么行盒子的baseline就是x字母的下边缘线。但是行盒子的baseline位置会受里面子元素影响而移动(相应字母x的位置也就变动了)

.line-box::after {
content: 'x';
}

4.3 如何确定 inline-level elements(内联级元素)盒子的 baseline 位置

display属性计算值为:

  • inline: 内联元素的baseline, 是里面文本(即使没有字母x,可以想象文本中有一个字母x)字母x的下边缘线

  • inline-block: 内联块元素baseline位置的确定规则又分为以下三种(baseline位置参照下图):

    • inline-block元素盒子里,没有内容(流内内容),是一个空的盒子时: baseline位置就是该盒子margin-bottom的边界(没有margin-bottom值,就是盒子的边界值)

    • inline-block元素盒子里,有内容元素: 并且overflow属性值为visible时(默认值),那么该盒子的baseline位置就是里面最后一个内容元素的baseline

    • inline-block元素盒子里,有内容元素: 并且overflow属性值为非visible时 (比如overflow:hidden),那么该盒子的baseline位置就是该盒子margin-bottom的边界

    Preview

4.4 elements(内联级元素)盒子取不同的值,与块级盒子(父元素)baseline的对齐关系

  • 默认情况,不设置任何值:

    实时编辑器
    function Test(props) {
      const box = {
        border: "1px solid #000",
        position: "relative",
      }
      const item = {
        display: "inline-block",
        width: "100px",
        height: "100px",
        border: "1px solid red"
      }
      const xStyle = {
        backgroundColor: "#23a576"
      }
      const centerLine = {
        position: "absolute",
        top: "100px",
        height: "1px",
        width: "300px",
        background: "blue",
      }
      return (
         <div style={ box }>
           <div style={ centerLine }></div>
           <div style={ item }></div>
           <div style={ item }></div>
           <span style={ xStyle }>x</span>
         </div>
      );
    }
    
    结果
    Loading...

    备注:

    - 横线: 测试用,用来比对两个子元素和父元素对齐的位置

    - x : 先给行盒子内容最后添加一个字母 x 来直观的确定下行盒子的 baseline 位置

    现象: 两个子元素(红色边框的盒子)内容都为空。因此两个子元素(红色边框的盒子)的基线(baseline)都为盒子的底部边界(若有margin-bottom,则为margin-bottom)

    结论: 两个子元素的baseline和行盒子的baseline对齐,其实字母x所占据的空间,在baseline的下方还会有一部分。字母x的整个空间大小如图上绿色区域所示。所以就会有那么一条空白缝隙存在。即使父块级元素里没有字母x,只有两个子元素盒子,还会存在该缝隙。所以父块级元素里面,内容最后总是存在一个没有没有任何对齐方式的内联元素。这个也就是所谓的幽灵空白节点部分

    扩展: 那么基于verticalAlign方式如何去除幽灵空白节点部门呢? 现在是子元素的baseline基于父块级盒子的baseline对齐,导致行盒子里的字母x临近行盒子底部,字母x baseline 以下部分所占据的空间撑起这条缝隙。所以我们可以让子元素以别的位置(vertical-align: 除了baseline以外其他任何值)对齐父块级盒子的baseline,这样字母x就不会临近行父块级盒子底部,也就不会有所谓的缝隙了。

  • vertical-align: middle

    实时编辑器
    function Test(props) {
      const box = {
        border: "1px solid #000",
        position: "relative",
      }
      const item = {
        display: "inline-block",
        width: "100px",
        height: "100px",
        border: "1px solid red",
        verticalAlign: "middle"
      }
      const xStyle = {
        backgroundColor: "#23a576"
      }
      const centerLine = {
        position: "absolute",
        top: "50px",
        height: "1px",
        width: "300px",
        background: "blue",
      }
      return (
         <div style={ box }>
           <div style={ centerLine }></div>
           <div style={ item }></div>
           <div style={ item }></div>
           <span style={ xStyle }>x</span>
         </div>
      );
    }
    
    结果
    Loading...

    现象: 通过vertical-align: middle, 让子元素盒子垂直中点与父块级盒子的 baseline+字母x高度的一半对齐

  • vertical-align: text-top

    实时编辑器
    function Test(props) {
      const box = {
        border: "1px solid #000",
        position: "relative",
      }
      const item = {
        display: "inline-block",
        width: "100px",
        height: "100px",
        border: "1px solid red",
        verticalAlign: "text-top"
      }
      const xStyle = {
        backgroundColor: "#23a576"
      }
      const centerLine = {
        position: "absolute",
        top: "0px",
        height: "1px",
        width: "300px",
        background: "blue",
      }
      return (
         <div style={ box }>
           <div style={ centerLine }></div>
           <div style={ item }></div>
           <div style={ item }></div>
           <span style={ xStyle }>x</span>
         </div>
      );
    }
    
    结果
    Loading...

    现象: 子元素盒子的顶部和父块级盒子里内容区域的顶部对齐

  • vertical-align: text-bottom

    实时编辑器
    function Test(props) {
      const box = {
        border: "1px solid #000",
        position: "relative",
      }
      const item = {
        display: "inline-block",
        width: "100px",
        height: "100px",
        border: "1px solid red",
        verticalAlign: "text-bottom"
      }
      const xStyle = {
        backgroundColor: "#23a576"
      }
      const centerLine = {
        position: "absolute",
        top: "100px",
        height: "1px",
        width: "300px",
        background: "blue",
      }
      return (
         <div style={ box }>
           <div style={ centerLine }></div>
           <div style={ item }></div>
           <div style={ item }></div>
           <span style={ xStyle }>x</span>
         </div>
      );
    }
    
    结果
    Loading...

    现象: 子元素盒子的底部和父块级盒子里内容区域的底部对齐

  • vertical-align: sub

    实时编辑器
    function Test(props) {
      const box = {
        border: "1px solid #000",
        position: "relative",
      }
      const item = {
        display: "inline-block",
        width: "100px",
        height: "100px",
        border: "1px solid red",
        verticalAlign: "sub"
      }
      const xStyle = {
        backgroundColor: "#23a576"
      }
      const centerLine = {
        position: "absolute",
        top: "100px",
        height: "1px",
        width: "300px",
        background: "blue",
      }
      return (
         <div style={ box }>
           <div style={ centerLine }></div>
           <div style={ item }></div>
           <div style={ item }></div>
           <span style={ xStyle }>x</span>
         </div>
      );
    }
    
    结果
    Loading...

    现象: 降低子元素盒子的基线到父元素盒子下标的位置

  • vertical-align: super

    实时编辑器
    function Test(props) {
      const box = {
        border: "1px solid #000",
        position: "relative",
      }
      const item = {
        display: "inline-block",
        width: "100px",
        height: "100px",
        border: "1px solid red",
        verticalAlign: "sub"
      }
      const xStyle = {
        backgroundColor: "#23a576"
      }
      const centerLine = {
        position: "absolute",
        top: "100px",
        height: "1px",
        width: "300px",
        background: "blue",
      }
      return (
         <div style={ box }>
           <div style={ centerLine }></div>
           <div style={ item }></div>
           <div style={ item }></div>
           <span style={ xStyle }>x</span>
         </div>
      );
    }
    
    结果
    Loading...

    现象: 升高子元素盒子的基线到父元素盒子上标的位置

  • vertical-align: <percentage>

    实时编辑器
    function Test(props) {
      const box = {
        border: "1px solid #000",
        position: "relative",
      }
      const item = {
        display: "inline-block",
        width: "100px",
        height: "100px",
        lineHeight:"100px",
        border: "1px solid red",
        verticalAlign: "2%"
      }
      const xStyle = {
        backgroundColor: "#23a576"
      }
      const centerLine = {
        position: "absolute",
        top: "100px",
        height: "1px",
        width: "300px",
        background: "blue",
      }
      return (
         <div style={ box }>
           <div style={ centerLine }></div>
           <div style={ item }></div>
           <div style={ item }></div>
           <span style={ xStyle }>x</span>
         </div>
      );
    }
    
    结果
    Loading...

    现象: 百分比的值是相对该元素的line-height数值的(元素有默认行高的),具体的升高/降低数值由由该元素的line-height的值乘以百分比计算得出。相对自己baseline,升高或较低该元素一定距离。0%位置就是默认的baseline

  • vertical-align: length

    实时编辑器
    function Test(props) {
      const box = {
        border: "1px solid #000",
        position: "relative",
      }
      const item = {
        display: "inline-block",
        width: "100px",
        height: "100px",
        border: "1px solid red",
        verticalAlign: "4px"
      }
      const xStyle = {
        backgroundColor: "#23a576"
      }
      const centerLine = {
        position: "absolute",
        top: "100px",
        height: "1px",
        width: "300px",
        background: "blue",
      }
      return (
         <div style={ box }>
           <div style={ centerLine }></div>
           <div style={ item }></div>
           <div style={ item }></div>
           <span style={ xStyle }>x</span>
         </div>
      );
    }
    
    结果
    Loading...

    现象: 该值为一定的像素数值,与vertical-align:percentage效果类似,除了移动的距离是被计算出来的。

  • vertical-align: top:

    实时编辑器
    function Test(props) {
      const box = {
        border: "1px solid #000",
        position: "relative",
      }
      const item = {
        display: "inline-block",
        width: "100px",
        height: "100px",
        border: "1px solid red",
        verticalAlign: "top"
      }
      const xStyle = {
        backgroundColor: "#23a576"
      }
      const centerLine = {
        position: "absolute",
        top: "100px",
        height: "1px",
        width: "300px",
        background: "blue",
      }
      return (
         <div style={ box }>
           <div style={ centerLine }></div>
           <div style={ item }></div>
           <div style={ item }></div>
           <span style={ xStyle }>x</span>
         </div>
      );
    }
    
    结果
    Loading...

    现象: 子元素盒子的顶部相对行盒子的顶部对齐

  • vertical-align: bottom

    实时编辑器
    function Test(props) {
      const box = {
        border: "1px solid #000",
        position: "relative",
      }
      const item = {
        display: "inline-block",
        width: "100px",
        height: "100px",
        border: "1px solid red",
        verticalAlign: "bottom"
      }
      const xStyle = {
        backgroundColor: "#23a576"
      }
      const centerLine = {
        position: "absolute",
        top: "100px",
        height: "1px",
        width: "300px",
        background: "blue",
      }
      return (
         <div style={ box }>
           <div style={ centerLine }></div>
           <div style={ item }></div>
           <div style={ item }></div>
           <span style={ xStyle }>x</span>
         </div>
      );
    }
    
    结果
    Loading...

    现象: 子元素盒子的底部和行盒子的底部对齐

五、场景


5.1 左侧是一个文字,右侧是一个图标,进行水平垂直居中对齐

  • 初始情况,未设置任何值

    实时编辑器
    function Test(props) {
        const box = {
            border: "1px solid black"
        }
        const label = {
    
        }
        const icon = {
            display: "inline-block",
            width: "20px",
            height: "20px",
            backgroundColor: "blue"
        }
        return (
            <div style={ box }>
                <span style={ label }>颜色xx</span>
                <span style={ icon }></span>
            </div>
        );
    }
    
    结果
    Loading...

    现象: 可以看见,是文字的baseline(字母x的下边缘)和蓝色盒子的底部边界值(baseline)对齐

  • 设置文字和图标的vertical-align:middle

    实时编辑器
    function Test(props) {
        const box = {
            border: "1px solid black"
        }
        const label = {
            verticalAlign: "middle"
        }
        const icon = {
            display: "inline-block",
            width: "20px",
            height: "20px",
            backgroundColor: "blue",
            verticalAlign: "middle"
        }
    
        return (
            <div style={ box }>
                <span style={ label }>颜色xx</span>
                <span style={ icon }></span>
            </div>
        );
    }
    
    结果
    Loading...

    现象: 现在文字和图标已经水平对齐了,理论上是文字图标垂直中点已经和行盒子的基线+字母x高度的一半对齐

    问题: 文字(span标签)的上方(注意是上方)与行盒子(父元素)之间有条缝隙。其实行盒子最后,会存在一个我们看不见的文本盒(w3c称作struct),这里我们通过添加一个伪元素,添加一个字母x,直观的模拟下,该文本盒也会有默认大小(除非font-size设为0)的。所以该缝隙,就是由于文本盒本身空间所占据的

    优化: 通过给父元素设置font-size:0, 子元素单独设置font-size 来去掉缝隙

  • 经过font-size 优化之后, 设置文字和图标的vertical-align:middle

    实时编辑器
    function Test(props) {
        const box = {
            fontSize: "0px",
            border: "1px solid black"
        }
        const label = {
            fontSize: "14px",
            verticalAlign: "middle"
        }
        const icon = {
            display: "inline-block",
            width: "20px",
            height: "20px",
            backgroundColor: "blue",
            verticalAlign: "middle"
        }
        return (
            <div style={ box }>
                <span style={ label }>颜色xx</span>
                <span style={ icon }></span>
            </div>
        );
    }
    
    结果
    Loading...
  • 给父元素高度之后,需要给父元素添加line-height: 父元素内容区高度 再设置文字和图标的vertical-align: middle

    实时编辑器
    function Test(props) {
        const box = {
            width: "200px",
            height: "200px",
            fontSize: "0px",
            lineHeight: "calc(200px - 2px)",
            border: "1px solid black",
            position: "relative",
        }
        const label = {
            fontSize: "14px",
            verticalAlign: "middle"
        }
        const icon = {
            display: "inline-block",
            width: "20px",
            height: "20px",
            backgroundColor: "blue",
            verticalAlign: "middle"
        }
        const line = {
            position: "absolute",
            width: "200px",
            height: "1px",
            top: "100px",
            backgroundColor: "red",
        }
        return (
            <div style={ box }>
                <span style={ label }>颜色xx</span>
                <span style={ icon }></span>
                <div style={ line }></div>
            </div>
        );
    }
    
    结果
    Loading...