CSS grid without border-box

这篇还是不删了,作为我愚蠢的教训…本文全是错的TT

在新公司呆了几天,任务还很轻松(刚来当然轻松,逃->

bootstrap中新版的网格系统(grid system)是及其好用的,http://getbootstrap.com/css/#grid

用非常简洁明了的class命名,如col-md-4, col-md-6这种来表示4 / 12, 6 / 12的宽度占比

同时用xs(extre small), sm(small), md(medium), lg(large)来解决响应式的问题,比如给一个元素同时加上class='col-md-4 col-xs-12'就表示正常下是占了三分之一,而手机上是独占一行

同时bootstrap还提供了col-md-offset-4这样的偏移定位,本质上不过是把width属性换成了margin-left属性罢了,bootstrap的网格通俗易懂,每个人都可以轻松手写之

可惜的是bootstrap的网格是彻底依赖* {box-sizing: border-box}

咱上http://caniuse.com/#search=border-box一查就可以发现IE6和IE7是不可能支持这种属性的

要知道,在border-box之前,所有的布局都是算出来的,我们必须把所有元素的border, padding, margin加起来,正好等于容器宽度才行。那时候的网页开发者,只能拿着计算器慢慢的计算,写个border都痛苦万分。border-box非常完美的解决了这个问题,把border和padding都归为自身长度

可惜我的任务是写出一个像bootstrap一样的网格,而且不能用border-box(我去年喵了个咪->

我从一开始就表示这是不可能的,如果也用百分比,那row中的每个col只能是等分,不然根本无法处理好中间的margin(算不出)

但需求就在那里,主管表示可以让一步,但必须要支持几种基本的布局,如1:1:1, 1:2, 1:1

其实这也已经很复杂了,因为margin不同, 直接写可能还很简单,但是要统一成一个css框架还是要开动脑筋的

其实只要是不用border-box,那外面的layout必然是固定宽度的,也就是必须写明是970px还是980px之类的,这样才能根据固定的margin算出宽度

至于为何是margin而不是padding,那是因为我们并不知道是border-box还是content-box,用margin两种都可以兼容

因此我们要写出这样一个css,拿960举例

.layout960 {
  width: 960px;
}
.layout960 .col {
  margin: xxxx;
  float: left;
}
.layout960 .cols-1 {
  width: xxxpx;
}
.layout960 .cols-2 {
  width: xxxpx;
}
...
.layout960 .cols-5 {
  width: xxxpx;
}

事实上,我们为了便于计算,会选择把.col左右的margin都放到margin-left中.

重点就是这些数值,它并不像border-box那样可以简单写一个百分比了事,他需要确切的数字,可以完成1:1, 1:2, 1:1:1等排列的数字

这个时候,我们终于发现,不带border-box的css果然还是数学问题,我们需要列出一个方程,这也回答了为啥我只用了6栏布局(12栏怎么算得出来嘛->

我们可以很快写出一个小程序

var s = []
for (var m = 10; m <= 30; m++) {
  for (var w = 940; w <= 1020; w++) {
    var x1 = (w - 5 * m) / 6
    var x5 = w - m - x1
    var x2 = (w - 2 * m) / 3
    var x4 = w - m - x2
    var x3 = (w - m) / 2
    var _x4 = w - 2 * m - 2 * x1
    if (x4 === _x4) {
      s.push([x1, x2, x3, x4, x5, m, w])
    }
  }
}

这段小程序就是遍历margin-left从10-30,layout的width从940-1020的全部匹配的值,发现会有超多。

我自己选了几组都是整数,而且看着顺眼的

cols-1, cols-2, cols-3, cols-4, cols-5, margin-left, width
[145, 308, 471, 634, 797, 18, 960]
[145, 310, 475, 640, 805, 20, 970]
[145, 312, 479, 646, 813, 22, 980]
[150, 320, 490, 660, 830, 20, 1000]
[150, 324, 498, 672, 846, 24, 1020]

也就是说,我们无法指定layout width的同时指定col的margin.只能靠计算撞到一组解,然后使用之,因此,大多不带border-box的css grid,都看起来用了些magic number

可惜这还是不对啊,我第一个col不要margin-left呀!

嗯,这个很容易,只需要加上

.layout960 .row {
  margin-left: -20px; /* 加个负边距就行了 */
}

虽然看上去解决了需求,但要注意的是它只支持如下布局

  • 1:1:1:1:1:1
  • 2:4
  • 3:3
  • 1:5
  • 2:2:2

不支持1:2:3, 1:1:1:3.方程无解咱也没办法啊..