前端大佬帮忙看一下这个关于 img 设置 max-width 后的行为问题

282 天前
 xfn

问题在 so 上也问过( https://stackoverflow.com/questions/78135768/behavior-of-img-width-when-max-width-is-set ),但好像回复都没有抓住我想问题的要点(也可能是我没表述清楚),在这里请 v2 的前端大佬帮忙看下。

问题是,我有一个 ul ,里面的 li 包含了一个 img 和一个 span (相当于一个 icon 和一个 label ,img 显式设置了宽度和高度),li 设置为了 display:flex 。而这个 ul 本身又在一个 div 下面,并且这个 div 的宽度可能不足以容纳 ul 的内容。如果 div 的宽度比 ul 窄,同时如果 li 里面的 img 设置了 max-widht:100%,则 li 就不能完全容纳这个 img 和 span ,span 的内容会超出 li 一部分。但如果把 img 的 max-width 去掉,或者把 img 换成 div (即便加上 max-width:100%),li 就能容纳元素的宽度。

我的问题是,既然在 flexbox 容器下 img 和 div 都是 block 元素(用浏览器 devtools 看到的结果),为什么行为会不一样?是什么样的 css 规则导致了这种不一致?

代码效果可以在这里看到: https://jsfiddle.net/c508dz6o/1/

<div class="root">
  <div class="container">
    <ul class="list-container">
      <li>
        <img class="icon" src="https://jsfiddle.net/img/favicon.png" />
        <span>Hello World !!!</span></li>
      <li>
        <div class="icon"></div><span>JavaScript</span>
      </li>
      <li>
        <div class="icon"></div>
        <span>Rust</span>
      </li>
    </ul>
  </div>
</div>

* {
  border: 0px;
  box-sizing: border-box;
  margin: 0px;
  padding: 0px;
}

.root {
  display: flex;
  justify-content: center;
}

.list-container {
  border: 1px solid black;
  padding: 24px;
  list-style:none;
  display: flex;
  flex-direction: column;
}

.list-container li {
  border: 1px solid green;
  margin-top: 10px;
  white-space: nowrap;
  display: flex;
  align-items: center;
}

.icon {
  background: green;
  width: 16px;
  height: 16px;
  max-width: 100%;
  margin-right: 14px;
}

.list-container li:first-child {
  margin-top: 0;
}

.container {
  display: flex;
  flex-direction: column;
  align-items: center;
  padding: 10px;
  width: 50px;
}
690 次点击
所在节点    前端开发
6 条回复
snarkprayer
282 天前
大概是 img 的默认 overflow:clip 导致的,hello word 禁止换行,所以 img 会被挤压,但因为 clip 所以挤压失效了,而父元素宽度是计算挤压后的,所以看起来文字超出了 li 元素,你把图片的 overflow 换成 auto 或者 hidden, flex-shrink 也改个值就能看出来区别了
xfn
282 天前
@snarkprayer 谢谢大佬提供思路。不过好像不是 overflow:clip 的原因,设为 hidden 后行为还是一样的。我把这个问题的示例简化了一下在这里 https://jsfiddle.net/o3dgjf0s/6/,只需要关注 item 这个 div 中的元素就行了。

```
<div class="root">
<div class="container">
<div class="item">
<img class="icon" />
<!-- <div class="icon"></div> -->
<span>Hello World !!!</span>
</div>
</div>
</div>

* {
border: 0px;
box-sizing: border-box;
margin: 0px;
padding: 0px;
}

.root {
display: flex;
justify-content: center;
}

.item {
border: 1px solid black;
display: flex;
align-items: center;
white-space: nowrap;
}

.icon {
background: green;
width: 16px;
height: 16px;
max-width: 100%;
flex-shrink: 0;
overflow: hidden;
}

.container {
display: flex;
flex-direction: column;
align-items: center;
padding: 10px;
width: 50px;
}

```
chnwillliu
281 天前
可以进一步简化 demo ,不需要嵌套 flex 就可以复现。

https://jsfiddle.net/zc9vqdn6/2/


```
<div class="item">
<img class="icon" />
<span>Hello World !!!</span>
</div>

<style>
.item {
border: 1px solid black;
display: inline-flex;
align-items: center;
white-space: nowrap;
width: min-content;
}

.icon {
background: green;
width: 16px;
height: 16px;
flex-shrink: 0;
max-width: 100%;
}
</style>
```
xfn
281 天前
@chnwillliu 谢谢大佬,简化后直观多了。所以这是啥原因造成的呢?😂
chnwillliu
281 天前
个人感觉应该跟这个 section 有关 https://www.w3.org/TR/css-sizing-3/#cyclic-percentage-contribution

If the box is replaced, a cyclic percentage in the value of any max size property or preferred size property (width/max-width/height/max-height), is resolved against zero when calculating the min-content contribution in the corresponding axis.

尝试来解释一下:

前提知识点:div 和 img 的 display 计算值虽然都是 block ,但是 CSS 内部还是区别对待 img 的,因为它是 replaced element 。

套多层 flex 起到的效果和 width:min-content 一样,简单说就是这个 container 的宽度由子元素 的 min-content 来贡献。虽然 .icon 已经有明确的 width 定义,但它的 max-width 也会影响它的 min-content 最终是多少,而 max-width 如果是百分值,也就是它需要先知道父容器( containing block )的 size ,所以这里就产生了循环依赖。CSS Box Sizing Module Spec 就规定了这种情况,解法分 replaced element 和 non-replaced element 。

non-replaced element 在计算 min-content / max-content 内在盒子大小时,遇到百分比或循环依赖值,直接就把整个值当作是没定义一样,即,使用其 initial value 来计算。div 的 max-width initial value 是 auto ,width:16px + max-width:auto 得到的 min-content 就是 16px 。

replaced element 在计算 max-content 时也是一样,但计算 min-content 时不同,循环依赖值会直接当成 0 来对待。width:16px + max-width: 0 得到的 min-content 就是 0 ,所以在父容器计算宽度时,img 贡献了 0 。

这里的 0 / auto 只会影响解盒子的 min/max-content 的流程,盒子本身的 sizing 过程百分比依然会被遵守,即,max-width:100% 在以 0 对待并算完父容器的宽度后再以百分比算出其值然后作用于 img 上。span 中的字符总宽度 hello word!!! 是 111.25px ,因此含 img 的 .item 算得宽度 111.25px ,img 最终得到 max-width:111.25px width:16px 宽度仍然是 16px 。

img 加 max-width: 100% 后在贡献宽度的时候贡献了 0 , 在分配宽度的时候还占 16px ,所以整体 size 就不够分了,没人 flex-shrink 所以就溢出了。
xfn
281 天前
@chnwillliu 先赞,感觉就是这个原因,具体规则再慢慢消化一下

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/1022498

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX