阅读 4164

一道面试题目引发的思考

起因

多列布局是前端一个经典的反复被提及的面试题目,最典型的即两列,左列定宽菜单栏,右列变宽为内容区域。

通常得到的答案无外乎左列浮动定宽,然后右列或浮动,或设置外边距,或绝对定位等等。偶尔会有面试者给出设置右列overflow属性的答案,心里就会有些惊喜,继而会继续追问,为什么这么设置就能实现效果,期待能有进一步惊喜,但基本大部分面试者都止步于这样设置,并不清楚原因。非常少的面试者会提到这样设置能够触发块级格式化上下文(Block Formatting Conext, BFC),如果继续追问触发BFC的原因,几乎没有一个面试者能给出比较满意的答案。

本文就是由这道面试题目引发的一些思考。针对设置overflow属性这一方法,做进一步的探讨。

overflow属性

overflow属性最常见的一个使用场景就是规定当内容溢出元素框时发生的事情。可能的值如下:

  • visible 默认值。元素内容不会被裁剪,元素框之外的内容仍然会呈现。
  • hidden 元素内容会被裁剪,并且元素 框之外的内容是不可见的。
  • scroll 元素内容会被裁剪,但是浏览器会显示滚动条以便查看其余的内容。
  • auto 浏览器自动处理元素内容的溢出,如果元素内容被裁剪,则浏览器会显示滚动条以便查看其余的内容。
  • inherit 规定应该从父元素继承 overflow 属性的值。

除此之外,也会经常看到通过overflow属性实现的一些效果,比如清除浮动,以及上面提到的两栏布局的实现。这些效果的实现,可能跟overflow属性的本意相差甚远,就像两种不相关的事务被硬生生的牵扯到了一起。其实不然,CSS Spec规范文档中还明确记录着overflow属性的另外一个重要作用。

overflow属性跟布局的关系

The CSS2.1 spec mandates that overflow other than visible establish a new "block formatting context".

CSS2.1规范中已经明确提出,设置overflow属性(非visible)能触发块级格式化上下文(Block Formatting Conext, BFC)。

BFC是个很大的话题,此处不展开,这里给出一个简化不精确的解释,BFC概念的引入,一定程度是为了特殊情况下布局计算的方便,元素触发BFC之后,其作用就相当于一个根容器,其内部的子元素会跟外界完全隔离开,子元素布局以及尺寸相关的计算都会以该元素容器为基础。

为什么overflow非visible值会触发BFC?

首先,设置overflow属性为visible的话,是一种默认情况,就相当于正常默认的布局,所有超出元素框的内容仍然会正常显示,不会被裁剪,也不会出现滚动条。但对于其它几种值的话(hidden, scroll, auto),元素的内容可能会被裁剪,此时,对于某些情况下可能出现的特殊布局处理就会出现争议。

比如对于垂直方向紧贴着的两个元素A和B,其中元素A中浮动的子元素可能会遮住元素B的部分文字区域,此时如果元素B的overflow属性设置为visible,则内容会包裹在元素A浮动子元素的周围,这种情况比较容易理解,如下图。

overflow属性设置为visible
overflow属性设置为visible

图1 overflow属性设置为visible

但当元素B的overflow属性设置为非visible的值时,各版本规范的规定就会出现差异。

CSS2.0规范规定,设置非visible属性值后,元素B的内容仍然包裹浮动元素,如图2所示。

overflow属性设置为novisible,CSS2.0规范中的处理
overflow属性设置为novisible,CSS2.0规范中的处理

图2 overflow属性设置为novisible,CSS2.0规范中的处理

此后如果元素B内容发生滚动,每次滚动行为,元素B中发生折叠的内容(图3中元素B中文字内容滚动后发生变化)全部要重新计算重绘,实际上这将会带来很大的性能问题,对滚动体验也会造成比较大的影响。

overflow属性设置为novisible,CSS2.0规范中发生滚动时的处理
overflow属性设置为novisible,CSS2.0规范中发生滚动时的处理

图3 overflow属性设置为novisible,CSS2.0规范中发生滚动时的处理

但这里存在进一步的疑问,即使按此规范的约定,元素B内容滚动时存在性能以及体验问题,但是非visible属性中的hidden值,难以理解,元素内容已经被裁剪掉了,为什么跟其它值auto, scroll归为一类?这里面就存在一个误区,overflow设置为hidden值并不代表内容不可滚动,此时浏览器只不过没有提供可滚动的UI,被"裁剪"掉的内容可以通过JavaScript脚本来控制滚动,这也是脚本模拟滚动条的基础。比如,可以通过JavaScript脚本设置元素的scrollTop实现图4的效果,更友好的方式可以自定义一个滚动条。

overflow属性设置为hidden,CSS2.0规范中发生滚动时的处理
overflow属性设置为hidden,CSS2.0规范中发生滚动时的处理

图4 overflow属性设置为hidden,CSS2.0规范中发生滚动时的处理

事实上各大浏览器厂商也都没有遵照CSS2.0来实现这一部分规范。取而代之,实现的是CSS2.1中的规范内容,即当元素B的overflow属性设置为非visible值时会触发BFC,元素B会创建自己的块级格式化上下文,并会被整体推向右侧,如图5所示。

overflow属性设置为nonvisible,CSS2.1规范中的处理
overflow属性设置为nonvisible,CSS2.1规范中的处理

图5 overflow属性设置为nonvisible,CSS2.1规范中的处理

备注 上面各图均来自于参考文献3

收尾

事实上,一些常见的其它布局技巧也都是基于上述的原理点,比如overflow属性非visible值可以用于清除浮动。如果一个面试者,能够比较清楚地讲出上面的各点,相信每个面试官心里面都会比较惊喜,上面只是自己的一些想法,可能会有些许的钻牛角尖,但单从这种对细节的钻研把控程度,候选人就一定不会太差,对候选人来说必然会有很大程度的加分。

上面只是针对两列布局这道题目一种方案的单方面探讨,这种方案有哪些优缺点等等都未提及,如果对每种方案都进行类似程度的拓展,将会发现这其中会涵盖很多前端知识点,所以看似简单的题目其实并不简单。越发觉得前端领域的水很深,伙伴们一起来努力探索实践吧!

参考文献

Written by Hong