前端大佬帮忙看一下这个关于 img 设置 max-width 后的行为问题 - V2EX
V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
xfn
V2EX    前端开发

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

  •  
  •   xfn 2024-03-11 12:38:41 +08:00 1000 次点击
    这是一个创建于 654 天前的主题,其中的信息可能已经有所发展或是发生改变。

    问题在 so 上也问过( https://stackoverflow.com/questions/78135768/behavior-of-imgwidth-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; } 
    第 1 条附言    2024-03-11 19:48:15 +08:00

    我把这个问题的示例简化了一下在这里 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; } 
    6 条回复    2024-03-12 19:22:35 +08:00
    snarkprayer
        1
    snarkprayer  
       2024-03-11 18:29:36 +08:00
    大概是 img 的默认 overflow:clip 导致的,hello word 禁止换行,所以 img 会被挤压,但因为 clip 所以挤压失效了,而父元素宽度是计算挤压后的,所以看起来文字超出了 li 元素,你把图片的 overflow 换成 auto 或者 hidden, flex-shrink 也改个值就能看出来区别了
    xfn
        2
    xfn  
    OP
       2024-03-11 19:43:55 +08:00
    @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
        3
    chnwillliu  
       2024-03-12 16:35:50 +08:00
    可以进一步简化 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
        4
    xfn  
    OP
       2024-03-12 17:38:52 +08:00
    @chnwillliu 谢谢大佬,简化后直观多了。所以这是啥原因造成的呢?
    chnwillliu
        5
    chnwillliu  
       2024-03-12 18:23:25 +08:00   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 所以就溢出了。
    xfn
        6
    xfn  
    OP
       2024-03-12 19:22:35 +08:00
    @chnwillliu 先赞,感觉就是这个原因,具体规则再慢慢消化一下
    关于     帮助文档     自助推广系统     博客     API     FAQ     Solana     1160 人在线   最高记录 6679       Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 37ms UTC 17:43 PVG 01:43 LAX 09:43 JFK 12:43
    Do hve faith in what you're doing.
    ubao msn snddm index pchome yahoo rakuten mypaper meadowduck bidyahoo youbao zxmzxm asda bnvcg cvbfg dfscv mmhjk xxddc yybgb zznbn ccubao uaitu acv GXCV ET GDG YH FG BCVB FJFH CBRE CBC GDG ET54 WRWR RWER WREW WRWER RWER SDG EW SF DSFSF fbbs ubao fhd dfg ewr dg df ewwr ewwr et ruyut utut dfg fgd gdfgt etg dfgt dfgd ert4 gd fgg wr 235 wer3 we vsdf sdf gdf ert xcv sdf rwer hfd dfg cvb rwf afb dfh jgh bmn lgh rty gfds cxv xcv xcs vdas fdf fgd cv sdf tert sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf shasha9178 shasha9178 shasha9178 shasha9178 shasha9178 liflif2 liflif2 liflif2 liflif2 liflif2 liblib3 liblib3 liblib3 liblib3 liblib3 zhazha444 zhazha444 zhazha444 zhazha444 zhazha444 dende5 dende denden denden2 denden21 fenfen9 fenf619 fen619 fenfe9 fe619 sdf sdf sdf sdf sdf zhazh90 zhazh0 zhaa50 zha90 zh590 zho zhoz zhozh zhozho zhozho2 lislis lls95 lili95 lils5 liss9 sdf0ty987 sdft876 sdft9876 sdf09876 sd0t9876 sdf0ty98 sdf0976 sdf0ty986 sdf0ty96 sdf0t76 sdf0876 df0ty98 sf0t876 sd0ty76 sdy76 sdf76 sdf0t76 sdf0ty9 sdf0ty98 sdf0ty987 sdf0ty98 sdf6676 sdf876 sd876 sd876 sdf6 sdf6 sdf9876 sdf0t sdf06 sdf0ty9776 sdf0ty9776 sdf0ty76 sdf8876 sdf0t sd6 sdf06 s688876 sd688 sdf86