深入理解CSS:line-height、vertical-align(转载)

2023-11-23 18:32

本文主要是介绍深入理解CSS:line-height、vertical-align(转载),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

转至: https://www.cnblogs.com/wfeicherish/p/8884903.html

说在前面:通过这次深入学习CSS的line-height和vertical-align属性,对CSS真是刮目相看,决心开始深入CSS学习哈哈。

一、从一个常见的需求开始

在一行中,左侧图标,图标右侧是文字,并且图标和文字在这行垂直居中。

上代码:

<div class="bg"><img src="hello.jpg"/><span class="span1">abgxxx</span><span class="span2">abgxxx</span>
</div>

在这里插入图片描述
  
  图片看着很正常,内联元素按顺序在一行排列,但是当给外层div加上背景色和边框之后就发现问题了。

.bg {background-color: lightblue;border: black solid 1px;
}

图片下方出现了空白,这是由于img元素的vertical-align属性默认为baseline,baseline意味着元素的基线通父元素的基线对齐,父元素的基线为字母x的下边缘(线),但是像图片或者输入框这种元素,本身没有基线,则是将其底端通父元素的基线对齐。
在这里插入图片描述
  那么图片下方的空白处的高度是怎么确定的呢?

.bg span{background-color: lightgreen;
}

在这里插入图片描述

给后面的span加上背景色后,看出空白的高度即字符的基线baseline和bottom之间的距离,而这个距离是由line-height决定的,而line-height的默认值是normal,那normal 是什么呢?我们经常将 normal 理解为 1,或者 1.2,就连CSS规范中也没有讲明白normal究竟代表多少。我们知道 line-height 的值为数字时,表示的相对于 font-size 的倍数,但问题在于,font-size:100px 对应的文字在不同字体里的高度是不一样的!那么 line-height 会随着文字大小的改变而改变吗? normal 真的表示 1 或者 1.2 吗?vertical-align 又是如何被 line-height 影响的呢?

让我们来深入理解一个不那么简单的 CSS 机制。(下文中字体度量摘自方老师的译文,见参考资料1)

二、font-size和字符度量

下面是一段简单的 HTML 代码,一个 p 标签包含了 3 个 span 标签,每个 span 各自有一个 font-family:

<p><span class="a">Ba</span><span class="b">Ba</span><span class="c">Ba</span>
</p>  
p  { font-size: 100px }
.a { font-family: Helvetica }
.b { font-family: Gruppo    }
.c { font-family: Catamaran }

(注:这几款字体我们的电脑上可能没有,在我们自己电脑上可以设置三个字体查看区别:monospace、默认字体Microsoft YaHei、fantasy,font-size均设置为40px,见下方)

<div class="bg" style="background-color:  lightblue;border: black solid 1px;">    <img src="hello.jpg"><span class="span1" style="font-family: monospace;background-color: lightgreen;font-size: 40px;">abgxxx</span><span style="font-size:40px;">abgxxx</span><span style="font-family: fantasy;background: lightcoral;font-size: 40px;">abgxxx</span>
</div>

在这里插入图片描述
在这里插入图片描述
  font-size 相同,font-family 不同,得到的 span 元素的高度也不同!

为什么 font-size: 100px 不能得到相同高度的元素呢?测量了一下每个 span 的高度:Helvetica 115px,Gruppo 97px,Catamaran 164px。
  在这里插入图片描述

乍看很奇怪,但是仔细想想,这么做又是很有道理的。原因在于字体本身,这是字体的原理:

  • 一款字体会定义一个 em-square,它是用来盛放字符的金属容器。这个 em-square 一般被设定为宽高均为 1000 相对单位,不过也可以是 1024、2048 相对单位。
    在这里插入图片描述
  • 字体度量都是基于这个相对单位设置的,包括 ascender、descender、capital height、x-height 、linegap等。注意这里面的值是允许相对于 em-square 出血(bleed outside)的,可以理解为超出 em-square
      来几张图:
    在这里插入图片描述  
    在这里插入图片描述
     在这里插入图片描述

(注:后面两张标注的字体度量值中的leading不是很明确,原谅我实在没找到合适的图描述linegap即leading的,看到下文vertical-align:normal的取值过程,你就能明白了!请耐心,继续往下看哦)

  • 在浏览器中,上面的 1000 相对单位会按照设定的font-size 缩放。
    我们把 Catamaran 字体放到 FontForge 中,分析它的字体度量:
  • em-square 是 1000
  • ascender 是 1100,descender 是 540。通过测试发现,macOS 上的浏览器使用了 HHead Ascent 和 HHead Descent 值,Windows 上的浏览器使用了 Win Ascent 和 Win Descent(而且两个平台上的值不一样)。我们还看到 Capital Height 是 680,X height 是 485。
      在这里插入图片描述

这意味着 Catamaran 字体占据了 1100 + 540 个相对单位,尽管它的 em-square 只有 1000 个相对单位,所以当我们设置 font-size:100px 时,这个字体里的文字高度是 164px。这个计算出来的高度决定了 HTML 元素的 content-area(内容区)。

我们还能看出大写字母的高度是 68px,小写字母的高度(x-height)是 49px。所以 1ex = 49px,1em = 100px,而不是 164px。(em 是基于 font-size,而不是基于计算出来的高度)
在这里插入图片描述

当 p 元素出现在屏幕上时,它可能包含了多行内容,每行内容由多个内联元素组成(内联标签或者是包含文本的匿名内联元素),每一行都叫做一个 line-box。line-box 的高度是由它所有子元素inline-box的高度计算得出的。浏览器会计算这一行里每个子元素的高度,再得出 line-box 的高度(具体来说就是从子元素的最高点到最低点的高度),所以默认情况下,一个 line-box 总是有足够的高度来容纳它的子元素。

每个 HTML 元素实际上都是由多个 line-box 的容器,如果你知道每个 line-box 的高度,那么你就知道了整个元素的高度。

如果我们修改一下最初的 HTML 代码:

<p>Good design will be better.<span class="a">Ba</span><span class="b">Ba</span><span class="c">Ba</span>We get to make a consequence.
</p>

那么就会得到 3 个 line-box(宽度固定):

  • 第一行和最后一行各有一个匿名内联元素(文本内容)
  • 中间一行包含两个匿名内联元素和三个 span

在这里插入图片描述

我们清楚地看到第二个 line-box 比其他两个要高一些。因为第二行里面的子元素因为有一个用到了 Catamaran 字体的 span。

line-box 的难点在于我们看不见它,而且不能用 CSS 控制它。即使我们用 ::first-line 给第一行加上背景色,我们也看不出第一个 line-box 的高度。

三、line-height

上文中已经提到过content-area,它的高度是由字体度量决定的,而line-box 的高度是根据子元素的高度计算出来的。

对于一个内联元素,它有两个高度:content-area(内容区高度)和vitual-area(实际高度),实际高度就是 line-height,这个高度用于计算 line-box 的高度

line-height 并非表示两个 baseline 之间的距离。

在这里插入图片描述
 在这里插入图片描述

virtual-area 和 content-area 高度的差异叫做 leading。leading 的一半会被加到 content-area 顶部,另一半会被加到底部。因此 content-area 总是处于 virtual-area 的中间。

计算出来的 line-height(也就是 virtual-area 的高度)可以等于、大于或小于 content-area。如果 virtual-area 小于 content-area,那么 leading 就是负的,因此 line-box 看起来就比内容还矮了。

还有一些其他种类的内联元素:

  • 可替换的内联元素,如 img / input / svg 等
  • inline-block 元素,以及所有 display 值以 inline- 开头的元素,如 inline-table /
    inline-flex
  • 处于某种特殊格式化上下文的内联元素,例如 flexbox 元素中的子元素都处于flex formatting
    context(弹性格式化上下文)中,这些子元素的 display 值都是「blockified」

这类内联元素,其高度是基于 height、margin、padding 和 border 属性。如果将 height 设置为 auto的话,那么其高度的取值就是 line-height,其 content-area 的取值也是 line-height。

在这里插入图片描述

下面解释 line-height:normal 是什么意思

回到 FontForge,Catamaran 的 em-square 高度是 1000,同时我们还看到很多其他的 ascender/descender 值:
在这里插入图片描述

  • 常规的 Ascent/Descent:ascender 是 770,descender 是 230,用于渲染字符。
  • 规格 Ascent/Descent:ascender 是 1100,descender 是 540。用于计算 content-area
    的高度
  • 规格 Line Gap:用于计算 line-height: normal。

在 Catamaran 这款字体中,Line Gap 的值是 0,那么 line-height: normal 的结果就跟
content-area 的高度一样,是 1640 相对单位。

为了对比,我们再看看 Arial 字体,它的 em-square 是 2048,ascender 是 1854,descender 是 434,line gap 是 67。那么当 font-size: 100px 时,

  • 其 content-area 的高度就是 100/2048*(1854+434) = 111.72,约为 112px;
  • 其 line-height: normal 的结果就是 100/2048*(67+1854+434) 约为 115px。

所有这些值都是由字体设计师设置的。

这么看来,line-height:1 就是一个很糟糕的实践。当 line-height 的值是一个数字时,其实就是相对 font-size 的倍数,而不是相对于 content-area。所以 line-height:1 很有可能使得 virtual-area 比 content-area 矮,从而引发很多其他的问题。
  在这里插入图片描述
  line-box 计算的一些细节:

  • 对于内联元素,padding 和 border 会增大 background 区域,但是不会增大 content-area(不是 line-box 的高度)。一般来说你无法再屏幕上看到 content-area。margin-top 和 margin-bottom对两者都没有影响。
  • 对于可替换内联元素(replaced inline elements)、inline-block 元素和 blockified 内联元素,padding、margin 和 border 会增大 height(译者注:注意 margin),因此会影响 content-area 和 line-box 的高度

四、vertical-align

vertical-align 属性,它也是计算 line-box 高度的重要因素之一。

它的默认值是 baseline。还记得字体度量里的 ascender 和 descender 吗?这两个值决定了 baseline 的位置。很少有字体的 ascender 和 descender 的比例是一比一的,所以我们经常看到一些意想不到的现象,下面是例子。

代码如下:

    <p><span>Ba</span><span>Ba</span></p>
    p {font-family: Catamaran;font-size: 100px;line-height: 200px;}

一个 p 标签内有两个 span 标签,span 继承了 font-family、font-size 和 200px 的 line-height。这时两个 span 的 baseline 是等高的,line-box 的高度就是 span 的 line-height。

在这里插入图片描述

如果第二个 span 的 font-size 变小了

span:last-child {font-size: 50px;
}

我们会发现一个非常line-box 的高度变高了!如下图所示。提示你一下,line-box 的高度是从子元素的最高点到最低点的举例。
在这里插入图片描述

我们来看另一个例子。p 标签有 line-height:200px,内含一个 span,span 继承了 p 的 line-height。

  <p><span>Ba</span></p>
p {line-height: 200px;
}
span {font-family: Catamaran;font-size: 100px;
}

此时 line-box 的高度是多少?貌似是 200px,但其实不是。这里需要考虑到的问题是 p 有自己的 font-family,默认值是 serif。p 的 baseline 和 span 的 baseline 位置不一样,因此最终的 line-box 比我们预想的要高一些。出现这种问题是因为浏览器认为每个 line-box 的起始位置都有一个宽度为 0 的字符(CSS 文档将其称为 strut),并将其纳入 line-box 的高度的计算中。

看不见的字符,看得见的影响。

为了说明这个问题,我们画图解释一下这个问题。
  在这里插入图片描述
  用 baseline 来对齐令人费解,如果我们用 vertical-align: middle 会不会好一点呢?CSS 文档说明:middle 的意思是「用父元素 baseline 高度加上父元素中 x-height 的一半的高度来对齐当前元素的垂直方向的中点」。baseline 所处的高度跟字体有关,x-height 的高度也跟字体有关,所以 middle 对齐也不靠谱。更糟糕的是,一般来说,middle 根本就不是居中对齐!内联元素的对齐受太多因素影响,因此不可能用 CSS 实现。

顺便一说,vertical-align 的其他 4 个值有可能有点用:

  • vertical-align: top / bottom,表示与 line-box 的顶部或底部对齐
  • vertical-align: text-top / text-bottom,表示与 content-area 的顶部或底部对齐
    在这里插入图片描述

不过依然要小心,大部分情况下,对齐的是 virtual-area,也就是一个不可见的高度。看看下面这个用 vertical-align:top 的例子:
在这里插入图片描述

最后,vertical-align 的值也可以是数字,表示根据 baseline 升高或降低,不建议使用数字。

  • line-box 的高度的受其子元素的 line-height 和 vertical-align 的影响
  • 我们无法轻易的用 CSS 来控制字体度量

小结:

1、字体度量相关概念:

字体度量是指对于指定字号的某种字体,在度量方面的各种属性,其描述的参数包括:

  • em-square: 相对单位
  • baseline: 字符基线
  • ascender: 字符最高点到baseline的距离
  • descender: 字符最低点到baseline的距离
  • linegap(leading): line-height为normal时的字符实际高度与content-area的差
  • capitalHeight: 大写字母顶部到baseline的距离

2、line-height:

语法 line-height : normal | <实数> | <长度> | <百分比> | inherit
说明设置元素中行的高度 
  
 normal默认行高,一般为1.1.2,基于字体度量计算出来的,计算公式如上
 实数实数值,缩放因子,相对于font-size的倍数,而不是content-area,可能得出一个比 virtual-area 还要矮的 content-area
 长度合法的长度值,可以取负值
 百分比百分比取值基于元素的字体尺寸
初始值normal 
继承性继承 
计算值长度和百分比值为绝对值,其他为指定值 
  • 当内容中含有图片时,如果图片的高度大于行高,则含有图片行的line box将被撑开到图片的高度(虽然撑开了linebox,但不会影响line-height,因此也不会影响到基于行高来计算的其他属性)
  • 所有的内联元素都有两个高度:基于字体度量的 content-area、virtual-area(也就是 line-height),这两个高度都无法看到

3、vertical-align:

语法

vertical-align : baseline | sub | super | top | text-top | middle | bottom | text-bottom | <百分比> | <长度> | inherit
说明设置元素内容的垂直对齐方式 
参数  
 baseline基线对齐:使元素的基线同父元素的基线对齐,像如片图片或者输入框这类替换元素,本身没有基线,则将其底端同父元素的基线对齐
 sub下标
 super上标:使元素的基线(替换元素的底端)相对于父元素的基线升高,移动的幅度CSS规范中没有规定
 top顶端对齐:将元素的行内框的顶端同行框的顶端对齐,即与line-box的顶部对齐
 text-top与文本的顶端对齐:将元素的行内框的顶端同文本行的顶线对齐,即与content-area顶部对齐
 middle中部对齐:通常使用在图片上,将图片的垂直方向的中线在基线位置上方半个ex的高度,注意ex值与字体尺寸有关
 bottom底端对齐
 text-bottom文本的底端对齐
 百分比和长度CSS2,可为负数
初始值baseline 
继承性不继承 
适用于行内元素和单元格(table-cell)元素 
计算值百分比和长度值为绝对长度;其他同指定值 

line-box 计算:

  • 对于内联元素,padding 和 border 会增大 background 区域,但是不会增大 content-area(不是line-box 的高度)。一般来说你无法再屏幕上看到 content-area。margin-top 和 margin-bottom对两者都没有影响
  • 对于可替换内联元素(replaced inline elements)、inline-block 元素和 blockified内联元素,padding、margin 和 border 会增大 height(译者注:注意 margin),因此会影响
    content-area 和 line-box 的高度
  • 受其子元素的 line-height 和 vertical-align 的影响

这篇关于深入理解CSS:line-height、vertical-align(转载)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/419918

相关文章

Vue项目中Element UI组件未注册的问题原因及解决方法

《Vue项目中ElementUI组件未注册的问题原因及解决方法》在Vue项目中使用ElementUI组件库时,开发者可能会遇到一些常见问题,例如组件未正确注册导致的警告或错误,本文将详细探讨这些问题... 目录引言一、问题背景1.1 错误信息分析1.2 问题原因二、解决方法2.1 全局引入 Element

详解如何在React中执行条件渲染

《详解如何在React中执行条件渲染》在现代Web开发中,React作为一种流行的JavaScript库,为开发者提供了一种高效构建用户界面的方式,条件渲染是React中的一个关键概念,本文将深入探讨... 目录引言什么是条件渲染?基础示例使用逻辑与运算符(&&)使用条件语句列表中的条件渲染总结引言在现代

详解Vue如何使用xlsx库导出Excel文件

《详解Vue如何使用xlsx库导出Excel文件》第三方库xlsx提供了强大的功能来处理Excel文件,它可以简化导出Excel文件这个过程,本文将为大家详细介绍一下它的具体使用,需要的小伙伴可以了解... 目录1. 安装依赖2. 创建vue组件3. 解释代码在Vue.js项目中导出Excel文件,使用第三

Java实现Excel与HTML互转

《Java实现Excel与HTML互转》Excel是一种电子表格格式,而HTM则是一种用于创建网页的标记语言,虽然两者在用途上存在差异,但有时我们需要将数据从一种格式转换为另一种格式,下面我们就来看看... Excel是一种电子表格格式,广泛用于数据处理和分析,而HTM则是一种用于创建网页的标记语言。虽然两

一文带你理解Python中import机制与importlib的妙用

《一文带你理解Python中import机制与importlib的妙用》在Python编程的世界里,import语句是开发者最常用的工具之一,它就像一把钥匙,打开了通往各种功能和库的大门,下面就跟随小... 目录一、python import机制概述1.1 import语句的基本用法1.2 模块缓存机制1.

深入理解C语言的void*

《深入理解C语言的void*》本文主要介绍了C语言的void*,包括它的任意性、编译器对void*的类型检查以及需要显式类型转换的规则,具有一定的参考价值,感兴趣的可以了解一下... 目录一、void* 的类型任意性二、编译器对 void* 的类型检查三、需要显式类型转换占用的字节四、总结一、void* 的

深入理解Redis大key的危害及解决方案

《深入理解Redis大key的危害及解决方案》本文主要介绍了深入理解Redis大key的危害及解决方案,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着... 目录一、背景二、什么是大key三、大key评价标准四、大key 产生的原因与场景五、大key影响与危

vue解决子组件样式覆盖问题scoped deep

《vue解决子组件样式覆盖问题scopeddeep》文章主要介绍了在Vue项目中处理全局样式和局部样式的方法,包括使用scoped属性和深度选择器(/deep/)来覆盖子组件的样式,作者建议所有组件... 目录前言scoped分析deep分析使用总结所有组件必须加scoped父组件覆盖子组件使用deep前言

VUE动态绑定class类的三种常用方式及适用场景详解

《VUE动态绑定class类的三种常用方式及适用场景详解》文章介绍了在实际开发中动态绑定class的三种常见情况及其解决方案,包括根据不同的返回值渲染不同的class样式、给模块添加基础样式以及根据设... 目录前言1.动态选择class样式(对象添加:情景一)2.动态添加一个class样式(字符串添加:情

深入理解C++ 空类大小

《深入理解C++空类大小》本文主要介绍了C++空类大小,规定空类大小为1字节,主要是为了保证对象的唯一性和可区分性,满足数组元素地址连续的要求,下面就来了解一下... 目录1. 保证对象的唯一性和可区分性2. 满足数组元素地址连续的要求3. 与C++的对象模型和内存管理机制相适配查看类对象内存在C++中,规