Two-Column Layout 与 Block Formatting Context

更新日期: 2017-01-02 阅读次数: 7855 分类: CSS

在临摹 semantic-ui 的 feed 实现时,发现我对 Two-Column Layout 的实现并没有掌握。

这是我第一次做出来的效果

Block Formatting Context

而我想要的效果是这样的

Block Formatting Context

双栏布局不就是加个 float: left 这么简单么?看起来并没有这么简单,我之前用 placehold.it 图片来模拟 div float 布局,看来是忽略了很多隐藏的问题。

Google 了一下,原来只需要在右侧 div 加上 overflow: hidden 即可。

WTF,overflow: hidden 与 float 布局有什么关系

我印象中,这应该不是第一次查到这么诡异的 CSS 解决方案了。如果是之前,我基本上解决之后就不管了。奈何现在写模板的机会越来越多,所以还是有必要深究一下。

overflow: hidden 是黑科技,为其对应的 box 创建了一个全新的 block formatting context (BFC)。而 block formatting context 会改变原有的布局策略。

Block Formatting Context 是什么?

如果一上来就想直接弄明白什么是 Block Formatting Context, 实际上很有难度。我元旦晚上一边看《机械师2》一边查的资料,整整理解了一个半小时,仍然对很多概念很模糊。最科学的方法是完整的看一遍 W3C Visual formatting model

不过这样一来就比较枯燥了,所以我还是最终选择了乱翻的阅读策略,遇到看不懂的再查。

看一下 W3C 的解释

Floats, absolutely positioned elements, block containers (such as inline-blocks, table-cells, and table-captions) that are not block boxes, and block boxes with 'overflow' other than 'visible' (except when that value has been propagated to the viewport) establish new block formatting contexts for their contents.

In a block formatting context, boxes are laid out one after the other, vertically, beginning at the top of a containing block. The vertical distance between two sibling boxes is determined by the 'margin' properties. Vertical margins between adjacent block-level boxes in a block formatting context collapse.

In a block formatting context, each box's left outer edge touches the left edge of the containing block (for right-to-left formatting, right edges touch). This is true even in the presence of floats (although a box's line boxes may shrink due to the floats), unless the box establishes a new block formatting context (in which case the box itself may become narrower due to the floats).

看完这三段,虽然各种疑问,但是有一点是明确的,加了 overflow: hidden 之后,对应的 div 创建了一个新的 Block Formatting Context。而在 Block Formatting Context 中,每个 box 的左边都会触及 container 的左边,这样就解决了我遇到的右侧 div 中内容上下不对其的情况。

但是,我产生了几个疑问

  • 没有任何 css 属性的最外层 div 是否是一个 Block Formatting Context ?
  • Block Formatting Context 中的非 block 元素,即 inline-block 为何没有遵守上面所述的布局规则

第一个问题,显然最外层的 div 并不符合 Block Formatting Context 的定义。那么还有哪些类似 BFC 的排版规则。CSS 2.1 中,box 的布局遵循三种 positioning scheme

  • Normal Flow。包含了三种情况,一是针对 block-level box 的 block formatting, 即对应上面的 Block Formatting Context 概念;而是针对 inline-level box 的 inline formatting; 再就是 block-level inline-level 混排的 relative positioning。这里也同时回答了第二个问题,Block Formatting Context 只是针对 block-level box 的布局规则,并不适用于 inline-level box。推测浏览器是按照不同的优先级执行各种布局策略。
  • Floats。在 float 模型中,一个 box 首先按照 normal flow 进行排版,然后再对应的向左或向右进行位移。
  • Absolute positioning。此模型中的 box 会从 normal flow 中移除。

还原案发现场

利用学到的新知识还原一下案发现场

<div class="container">
  <div class="avatar"></div>
  <div class="main">
    <p class="author"></p>
    <p class="comment"></p>
  </div>
</div>

首先,main div 参与了 normal flow 的布局,左边与 container 的左边重合。而 avatar 参与了 floats 布局,也与 container 的左边重合。这样 container 中的 author, comment 部分就被挤到了 avatar 的右侧。但是需要注意的是,main 的左边依然是与 container 的左边是重合的。所以当 comment 内容过长时,就会出现与 container 左边零距离的情况。

解决方案就是使 author, comment 的左边紧贴 main 的左边,所以加上 overflow: hidden 使其变成新的 BFC, 这样 main 的子元素就能保证左边与其左边零距离了。于是,main 就不得不远离 container 的左边。

用人话来说就是,构成新 BFC 之后,就不会与平级的 float box overlap 了

overflow: hidden 是最好的解决方案么

如果是使用 float 进行排版布局,这的确是最好的解决方案。但是,能明显看出,float 诞生的原始初衷并不是用来进行双栏、多栏布局的,否则就不需要这种可读性为零的黑科技了。也许,只有 table 是天生用来进行多栏布局的,但是,实战中,大家发现还是 float 的布局效果更佳,所以宁愿抛弃可读性,也不愿意使用 table。

但是,这种的代码太恶心了。还是有必要学习一下 flex box 的布局方式,这说明 bootstrap 4 采用 flex box 是有道理的。

参考

关于作者 🌱

我是来自山东烟台的一名开发者,有敢兴趣的话题,或者软件开发需求,欢迎加微信 zhongwei 聊聊, 查看更多联系方式