这篇帖子是 https://www.v2ex.com/t/559626 11 楼问题的进一步讨论.
为什么单开个帖子?
1 V2EX 的回复不支持 Markdown
2 个人觉得这个回答还是有问题.这里人才多,如果哪里搞错了欢迎指出.
本文所讨论的中心点为: 如果"不定宽元素"内有"宽度为百分数的 img", 浏览器将如何计算容器和 img 的实际宽度.
注意:
1 除非提及,本文所有代码以 Chrome75 的效果为准.
2 本文中的"不定宽元素"是一个自造词,详见"不定宽元素"一节.
另外,本文是 stackoverflow 上这个讨论的总结,推荐直接去看原文.
举例:
有一个浮动元素(以下记为 container):
<div class="container" style="float: left"></div>
container 中有一些图片, 当图片的 width 值为百分比时,浏览器如何计算 container 和 img 的宽度?
<!DOCTYPE html>
<html lang="en">
<head>
<style>
body {
margin: 0;
font-size: 0;
}
</style>
</head>
<body>
<div id="container" style="position:absolute;width: max-content;">
<!-- #img1 的宽度为 300x200px -->
<img id="img1" src="https://i.imgur.com/fH2hTRa.jpg" style="width: 100%" />
<!-- #img2 的宽度为 200x100px -->
<img id="img2" src="https://i.imgur.com/Ed0juok.jpg" style="width: 50%" />
</div>
</body>
</html>
最终结果很有趣:
注意: 本文中的"不定宽元素"是一个自造词,用来代指其宽度由内容决定的元素.
例如:
绝对定位元素
<div style="position:absolute;"></div>
内联块元素
<div style="display:inline-block;"></div>
浮动元素
<div style="float:left;"></div>
如果未声明元素宽度(width: auto),部分元素会使用 shrink-to-fit width 作为元素的实际宽度.
If 'width' is computed as 'auto', the used value is the "shrink-to-fit" width.
如果没有指明宽度,则使用 shrink-to-fit width 计算实际的宽度值.
CSS2/visudet/zh-hans - HTML5 Chinese Interest Group Wiki
shrink-to-fit width 的计算方式如下:
shrink-to-fit width = min(max(preferred minimum width, available width), preferred width)
以下元素会采用 shrink-to-fit width 来计算实际宽度:
另外在 CSS3 中, 这些名词有不同的叫法:
fit-content size 的计算公式如下,其实和 CSS2 一样:
fit-content size = min(max-content size, max(min-content size, stretch-fit size))
CSS Intrinsic & Extrinsic Sizing Module Level 3
通常来讲, 样式 width: <precentage> 的实际宽度相对于 containing block (包含块)计算.
但在某些情况下, 包含块的宽度由子元素决定,子元素的宽度又依赖包含块.
例如:
浮动元素 #container 内有一个 #img.
因为 float,container 有多宽取决于内部的 img.
但是 img 的 width 值是个百分数, 也就是 img 有多宽取决于外部的 container.
如此下去,就变成死循环了.
<style> body {margin: 0;} </style>
<div id="container" style="float: left">
<!-- img 的原始尺寸为 300x200 -->
<img id="img" src="https://i.imgur.com/fH2hTRa.jpg" style="width: 100%" />
</div>
为了解决这个问题, CSS3 是这样做的:
When calculating the containing block ’ s size, the percentage behaves as auto.
忽略百分比的 width 值, 当作 width: auto 来处理.
CSS Intrinsic & Extrinsic Sizing Module Level 3
相当于:
<img id="img" src="https://i.imgur.com/fH2hTRa.jpg" style="width: 100%" />
-->
<img id="img" src="https://i.imgur.com/fH2hTRa.jpg" />
也就是,根据"图片的原始宽度"(300px)来计算 #container 的宽度.
这里又有了一个新问题.
上文提到了不定宽元素的宽度计算公式是: min(max(preferred minimum width, available width), preferred width).
那么, 这个"图片的原始宽度"(300px)会被带入到 preferred minimum width | available width | preferred width 这三个值中的哪个呢?
(本文假设 body 的宽度 width 为 1366px)
available width 指的是外层包含块的宽度, 即#container 的父元素 body 的宽度(1366px). 因此排除 available width:
min(max(preferred minimum width, 1366), preferred width)
preferred minimum width 和 preferred width 不太好判断,所幸 CSS3 提供了 min-content 和 max-content.
通过这两个属性值,可以直接得到 preferred minimum width 和 preferred width 的结果.
下面写两个 demo 看一下
preferred width:
<div id="container" style="width: max-content">
<!-- img 的原始尺寸为 300x200 -->
<img id="img" src="https://i.imgur.com/fH2hTRa.jpg" style="width: 100%" />
</div>
preferred width 的结果如下(300px),和图片的原始宽度一致.
preferred minimum width:
<div id="container" style="width: min-content">
<!-- img 的原始尺寸为 300x200 -->
<img id="img" src="https://i.imgur.com/fH2hTRa.jpg" style="width: 100%" />
</div>
preferred minimum width 的结果如下, 是 0?
如上图所示, preferred minimum width 的结果非常奇怪.所以我又写了几个测试:
1 先多加几个图片试试看
<div id="container" style="width: min-content">
<!-- img 的原始尺寸为 300x200 -->
<img id="img" src="https://i.imgur.com/fH2hTRa.jpg" style="width: 100%" />
<img id="img" src="https://i.imgur.com/fH2hTRa.jpg" style="width: 100%" />
<img id="img" src="https://i.imgur.com/fH2hTRa.jpg" style="width: 100%" />
</div>
结果如下图,还是 0.
2 加个字母进去试试
<div id="container" style="width: min-content">
<!-- img 的原始尺寸为 300x200 -->
<img id="img" src="https://i.imgur.com/fH2hTRa.jpg" style="width: 100%" />
<img id="img" src="https://i.imgur.com/fH2hTRa.jpg" style="width: 100%" />
<img id="img" src="https://i.imgur.com/fH2hTRa.jpg" style="width: 100%" />
aaaaaa aaaaaa
</div>
可以看到,这回图片能显示了.并且 #container 的宽度为其内部"最长单词的宽度".
根据如上的两个 demo 可以假设, 在这种特定的情况下(不定宽元素内存在百分比宽度的 img). Chrome 在计算 #container 元素的 preferred minimum width 时,忽略了内部"百分比宽度的" img, 将这些图片的宽度当作 0 来处理.
因此,#container 元素的 fit-content size 为 300px:
min(max(0, 1366), 300) = 300
Chrome 将图片的原始宽度作为 preferred width 处理,不考虑其高度(height),但是 Firefox 不同.
Firefox 会先计算 img 的高度(height), 然后根据图片原始的宽 /高的比例来计算 preferred width.
例如如下这段代码:
<div id="container" style="float: left">
<!-- img 的原始尺寸为 300x200 -->
<img id="img" src="https://i.imgur.com/fH2hTRa.jpg" style="width: 100%;height: 100px" />
</div>
图片元素的尺寸为 300x200,比例大约是 1.5:1.
忽略 width 后, 火狐会根据 height(高度)和比例值计算宽度,也就是 100 * 1.5 = 150.
因为, 图像的最终宽度为 150px.
Chrome 的结果:
Firefox 的结果:
值得一提的是, img 元素的宽度计算了两次.
第一次计算发生在 container 的宽度确定之前,用于确定 container 的宽度.
第二次计算发生在 container 的宽度确定之后,用于确定 img 自身的宽度.
2
pinews 2019-05-04 03:06:26 +08:00
@rabbbit 非常感谢你详细的讲解,把一件一般没人注意到的地方研究透彻,我虽然提出了疑问,却没有自己解决,很惭愧。
具体计算方式还有待消化,但是仿佛看到一个新的大门 |
3
Exin 2019-05-04 13:58:48 +08:00 via iPhone
有点意思 请问能否介绍一点系统地了解这些计算逻辑的资料?
|