从height:100%不支持聊聊CSS中的“死循环”

这篇文章发布于 2016年09月16日,星期五,17:25,归类于 CSS相关。 阅读 59810 次, 今日 3 次 36 条评论

 

一、从为何height:100%无效的回答说起

大家应该都知道,如果元素没有格式化的高度值,子元素的height百分比高度是不起作用的,常见的就是height:100%无效,而宽度就没有此问题,所以,新人经常会提出的一个问题是:“为何我的div设置了height:100%却没有效果?”

不知道大家有没有思考过这样一个问题。

然后,有同行就提出了这么一个看似很合理的解释,就是,“假如父元素height:auto且没有其他格式化高度,子元素支持height:100%,则很可能会出现高度死循环”。

什么意思呢?

例如:一个<div>元素里面有一张vertical-alignbottom同时高度为192像素的图片,此时,该<div>高度就是192像素,假设此时,插入一个子元素,高度设为100%,如果此时height:100%可以计算,则子元素应该也是192像素。但是啊但是,我们的父元素height值是auto,岂不是现在高度要从原来的192像素变成384像素,然后height:100%的子元素高度又要变成384像素,父元素高度又双倍……死循环了!

实际上,这种解释是错误的,大家千万别被误导。

证据就是宽度也存在类似场景,但并没有死循环。例如下面这个例子,父元素采用“最大宽度”,然后有个inline-block子元素宽度100%:

<div class="box">
  <img src="1.jpg">
  <span class="text">红色背景是父级</span>
</div>
.box {
  display: inline-block;
  white-space: nowrap;
  background-color: #cd0000;
}
.text {
  display: inline-block;
  width: 100%;
  background-color: #34538b;
  color: #fff;
}

如果按照上面“高度死循环”的解释,这里也应该“宽度死循环”,因为后面的inline-block元素按照我们的理解应该会让父元素的宽度进一步变大。但是,实际上,并没有,宽度范围可能超出你的预期:

宽度并非死循环,而是图片加文字宽度

父元素的宽度就是图片加文字内容的宽度之和。

眼见为实,您可以狠狠地点击这里:宽度死循环不存在demo

二、为什么不会死循环

这需要了解浏览器渲染的基本原理。首先,先下载文档内容,加载头部的样式资源(如果有),然后按照从上而下,自外而内的顺序渲染DOM内容。套用本例就是,先渲染父元素,后渲染子元素,是有个先后顺序的。因此当渲染到父元素的时候,子元素的width:100%并没有渲染,所以,宽度就是图片加文字内容的宽度;等渲染到文字这个子元素的时候,父元素宽度已经固定,此时的width:100%就是已经固定好的父元素的宽度,宽度不够怎么办?溢出就好了,overflow属性就是为此而生的。

同样的道理,如果height支持任意元素100%,也是不会死循环的,和宽度类似,静态渲染,一次到位。

这就引申出另外一个问题,父选择器,大家有没有想过如果CSS支持了父选择器,会有什么后果?

后果之一,就是原先的一次渲染被破坏,子元素能够影响父元素的渲染,于是乎,“死循环”开始了,页面渲染会出现各种各样的死循环,现有的很多CSS规则会被颠覆,无限宽度反复渲染等问题就会出现。这就是为什么父选择器呼声那么高,却迟迟不支持的原因。

其实,在CSS中,不会死循环的例子还有很多,我再举一个很有意思的!

三、CSS padding百分比、滚动条与“死循环”

CSS的padding属性值如果是百分比值,则无论是水平方向还是垂直方向都相对于宽度计算,这就埋下了一个看似会“死循环”的隐患,我们直接看一个例子。

一个div有如下CSS:

.box {
  width: 200px;
  height: 199px;
  overflow: auto;
}
.child {
  padding: 50%;
}

容器宽200px199px,子元素padding:50%,则此时,子元素的宽高应该都是200px,但是,如果高度是200px,父元素就会出现滚动条,因为父元素高度199px不足200px,但是,父元素出现滚动条,父元素的content box的宽度就要减去滚动条的宽度,例如window 7下都是17像素,此时,子元素自然宽高也要随之降低,应该是183px,但是,变成183px后高度又小于了父元素的199px,滚动条又会消失,子元素宽度又回到200px,于是,一个看上去的死循环开始了……

但是,实际上,最终渲染是一次性的,如果父子元素分别给个背景色,则结果如下:
padding:50%宽度却和父元素不一样

大家就见到了上图所示的“神奇的”一幕,也就是padding:50%元素的宽度居然不是父元素的content box宽度。

padding:50%的元素宽度

结果,父元素宽度还是原来的200px,但是,子元素却是183px,右侧和下方都留了看上去不能理解的空白间距。

眼见为实,您可以狠狠地点击这里:CSS padding百分比值“死循环”不存在demo

这就是CSS的一次渲染机制造成的效果。

这个例子也进一步证明了:CSS中,如果单纯是静态渲染,是没有死循环这种说说法的!

再如我们CSS :hover某元素,让其到远离鼠标的地方,按照道理,远离了,应该不执行:hover渲染要回到原地,实际上,远离了就远离,不会不断执行渲染的。

四、CSS中的无限循环

实际上,CSS中是有与“死循环”相关的关键字的,叫做infinite,英菲尼迪,无限的意思,出现在CSS3 animation中,可以让动画无限循环,但是,“无限循环”并不等同于“死循环”。

五、结束语

一开始的问题“为何height:100%无效”究竟原因是什么呢?

这个其实可以从规范中找答案,具体是什么呢?我这里卖个关子,大家可以关心我日后的著作,其中会解释“为何height:100%无效”。

OK,最后一句话总结下本文的中心论点:CSS中没有死循环的说法

感谢阅读,欢迎交流!

(本篇完)

分享到:


发表评论(目前36 条评论)

  1. CodeHz说道:

    https://codepen.io/CodeHz/pen/roPJGP
    我这个例子里是会发生无限渲染的。。。至少在Chrome 60-76都是这样的(

  2. David说道:

    “为何height:100%无效”究竟原因是什么呢
    –张老师可以公布答案了么?

  3. matusmoto说道:

    Chrome 61.0.3163.100(正式版本),”CSS padding百分比值“死循环”不存在demo”这个例子里,改变页面大小,当页面未出现滚动条时,子元素为200*200;当页面出现了滚动条时,会出现文中描述的效果。

  4. hangyangws说道:

    html:

    css:

    .hover {
    width: 100px;
    height: 100px;
    margin: 10px;
    background-color: #f00;
    position: relative;
    }
    .hover:hover {
    left: 400px;
    }

    浏览器会死循环啊

  5. zsj说道:

    在最新版google中,子元素还是200*200
    版本 59.0.3071.115(正式版本) (64 位)

  6. tomwang1013说道:

    第三点: CSS padding百分比、滚动条与“死循环”
    在新的chrome浏览器上没有看到父元素的红色背景呢,父元素还是200*199,child元素还是200*200

  7. zhangkai说道:

    我遇到一个问题,父元素高度100%, 有2个子元素,第一个固定高度42px,第二个高度100%; 实际高度第二个元素高度和父元素一样,但是如果去掉父元素的高度100%,第二个子元素的高度就是父元素高度减去第一个子元素的高度,用的是你的mobilebone框架,父元素样式就是page,元素都是div.

  8. css的19堂課说道:

    先謝謝博主的對height死循環的見解

    然,css的第一堂課即css標準與定義均在w3c
    遇到問題先把w3c中相關的等,翻出來閱讀並了解後,方能對症下藥採取方法和測試

    死循環
    第一,不會出現在width
    第二, 也不會出現在height
    第三,padding的百分比和此無關
    第四,與html宣染無關

    這些在w3c.rog有答案喔,謝謝

  9. DX说道:

    我有一个问题,当父元素的 height: auto; 子元素的 height: 100%,父元素被一个其他的子元素撑开了,但是此时子元素的height却是0,并没有被父元素撑开后的height渲染,这是怎么回事

  10. 跳蚤说道:

    菜鸟,道行太浅,没悟透。。。 平时几乎不用height:100%,不知道什么时候用写height的百分比

  11. byc说道:

    大赞!!!

  12. ycy说道:

    最后这个表情好吓人啊

  13. susan说道:

    还是没看懂讲什么

  14. 我是忠实粉丝说道:

    大神,我有一个问题,提在这上面了,如果你要是有空,可以瞧瞧吗。不好意思,评论里提问题了。O(∩_∩)O谢谢http://www.w3cfuns.com/notes/24364/eb7ae0f5b7ac91c832be98150f59ed0c.html

  15. 没太看懂,不过要想height:100%;只需要当前元素和父元素height:100%就ok

  16. popeyesailorman说道:

    学到好多东西

  17. handsome说道:

    infinite读音不应该是英菲尼特么,为何是英菲尼迪- –

  18. Pandamo说道:

    子元素很少用,偶尔会用到body{height:100%}

  19. min-height:100%说道:

    之前还遇到过html,body设置min-height:100%;但是如果内容少的话,footer就会到窗口中间去;最后是参考的百度首页的方式处理的;子元素设置属性百分比的都是根据父元素来计算的

  20. fengchenxiujisd说道:

    我猜呢 height 就像margin一样 是默认水平渲染的(不知道这么说对不对 很忐忑呢) margin给个auto可以搞起这水平可以搞起 但是垂直方向就萎了(auto 在垂直方向即为0)
    就像上面有兄弟说的 “like ‘auto'”
    那既然文档的渲染是左右的 height默认是垂直 那肯定auto也会萎了啊(即不设置父级即为0) 0的100%.. 用张师傅的话说 即使数学是体育老师教的…[怀疑他是不是一直是在体校考上华中的]

  21. ggbond说道:

    学习了,下面这段可能有点问题
    再如我们CSS :hover某元素,让其到原理鼠标的地方,按照道理,原理了,应该不执行:hover渲染要回到原地,实际上,远离了就远离,不会不断执行渲染的。

    原理应该是远离

  22. zyc说道:

    老师为什么我在chrome下面看第三个例子时,高度有滚动条,如果拉伸一下浏览器窗口,滚动条消失并且没有box的背景只有里面的child的背景

    • vera说道:

      对!我看的时候也有这种情况,不过只是在chrome下,ie和firefox下滚动条始终存在,这是跟chrome渲染机制有关系么?求大神解答!万分感谢!!!
      (ps:之前做的一个项目也有出现这样的情况,感谢张鑫旭大大解开我的疑惑!)

    • peikon说道:

      我也发现了,最大化的时候并不会出现滚动条的位置,缩小窗口后就出现了

      • peikon说道:

        重新测试了下,chrome下每渲染一次,都会在显示滚动条位置/不显示滚动条位置之间来回切换,跟窗口大小没有关系

      • lat说道:

        chrome最大化有,拖动的时候,有时候有,有时候没有

    • kenzid说道:

      同问。。为什么在拖动的时候。有时候滚动条就消失了。此时child的padding是从原来有滚动条的情况下91.5(183*50%)变成了100(content-box为200超过199).这时浏览器好像不认识overflow了一样。竟然没有滚动条了。要不然就是浏览器不认识新的width。。。

  23. sdasdasd说道:

    远离 不是 原理

  24. 李帅说道:

    学习了。。。难怪有的时候高度100%不好使。。。又吃成长快乐了,好开心啊。。哈哈

    • 李帅说道:

      The percentage is calculated with respect to the height of the generated box’s containing block. If the height of the containing block is not specified explicitly (i.e., it depends on content height), the value is interpreted like ‘auto’.

      • 李帅说道:

        默默的再飘一次,安卓UC浏览器你设置了html和body高度为100%,然后再点击加载更多,内容加载出来了,但是。。。但是,滚动条划不动了.你去掉以后,其他浏览器又不正常了。。论UC浏览器的html 100%到底什么鬼

  25. 皮蛋周说道:

    张老师是要出书吗? 好激动 ^_^