问题在 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;
}
我把这个问题的示例简化了一下在这里 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;
}
1
snarkprayer 264 天前
大概是 img 的默认 overflow:clip 导致的,hello word 禁止换行,所以 img 会被挤压,但因为 clip 所以挤压失效了,而父元素宽度是计算挤压后的,所以看起来文字超出了 li 元素,你把图片的 overflow 换成 auto 或者 hidden, flex-shrink 也改个值就能看出来区别了
|
2
xfn OP @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; } ``` |
3
chnwillliu 263 天前
可以进一步简化 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> ``` |
4
xfn OP @chnwillliu 谢谢大佬,简化后直观多了。所以这是啥原因造成的呢?😂
|
5
chnwillliu 263 天前 1
个人感觉应该跟这个 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 所以就溢出了。 |
6
xfn OP @chnwillliu 先赞,感觉就是这个原因,具体规则再慢慢消化一下
|