图解CSS: 元素尺寸的设置

发布于 大漠

众所周之,Web上的任何一个元素都是一个盒子(框模型),对于该盒子在CSS中有一个专业的术语,即CSS盒模型。其中CSS的display属性可以改变盒子的格式化上下文,每个格式化上下文都拥有自己不同的渲染规则,而这些规则是用来决定其子元素是如何定位,以及和其他元素的关系,这些关系中也包括了盒子大小。换句话说,Web中的任何一个元素都是有大小的,即使没有给元素设置任何有关于大小的属性。了解如何设置元素大小是非常重要的,因为它们直接会影响一个元素的渲染,特别是在页面布局的时候非常重要,而且在使用Flexbox和Grid布局时,元素大小变得就越来越重要。另外在Flexbox和Grid中有很多内置的灵活性,因为它们管理项目在容器中怎么显示。

在CSS中有一个独立的功能模块(CSS Intrinsic & Extrinsic Sizing Module Level 3)是用来定义元素大小的。接下来的内容,我们就来一起探讨该模块中的相关属性与特性。

容器的尺寸由谁来决定

Web上的元素就是一个容器,它们就好比生活中的器皿,通过格式化之后有不同的形态:

不同的器皿都有着自己的尺寸大小。而其大小可以是容器的内容决定,也可以显式的通过属性来控制。大家最为熟知的应该是CSS盒模型中的属性来决定一个元素的大小。

CSS2中可以通过widthheightmin-widthmin-heightmax-widthmax-heightcolumn-width来显式指定容器大小。这些属性可以接受autononemin-contentmax-contentfit-content()以及CSS值和单位指定的值。除此之外,盒模型中的borderpadding以及box-sizing等属性也会直接影响容器的大小。但是在Flexbox和Grid布局中还有部分属性会根据Flexbox容器或Grid容器来修改其子元素的大小,哪怕是给这些元素显式设置了指定的大小。这个我们后面会详细的介绍。

了解一些术语

在深入和大家一起探讨论元素尺寸大小设置之前,我们有必要对一些专业术语做一些了解。这样能更好的帮助大家更好的理解接下来的内容。

尺寸(Size)

我们在Web中看到的元素,一般情况都是一个平面的,定义这个元素的大小一般由这个元素的**宽(width高(height)**来决定,只不过定义和计算widthheight的方式不同。而我们所说的尺寸就是widthheight构成,但随着CSS逻辑属性的出现,定义元素的尺寸不再局限于widthheight这样的物理属性来定义,而改成inline-sizeblock-size来定义(CSS盒模型中的物理属性有着一一对应的逻辑属性)。

改用逻辑属性来定义元素的大小,有其明显的优势,那就是随着HTML的dir属性或者CSS的directionwriting-mode属性会让宽度有可能不是宽度,高度有可能不是高度。这有可能让你无法理解,我们来看一个简单的示例:

改变dirwriting-mode的值,就可以看出物理属性和逻辑属性之间的差异:

内部尺寸(Inner Size)和外部尺寸(Outer Size)

熟悉CSS盒模型的同学都知识,根据盒子不同的边缘可以将盒子划分成不同类型的盒子,比如content-boxpadding-boxborder-boxmargin-box

其中content-box对应的尺寸被称为内部尺寸,而margin-box对应的尺寸被称为外部尺寸

明确的尺寸(Definite Size)和 不确定的尺寸(Indefinite Size)

明确的尺寸指的是不需要执行布局就可以确定盒子的大小。也就是说,显式给容器设置一个固定值、或内容所占区域的大小、或一个容器块的初始大小,或通过其他计算方式得到的尺寸(比如Flexbox和Grid布局中的“拉伸和压缩(Strretch-fit)”)。

不确定的尺寸指的是一个未知的尺寸。换句话容器的大小具备无限空间(即有可能很大,也有可能很小)。

通俗一点来理解的话,明确的尺寸是知道容器的width(或inline-size)和height(或block-size);不确定的尺寸是需要根据内容来计算的,所以要知道不确定的尺寸需要先检查内容。

可用空间(Available Space)

可用空间是表示盒子被放置的空间,它由所参与的格式化上下文的规则来决定。一个盒子可用的空间通常是一个包含它的块容器(该容器尺寸是个明确大小)或一个无限大小(容器尺寸不确定)。

正如上图所示,带斜线的容器有的时候正好容纳子元素,这个时候可用空间为0,有时候还有剩余的空间,这个时候可用空间就是容器宽度减去所有子元素宽度和其之间的间距,有的时候子元素和其之间的宽度超过容器宽度,内容被溢出,这个时候可用空间是一个负值。

可用空间在Flexbox和Grid布局中非常的有用,Flex项目和Grid项目在一些场景中会根据容器可用空间来进行拉伸和压缩。

另外,可用空间会受最小内容(min-content)和最大内容(max-content)约束。

自动尺寸

在CSS中任何一个容器有四种自动计算尺寸大小的方式:

  • auto:会根据格式化上下文自动计算容器的尺寸
  • min-content:是在不导致溢出的情况下,容器的最小大小
  • max-content:容器可以容纳内容的最大在小,如果容器中包含未格式化的文本,那么它将显示为一个完整的长字符串
  • fit-content:如果给定轴中的可用空间是确定的,则等于min(max-content, max(min-content, stretch-fi)),反之等于max-content

了解了这些术语之后,我们接着往下阅读,看看如何设置容器尺寸。

指定容器尺寸的方式

在CSS中我们可能通过下面这些属性给一个元素显式指定尺寸:

物理属性 逻辑属性(horizontal-tab) 逻辑属性(vertical-lr) 逻辑属性(vertical-rl)
width inline-size block-size block-size
height block-size inline-size inline-size
min-width min-inline-size min-block-size min-block-size
min-height min-block-size min-inline-size min-inline-size
max-width max-inline-size max-block-size max-block-size
max-height max-block-size max-inline-size max-inline-size

用于定义元素尺寸的属性上的值,可以是下面这些:

属性名称 描述
auto 如果给widthheight设置值为auto时 容器的大小将会以容器内容来计算,会是一个自动大小; 如果给min-widthmin-height设置值为auto,将会指定一个自动计算好的最小值。
none 如果取值为none时,元素盒子的大小是没有任何限制
<length-percentage> 使用<length><percent>指定元素容器的大小,在适当的情况下,根据框的包含块的widthheight来解析百分比
min-content 如果指定了内联轴(Inline Axis),那么min-content对应的大小则是内联大小(Inline Size);否则将表现为属性的初始值。即固有的最小宽度
max-content 如果指定了内联轴,那么max-content对应的大小则是内联大小,否则将表现为属性的初始值。即固有的首选宽度
fit-content() 如果显式指定了内联轴,使用了fit-content()函数,可以用指定的参数替换可用空间,即 min(max-content, max(min-content, <length-percentage>));否则将表现为属性的 初始值。对于内在尺寸,fit-content(<length>)表现长度值(length)。如果fit-content()使用了百分比值的话,将会作为min-content当作最小内容,max-content作为最大内容

注意,上面表格中的属性都既可以用于物理属性也可以运用于逻辑属性,其中取值为<length>是和CSS的单位有关

其中min-contentmax-contentfit-content()对于很多同学来说是新东西,在后面的内容,我们会结合Flexbox和Grid布局和大家一起深入讨论这几个属性值怎么运用于项目中。来更好的帮助我们完成Web布局。

容器自动尺寸

你会发现,设置容器尺寸大小的属性都有一个默认值,即**auto**。简单地说,在Web中的任何一个元素都是一个盒子,该盒子都有大小,如果用物理尺寸来描述的话就是width x height,如果用逻辑属性来描述的话就是inline-size x block-size。另外,盒子有着自己的形态(格式化上下文),不对的格式化上下文对于容器的自动尺寸计算有着关键性的影响。而且容器自动尺寸的计算是根据容器内容或子元素来计算的,即使是该容器没有任何内容,它也会有自动尺寸,只不过有可能是0 x 0(在没有设置盒模型其他属性时)。来看个小示例:

<div></div>
<span></span>

<div>我是一个块元素</div>
<span>我是一个内联元素</span>

我们在样式上没有做任何的设置,浏览器解析出来的结果如下(下图截取于Chrome浏览器):

注意,div取得值和浏览器视窗大小有关。

当你使用display改变元素的格式化时,容器的自动计算也会相应发生变化:

div {
    display: inline;
}

span {
    display: block;
}

div的表现形为如同span,而span的表现形为如同div

如果将display的值设置为inline-blockgridflexinline-gridinline-flex自动计算取出来的值都会有所不同。另外,元素容器通过display将其更换成Flexbox或Grid容器时,其自动计算也会有变化。是不是感觉很复杂,在CSS的世界中的确如此。不过你也不用过于担心,只要你对CSS的display视觉格式化模型相关的知识,你就知道是为什么了。因此这里就不一一向大家演示取不同值的效果了。感兴趣的同学,可以自己验证一下自己的想法。

容器宽度设置

首先我们来看看容器宽度相关的属性。从上面的内容中我们可以得知,显式给一个容器设置宽度的属性主要有widthmin-widthmax-width,它们中的每一个都很重要,都有着自己的运用场景。

在接下来的内容中,主要还是拿物理属性举例,其对应的逻辑属性使用原理也是类似,唯一不同的是,逻辑属性在多语言布局中更为友好,可以随着语言的排列方式,自动匹配我们物理属性中的宽高。

width

在CSS中我们可以给任何一个非内联的元素设置一个width属性,用来指定容器的宽度。只不不同的属性值渲染出来的结果有所差异。我们来看一个简单的示例:

从上面的示例可以看出,使用width属性设置宽度时:

  • 如果使用固定单位,比如px,当容器后元素元素总宽度超过容器宽度时候,会造成断行;如果使用white-space: nowrap造成成内容溢出
  • 如果使用相对单位,比如%,容器宽度会根据其父元素width来计算,其他相对单位的计算,可以参考《CSS 的值和单位》一文
  • min-contentmax-contentfit-content()等属性值我们放到后面来介绍

当给一个容器的width设置一个固定值时,有一个极大的缺陷(内容断行内容溢出):

很多时候我们并不知道容器的内容会是什么,所占宽度是多少,这就会造成上图的现象。哪怕是使用了min-contentmax-contentfit-content()也会面临这现象。

min-width

首先能运用于width的值都可以用于min-width属性。不同的是,min-width可以防止width值小于min-width指定的值所造成的布局缺陷。

注意:min-width的默认值是auto,解析出来的值为0

在上面的示例中,我们知道容器width设置的值小于内容宽度的时候会造成一定的缺陷(我们不希望看到的效果)。如果我们在上面的示例中,将width换成min-width就可以较好的避免这一现象。比如下面这个示例:

尝试着点击按钮编辑文本内容,你会发现容器宽度自动变长适合内容,不会造成断行或内容溢出:

而且min-width运用于多语言网站上也特别有用:

在多语言版本网站中表达同一个意思的文本内容所占宽度是不一致的,比如(中文的“确定”,英文的“Done”,阿拉伯文的“تم”)。当你从中文切换到阿拉伯文(或英文切换到阿拉伯文)时,文本内容宽度不一致。加上未显式给容器设置widthmin-width属性,就会造成按钮大小不一致(如上面示例所示),这也很有可能会影响Web的布局或整体的视觉效果。如果显式给容器设置width就有可能会造成内容断行或溢出,那么这个时候设置一个min-width就会有一个效果好的效果。比如下面这个示例:

使用min-width的好处大家都看到了,在内容较长的时候,min-width可以扩展容器的宽度,为了具有更好的视觉效果,我们应该在水平方向添加一定的padding值:

max-width

在设置max-width属性,它的好处在于防止width属性的值超过了max-width指定的值。max-width的默认值为none

max-width的一个常见而简单的用例是将它与图像一起使用。考虑下面的例子:

<img>比它的父容器更大。通常给img设置max-width: 100%,图像的宽度不会超过容器的宽度。如果图像比父容器小,则max-width: 100%不会对象图像产生实际影响,因为它比容器小。

属性权重

CSS中的widthmin-widthmax-width都可以来设置元素的宽度。很多时候,在实际的使用过程中可能会有多个属性同时用来设置容器宽度。比如widthmin-widthwidthmax-width或者同时都运用到同一个容器上。那么问题就来了,它们都可以用来指定容器的宽度,那么哪个权重更大。换句话说,最终是哪个属性的值会运用于元素。

widthmin-width

我们先来看widthmin-width同时用于元素上的情景:

从上面的示例中我们可以得出一个简单地结论:

width大于min-width时,会取width的值;当width小于min-width时,会取min-width的值

widthmax-width

从上面的示例中我们可以得出一个简单地结论:

width大于max-width时,会取max-width的值;当width小于max-width时,会取width的值

min-widthmax-width

如果min-width大于max-width,那么min-width值将作为元素的宽度;如果min-width小于max-width,那么min-width值将作为元素的宽度

也就是说,当widthmin-widthmax-width同时出现在一个元素上时,将会按下面的规则来决定元素的宽度:

  • 元素的width大于max-width时,元素的width等于max-width,即 max-width能覆盖width
  • 元素的width小于min-width时,元素的width等于min-width,即 min-width能覆盖width
  • min-width大于max-width时,min-width优先级将高于max-width

容器高度设置

容器高度的设置和宽度的设置非常的相似,在CSS中可以使用heightmin-heightmax-height三个属性来设置,这些属性同样接受auto<length-percentage>min-contentmax-contentfit-content(<length-percentage>)等属性值。

这里不做过多的阐述,因为他们的使用和widthmin-width以及max-width是一样的,只不过方向改变了。

最小和最大属性的用例

通过前面的学习,我们了解了min-widthmax-widthmin-heightmax-height的使用,接下来我们来看一些这些属性的使用用例(常见和不常见的)。

标签列表

在Web中,我们时常可以看到一些标签列表:

在构建这样的标签列表时,建议限制一个标签的最小宽度,这样一来,当内容很短时,它的外观就不会受到影响。

有了这种灵活性,无论内容有多短,标签在视觉上看起来都不错。另外,除了考虑内容过短之外,我们还需要考虑内容过长时对UI的视觉影响,这时候我们可以考虑使用max-width属性,并且配合其他的CSS的属性,比如text-overflow: ellipsis。这样做的好处是,当内容超出max-width所设置的值,那么内容将会被截取,并以三个...结尾,告诉用户这里有省略的内容。比如下面这个示例:

使用Flexbox时将min-width设置为0

在Flexbox布局中有很多奇异的现象,其实这并不是Bug,这其实是Flexbox的特性。在上面的标签列表示例中,如果我们把.tag中的displayinline-block换成inline-flex,你会发现text-overflow并无法生效:

造成这种现象的主要原因是:min-width的默认值是auto,客户端计算出来的值为0。但在Flexbox布局中Flex项目的min-width不会计算为0,而是等于其内容的大小。默认情况下,Flex项目不会缩小到它们的最小内容(最长单词或固定大小元素的长度)以下。要更改此设置,就需要对min-width设置为0(在Flex项目中的min-height使用也是类似,需要设置为0)。

我们来看一个简单示例:

@kizmarh的博客介绍了一种更为灵活的overflow的运用,很有创意的一种思路,整个效果如下:

使用Flexbox时将min-height设置为0

另外一种场景,overflow也会触发Flexbox的边缘情况。比如业务上有这样的需求:

虚线框中内容是带有滚动的。因为容器的高度是固定的,内容很有可能会超过容器的高度。在目前Flexbox布局为主流布局的场景下,很有可能会在滚动容器上显式设置flex:1

在滚动容器中很有可能显式设置overflow-y为scroll时,滚动并没有生效,而且在部分浏览器直接会把布局整乱:

事实上,这真不是Bug,在结果上和样式的正确使用,就不会出现这样的现象。比如:

对应的HTML结构也很简单:

<div class="main-container"> // flex-direction: column;
    <div class="fixed-container">Fixed Container</div> // height: 100px;
    <div class="content-wrapper"> // min-height: 0; 
        <div class="overflow-container">
            <div class="overflow-content">
            Overflow Content
            </div>
        </div>
    </div>
</div>

关键样式代码:

.main-container {
    display: flex;
    flex-direction: column;
}

.fixed-container {
    height: 100px;
}

.content-wrapper {
    display: flex;
    flex: 1;
    min-height: 0; /* 这个很重要*/
}

.overflow-container {
    flex: 1;
    overflow-y: auto;
}

具体原因和min-width是一样的。

限制每行字数

在《编写CSS时要考虑可访问性》中我们可以获知,“阅读很长的文本对我们的眼晴来说是很累的”。最适合阅读的标准:

每行65个字符(是罗马字母的2.5倍)通常被认为是最完美的度量标准

CSS中的ch单位和max-width可以很好地帮助我达到这样的效果。ch单位等于元素字体中0字符宽度。

这样一来,我们只需要在段落中设置:

p {
    max-width: 65ch;
}

就可以控制每行的字数。需要注意的是ch较为适合英文,如果针对中文的话,建议使用em单位(一个em就是一个中文汉字的宽度)。

未知高度

在某些情况下,我们面临的场景不知道内容的高度,比如手风琴,下拉菜单等。特别是制作一些动效的情景之下,我们使用max-height可能是一个很好的解决方案。比如下面这个示例:

当菜单按钮被点击,菜单应该从上到下滑动,而且这有个动滑动的动效。如果没有固定的高度(个人不建议使用固定的高度),那么这个展开的动效要是使用纯CSS实现是不太可能的。如果我们换成max-height就有可能。这个想法是为高度添加大的值,例如max-height: 20rem,这可能是不会达到的,然后我们可以在@keyframes中设置一个max-height020rem的变化:

使用max-width/height和视窗单位实现流体的宽高比

在《CSS实现长宽比的几种方案》一文中介绍了不同方式实现的宽高比的方案。

在不久的未来,我们可以直接使用CSS的属性aspect-ratio直接实现宽高比。除此之外,我们还可以借助视窗单位和max-width以及max-height实现一种流式(响应式)宽高比效果。

我们有一张尺寸为644px * 1000px的图像。如果要实现流式的宽高比(响应式),我们需要:

  • 纵横比:height/width
  • 容器的宽度:可以是固定的数值,也可以是流式的(比如100%
  • 设置height为视窗高度乘以纵横比
  • 设置max-height,即容器宽度乘以纵横比
  • 设置max-width为容器宽度

如果用代码来表述话,如下所示:

img {
    --ratio: 664 / 1000;
    --container-width: 30em;
    display: block;
    height: calc(100vw * var(--ratio));
    max-height: calc(var(--container-width) * var(--ratio));
    width: 100%;
    max-width: var(--container-width);
}

调整视窗大小,你可以看到效果如下:

媒体查询中的媒体特性

上面我们看到有关于min-widthmax-widthmin-heightmax-height的示例都是给一个元素设置尺寸。其实在CSS的媒体查询中也会用到这些属性。在媒体查询的媒体特性中用来做来查询条件:

大多数的媒体特性都可以带有minmax的前缀,比如min-widthmax-width,用于表达“最小的...”或“最大的。...”:

使用minmax前缀是主要为了避免与HTML或XML中的<>字符相冲突。如果你觉得使用<>符更易于理解的话,那么可以使用postcss-media-minmax插件来帮助你。

比如下面这样的示例:

如果你想向最小宽度的20em的手持设备或屏幕应用样式表,你可以使用下面这样的媒体查询规则:

@media handheld and (min-width: 20em), screen and (min-width: 20em) {
    /* 样式规则 */
}

如果你想大宽度在20em36em之间的屏幕运用不同的样式规则,则可以使用下面这样的媒体查询规则:

@media screen and (min-width: 20em) and (max-width: 36em) {
    /* 样式规则 */
}

如果你想大最小宽度为375px和最小高度为812px的屏幕上运用相应的样式规则,则可以像下面这样写:

@media only screen and (min-width: 375px) and (min-height: 812px) {
    /* 样式规则 */
}

在我们实际生产中,有关于min-widthmin-heightmax-widthmax-height的使用示例还有很多种。这里就不一一介绍了,如果你有更好的实例,欢迎在下面的评论中与我们一起共享。

Flex项目上的min-width/heightmax-width/height

在Flexbox布局中Flex项目除了widthmin-widthmax-width可以指定宽度之外,还可以通过flex-basis来设置。而Flex项目的宽度计算是件复杂的事情。这里不做详细的阐述,如果你对这方面感兴趣的话,可以阅读:

在Flexbox布局中设置Flex项目的尺寸大小时有一个隐式的公式存在:

contentwidthflex-basis

简单地说,如果未显式指定flex-basis的值,那么flex-basis将回退到width属性;如果未显式指定width,那么flex-basis将回到基于Flex项目内容的计算宽度。既然如此,Flex项目最终尺寸会受到min-widthmax-width属性限制。这一点必须得注意。

前面也提到过,在CSS中的同一个元素同时出现widthmin-widthmax-width来控制元素的宽度时会受三者权重关系的影响。同样的,当flex-basismin-widthmax-width同时出现时,也可以套用该权重,最终客户端会决定哪个值运用于元素上。这就是为什么 flex-basis会受制于min-widthmax-width

来看几个小示例。先来看flex-basismin-width的结合:

.item {
    flex-basis: 10vw;
    min-width: 15vw;
}

在该示例中,.item同时出现了widthflex-basismin-width。首先根据前面的法则一:

content ➜ widthflex-basis

简地说,该示例中.itemwidth会被忽略。接着我们再根据前面的法则二:

元素的width小于min-width时,元素的width等于min-width,即min-width能覆盖width

将该法则套到flex-basis中来:

如果flex-basis小于min-width时,Flex项目的宽度会取值min-width,即min-width覆盖flex-basis

因此,就该示例来说,最终运用Flex项目宽度的值是min-width,客户端计算出来的值是252px

我们再来看一个flex-basismax-width的示例。

.item {
    flex-basis: 15vw;
    max-width: 12vw;
}

同样的将前面的规则套进来,不同的是第二条规则换成:

元素的width大于max-width时,元素的width等于max-width,即max-width能覆盖width

套到flex-basis中来。

flex-basis大于max-width,Flex项目的宽度等于max-width,即max-width能覆盖flex-basis

所以最终用于Flex项目width的值是max-width,即12vw,客户端计算出来的值是201.594px

同样的,如果在Flex项目中同时运用了flex-basismin-widthmax-width时,将会根据另外一条规则来进行判断:

min-width大于max-width时,min-width优先级将高于max-width

那么套到Flex项目中来:

  • flex-basis大于max-width,Flex项目的宽度等于max-width,即 max-width能覆盖flex-basis
  • 如果flex-basis小于min-width时,Flex项目的宽度会取值min-width,即 min-width覆盖flex-basis

由于min-width大于max-width时会取min-width,有了这个先取条件我们就可以将flex-basismin-width做权重比较,即:flex-basis会取于min-width。反过来,如果min-width小于max-width时则依旧会取max-width,同时要是flex-basis大于max-width就会取max-width

上面关系看上去有点乱乱的,但如果能一步一步将相应的规则套进来,则会很清晰,也会很简单。比如下面这个示例:

.item {
    width: 10vw;
    flex-basis: 12vw;
    max-width: 15vw;
    min-width: 18vw;
}

你可以看到最终用于Flex项目的则是min-width的值18vw,客户端计算出来的值是302.4px

如果你理解了的话,可以使用更简单的规则来决定用于Flex项目的尺寸。

首先根据contentwidthflex-basis来决定用哪个来决定用于Flex项目。如果Flex项目显式设置了flex-basis属性,则会忽略contentwidth。而且min-width是用来设置Flex项目的下限值;max-width是用来设置Flex项目的上限值

虽然使用flex-basis显式设置Flex项目的尺寸优于widthheight,但这仅是理想尺寸。因为在Flexbox布局中就算是你显式设置了flex-basis也不能保证这将是Flex项目的最终尺寸。那是Flex项目放入Flex容器之后一切都将有可能改变。前面也提到过了,Flex项目的实际尺寸大小会被flex的另外两个属性flex-growflex-shrink的影响。

简单地说,如果容器有足够多的空间,Flex项目会根据扩展因子扩展到足够填充Flex容器;反之,如果容器没有足够多的空间容纳Flex项目时,Flex项目会根据收缩因子缩小到足够被容纳在Flex容器中

理解max-contentmin-contentfit-content()

在设置容器宽度和高度的属性上都可以设置max-contentmin-contentfit-content()等属性值。而这些属性值在CSS的体系中是一个全新的尺寸体系。为了更好的帮助大家理解这几个属性值,我们先来回忆一下CSS 2.1中的尺寸体系。

CSS 2.1的尺寸体系

在CSS 2.1的世界中,常见的尺寸体系可能有以下几种:

允分利用可用空间

在HTML中有一些块元素(块盒子),如果不做任何样式的设置,客户端对这些块元素的宽度解析的值是100%:

这种允分利用可用空间的行为常称为**fill-available**。

收缩与包裹

在CSS中我们可以通过一些样式改为元素在浏览器中默认显示的行为,比如在一个div元素上使用display改变其格式化上下文,比如使用floatposition让元素脱离文档流。其实这个时候也会让元素有一个收缩行为,根据内容将元素大小收缩到最合适之处(刚刚好容纳下元素内容)。这种行为事实上和CSS的fit-content很相似。我们来看一张图,可能会更好理解一些:

收缩到最小

大家是否还记得table用来布局,或者做数据统计表的时候,会发现单元格td的宽度总是会受到其他单元格的影响,哪怕是显式的给td设置了width。该表现行为和CSS的min-content是相似的。

超出容器限制

这个大家应该熟悉,比如在文本内容中有url长地址出现时,就有可能会超出容器显式。在CSS中使用white-space: nowrap也可以让文本不断行,超出容器显式。CSS的max-content和这个表现行为有类似之处。

min-contentmax-contentfit-content基本概念

这几个关键字扩展了CSS大小属性,这些关键字表示基于内容的内部大小和基于上下文的外部大小,允许CSS更容易地描述适合其内容或适合特定布局上下文的盒框型(框)。其具体的含义是:

min-content

如果指定了内联轴(Inline Axis),那么min-content对应的大小则是内联大小(Inline Size);否则将表现为属性的初始值。即固有的最小宽度。

如果用到CSS Grid布局中来表述的话,它代表单元格最小宽度,可以不让内容溢出单元格。除非是不可避免的。比如下面这个示意图,就是在minmax()函数中的最小值和最大值都设置min-content,也更好的说明了min-contentmax-content的差异:

max-content

如果指定了内联轴,那么max-content对应的大小则是内联大小,否则将表现为属性的初始值。即固有的首选宽度。

同样的用CSS Grid来举例的话,max-content它代表了单元格“最理想的大小”。单元格最小的宽度围绕着它的内容。例如,如果单元格的内容是一个句子,理想的单元格的宽度是整个句子的长度,无论长度是多少,单元格的内容都不会断行。如下图所示:

使用CSS的minmax()函数可以很好的阐述min-contentmax-content间的差异,在稍后的示例中也会和大家介绍

fit-content()

如果显式指定了内联轴,使用了fit-content()函数,可以用指定的参数替换可用空间,即 min(max-content, max(min-content, <length-percentage>)) ;否则将表现为属性的 初始值。对于内在尺寸,fit-content(<length>)表现长度值(length)。如果fit-content()使用了百分比值的话,将会作为min-content当作最小内容,max-content作为最大内容。

其实除了上面这三个关键词之外,还有一个fill关键字,目前只能将fill要用的话需要使用-webkit-fill-available来替代。表示的意思是“使用fill-available行内尺寸或者fill-available块级尺寸其中一种来作为合适的书写模式”。

min-contentmax-contentfit-content使用示例

接下来先来看看这几个关键词怎么使用,带来的变化是什么样的。首先在示例中创建一个div,该div作为容器,里成包含了一个img和两个段落标签:

<div class="content" id="variable">
  <img src="https://static.fedev.cn/sites/default/files/blogs/2020/2001/avatar_207.png" alt="">
  <p>Extrinsic sizing d...</p>
  <p>Web自1989到今年刚好走过30年历程。。。</p>
</div>

div没有显式设置width时,它通常会水平扩展到容器允许的宽度。在垂直方向上,它可以根据自己的内容进行折叠。比如下图所示:

上图就是div这样的块元素默认渲染行为。

如果在div元素上运用min-content

.min-content {
    width: min-content;
}

容器宽度会成为最窄的宽度,其大小适合最宽的子元素。在这个示例中,是img元素的宽度:

如果我们把img元素删除,那么宽度将是最长英文单词的宽度:

继续做减法,把英文这个段落删除,只留下中文:

如果把div的宽度设置为:

.max-content {
    width: max-content;
}

你会发现效果就好比在p元素上设置了:

p {
    white-space: nowrap;
}

div中的内容不会被分解成更小的块,而是完全不考虑容器的大小,直接溢出容器(除非内容总宽度不足容器的宽度之外):

如果把divwidth设置为fit-content,将会试图容纳最宽的内容,同时仍然尊重它的容器:

要是感兴趣的话,可以尝试着选择不同的值,查看对应的效果:

min-contentmax-contentfit-content在Flexbox布局中的运用

我们来看看min-contentmax-contentfit-content在Flexbox中的布局。这里所指的是运用Flex项目上的flex-basis属性。然而从W3C的规范中我们可以获知,到目前为止flex-basis只接受content<width>两个值。而这里的<width>所能接受的是<length><percentage>autoinherit值。

注意,此处的<width>指的是CSS的属性值,和CSS的width属性不是同一个东东

言外之意,min-contentmax-contentfit-contentflex-basis将是无效的。是不是这样,来看一个真实的案例:

你可以发现,将min-contentmax-contentfit-content运用于flex-basis时,客户端会将其视为无效的属性值:

前面我们提到过,在Flexbox布局中设置Flex项目的尺寸大小时有一个隐式的公式存在:

contentwidthflex-basis

将上面的示例调整一下:

调整下拉选项的值,你可以看到Flex项目的变化:

注意,在这个示例中有效果,并非是flex-basis生效,正因为min-contentmax-contentfit-content运用在flex-basis属性上时是个无效属性值;再根据Flex项目计算规则,当flex-basis无效或未设置时,Flex项目的计算根据width来计算,但这几个属性值是可以运用于width属性上。因此上面示例有效果是正常的

有关于这方面的介绍还可以阅读《Flexbox: How Big Is That Flexible Box?》一文。

min-contentmax-contentfit-content在Grid布局中的运用

虽然min-contentmax-contentfit-content不能运用于Flex项目的flex-basis属性上。但幸运的是可以用于CSS Grid布局中,可以使用这些关键词来指定网格轨道的宽度。

将上面的示例改成Grid布局:

有关于这方面更详细的介绍,还可以阅读《How Big Is That Box? Understanding Sizing In CSS Layout》或 《Intrinsic Sizing In CSS》一文。再给大家上一张来自@shadeed9在Intrinsic Sizing In CSS提供的一张图:

改变容器尺寸计算方式

了解CSS盒模型的同学都知道,CSS的border-widthpadding都会影响元素盒子的尺寸计算。

比如下面这段代码:

div {
    width: 100%;
    padding: 1rem;
}

会让div超出容器,类似下图这样:

面对这样的场景时,就需要借助CSS的box-sizing属性,它可以更好的帮助我们控制盒子的大小:

用个实例来解释,这样更易于理解:

CSS的内部尺寸和外部尺寸

如果你足够仔细的话,你可能已经发现了,在CSS中给一个元素框设置大小时,有的是根据元素框内在的内容来决定,有的是根据上下文来决定的。根据该特性,CSS的尺寸也分为内部尺寸和外部尺寸。

内部尺寸

内部尺寸指的是元素根据自身的内容(包括其后代元素)决定大小,而不需要考虑其上下文。

而其中min-contentmax-content能根据元素内容来决定元素大小,因此它们统称为内部尺寸

外部尺寸

外部尺寸指的是元素不会考虑自身的内容,而是根据上下文来决定大小。最为典型的案例,就是widthmin-widthmax-width等属性使用了%单位的值。

有关于这部分更详细的介绍,还可以参阅规范中的第四部分

小结

在CSS中任何一个元素都是一个盒子,该盒子有大小。其大小除了依据自身内容来决定大小之外,还可以通过一些属性来决定,比如大家熟悉的width/heihtmin-width/min-heightmax-width/height以及大家有可能不太熟悉的逻辑属性inline-sizeblock-sizemin-inline-sizemin-block-sizemax-inline-sizemax-block-size等。而且在CSS Intrinsic & Extrinsic Sizing Module Level 3规范中还为这些属性提供了一些新的属性值min-contentmax-contentfit-content等。这些新特性可以让我们更好的依赖元素内容来实现布局。

特别是在CSS Grid布局中min-contentmax-contentfit-content能更好的帮助我们控制网格轨道宽度。在文章中我们也简单地用一个示例向大家演示了。感兴趣的同学可以自己深入的了解一下。