[译] CSS 内在尺寸教程(min-content、max-content 和 fit-content )

4,721 阅读8分钟

原文链接:Intrinsic Sizing In CSS,by Ahmad Shadeed

CSS 中存在两种尺寸:内在尺寸(intrinsic)和外在尺寸(extrinsic)。为元素的 widthheight 设置的固定属性值,就是指外部尺寸,这是最常用的方式。而内部尺寸,则是由元素包含的内容量决定的。

本篇将会详细介绍控制内在尺寸的每个值,看看它们的使用能带来什么好处。同时,我还会举例说明这些值与 CSS Grid 布局/其他属性如何结合使用。

外在尺寸

外在尺寸是指用精确的值指定元素尺寸。举个例子:

.c-button {
    width: 100px;
}

这个按钮的尺寸是 100px,这就是外在尺寸了。再比如一个 <div> 元素,它默认是个块级元素,就是说它的宽默认等于 100% 父元素的宽。

有时,我们想要根据元素实际的内容来设置尺寸,这个时候,使用外在尺寸就没有用了。接下来,我们来看看如何是用内在属性值来解决问题的吧!

min-content

min-content 值表示内在最小宽度(译注:这里说的不准确,也可以是“最小高度”,因此应该称为“最小尺寸”),它等于元素内容里最长的那个单词宽度。

CSS Working Group (CSSWG) 的说法是:

The inline size that would fit around its contents if all soft wrap opportunities within the box were taken.

坦率地讲,我太理解 CSSWG 关于这个概念的定义。我自己是这么理解的:我在元素的周围包裹了一个框,元素内容(很多单词)会因此而折行,这个框的宽度将等于元素中最长的那个单词。如果我这里表述有误的话,希望大家来更正。

举一个例子,有一个标题元素:

<h2>A title for an awesome article</h2>
h2 {
  width: min-content;
}

intrinsic-sizing-1.png
大家可以看见,当元素的 width 设置成 min-content 后,其宽度等于最长单词的宽度,也就是这里的“awesome”。

image.png

A pen by Ahmad Shadeed

max-content

max-content 表示元素的内在首选宽度(intrinsic preferred width),它等于元素内容的宽。

还举一个标题元素的例子,不过这次改用 max-content 了:

h2 {
  width: max-content;
}

 
intrinsic-sizing-2.png
注意观察,元素宽度等于标题宽度了。这个宽度是动态的,随着标题内容的改变,max-content 所代表的值也相应改变。

fit-cotent

这个属性可以看成是 min-contentmax-content 的结合。搜索的时候,我发现 Stackoverflow 上的这个回答我很喜欢:

fit-content 默许使用 max-content;如果 available < max-content,那就使用 available;如果 available < min-content,那就使用 min-content

画一个流程图表示,就是下面这样的:

intrinsic-sizing-3.png
注意,这里的“available”表示元素在视口中的可用空间。

🤔 译注
上面的流程图跟 Stackoverflow  上的回答比起来,并不十分清晰,反而是后者更容易理解起来。关于 Stackoverflow  上的回答我的理解如下:

fit-content 属性到底取用何值,跟当前元素的可用空间(available)是紧密相关的。

1. 如果可用空间充足,那就用你元素的 max-conetnt 又何妨,反正够装你的。
2. 如果可用空间不够充裕,比 max-conetnt 小点,那就得用可用空间的值了,这才显得合适(fit),不会导致内容越界。
3. 如果可用空间很少,甚至比 min-conetnt 还小,那不好意思,得用 min-content 了,不然显示会很难看,就不合适了。

让我们举个例子看看它是如何工作的。

h2 {
    width: fit-content;
}

查看下面的 GIF 图,观察标题尺寸是如何随着视口的变化做调整的。

fit-content.gif

我们再回顾一下关于 fit-content 的知识:如果当前的可用空间比 max-content 还大的话,width 就等于 max-content;如果可用空间比 max-content 的小的话,那么 width 就等于可用空间的宽了;最后,如果可用空间比 min-content 还小,那么 width 就等于 min-content

到目前为止,我解释完了每一个内在值。让我们转向真实的案例来看下吧。

使用案例

<figure><caption>

现在我们假设有一个带 <caption><figure>,因为它是个块级元素,因此默认等于 100% 父元素宽度的。

<figure>
    <img src="blueberry-cheesecake-x.png" alt="">
    <figcaption>..</figcaption>
</figure>

intrinsic-figure-0.png
我们预期的行为是希望 <figure> 包裹图像的,使用 max-content 就能取得这些效果,让 <figure> 与内容(最大)宽度一样。

figure {
    width: max-content;
    margin-left: auto;
    margin-right: auto;
}

intrinsic-figure.png
但有一个问题,如果图片比视口范围还大的话,这时因为 <figure> 的宽度就等于图片的宽度,因此就会导致水平滚动条的出现。

intrinsic-figure-2.png

为了解决这个问题,我们就要为图片使用 fit-content 了。它不会让大图片超出视口之外显示。

figure {
    width: fit-content;
}

image.png

A pen by Ahmad Shadeed

带分隔符的标题

在这个例子里,“Top Stories”要折成两行显示。宽度还是动态的,就是说不管实际标题如何,都需要折行。为了实现这个效果,我们可以使用 min-content

span {
    width: min-content;
}

image.png

A pen by Ahmad Shadeed

带下划线的标题

内在值的另一个有趣的用例是带边框的标题,要求边框跟标题内容一样长。考虑下图:

intrinsic-title.png

注意,标题是个块状元素。为了得到上述的效果,以前的做法是将内容包装在 <span> 标签里,给 <span> 设置边框效果。

<h2><span>A title for an awesome article</span></h2>
h2 span {
    border-bottom: 2px solid #e2deed;  
}

当然,我们还可以借助 fit-content 来让标题宽度与内容一样长。

h2 {
    width: fit-content;
    margin-left: auto; /* For centering */
    margin-right: auto; /* For centering */
    border-bottom: 2px solid #e2deed;
}

image.png

A pen by Ahmad Shadeed

导航


如果一个页面导航的宽度是基于内容的。那么,可以通过使用 max-content,轻松实现效果:

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

Demo

Todo 列表

在网上找内在值的使用案例的时候,看到了 一篇文章,将我吸引了。

考虑下面的例子:

这个 Todo 列表包含:页眉、列表和页脚。

不管有多少列表项,中间部分的高度都应该是 100% - header - footer。为了实现这个效果,我们可以将 CSS Grid 与 min-content 搭配使用。

.c-todo {
  display: grid;
  grid-template-rows: min-content auto min-content;
  height: 100vh;
}


你可能想知道,如果我们不使用 min-content,会是什么结果呢?好吧,来看看:

聊天窗口

设想构建一个聊天程序。在下面的示例里,布局结构与 Todo 列表非常相似。当没有\聊天记录,并且没有使用 min-content 时,就会导致布局遭到破坏。

Demo

Hero

假设我们页面 <header> 里包含一个 Hero 组件。我们的目标是让这个 Hero 组件实现动态布局,占据一屏里剩下的空间。

.c-section {
    display: grid;
    grid-template-rows: min-content 1fr;
}

有两行内容,第一行使用的是最小内容高度,而第二行则可以扩展、填充完剩下的可用空间。

image.png

A pen by Ahmad Shadeed

Sidebar 和 Main

我总是想知道为什么我们非要给侧边栏一个固定宽度呢。如果它的宽度是基于内容的呢?比如说,它有一个基于内容的最小宽度和一个最大宽度?我们来试试。

<div class="wrapper">
  <aside></aside>
  <main></main>
</div>
.wrapper {
    grid-template-columns: fit-content(150px) 1fr;
    grid-gap: 1rem;
}

通过使用 CSS Grid fit-content 函数,我们可以确保侧边栏的宽度不会超过 150px,并且可以在内容很短的情况下缩小到 150px 以下。

当侧边栏的内容很少时,它会缩小一点:

🤔译注

这的 fit-content() 文中并没有讲到。可以这样简单理解:

一般使用这个功能函数时,所用参数一般都是小于元素的 max-content 值的。比如 fit-content(150px),那么说 150px 是小于元素 max-content 值的。这种情况下,元素最终的渲染尺寸是介于 min-content ~ 150px 之间的。具体是多少还要看当前的可用空间,如果可用空间充足,拿就显示最大的 150px,如果可用空间小,那么就取 min-content ~ 150px 之间的某个值,如果可用空间不足(比 min-content 还小),那就用最小值 min-content,不能再小了。

当然,如果 fit-content() 里的值比元素 max-content 还大,那么元素最终的渲染尺寸是介于 min-content ~ max-content 之间的。具体是多少还要看当前的可用空间,与上面类似。

反正,fit-content() 返回的最小值是 min-content,不能比 min-content 更小了。

更多信息,查看 MDN 上的 fit-content() 文档

标题和描述

我们有一个标题和一个描述文本。描述文字的宽度不能超过主标题的宽度。对我来说,这是一个有趣的用例,我以前认为它不可能仅用 CSS 就能实现。

看下下面的模型。

为了实现上述功能,我们需要将 min-content 设置为包装元素的宽度,而为标题元素设置 width: max-content

section {
  width: min-content;
  margin: 0 auto;
  text-align: center;
}

h2 {
  width: max-content;
}

请注意,上面代码需要在移动端做些调整额,否则可能会导致导致水平滚动条的出现。

image.png

A pen by Ahmad Shadeed

浏览器兼容性

根据 Can I Use 数据,除了 Microsoft Edge (EdgeHTML) 之外的所有主流浏览器都支持内在值的设置了。

注意:Microsoft Edge (Chromium)  从发布的第一个版本(79)就已经支持了。

image.png

(正文完)


广告时间(长期有效)

我有一位好朋友开了一间猫舍,在此帮她宣传一下。现在猫舍里养的都是布偶猫。如果你也是个爱猫人士并且有需要的话,不妨扫一扫她的【闲鱼】二维码。不买也不要紧,看看也行。

(完)