看到微博和朋友圈都实现了图片九宫格,曾经有次面试也问到了九宫格这个问题,当时想到的是先固定每个单元格的宽高,然后进行浮动。今天想折腾一下,实现自适应父元素宽度的布局。这次我只写了四种方式去实现九宫格,算上inline-block
的话就是五种了。
首先要注意的是九宫格容器是宽高相等的正方形,并且是自适应的,这里关键是实现宽高相等,有些人想到了相对视口宽度 vw,但是它是相对于屏幕可见宽度来设置的,并且会忽略滚动条的宽度,所以这是不可行的。这里我用一种变通方法,看代码…
FlexBox
HTML 结构如下:
12345678910111213
<div class="square"> <ul class="square-inner flex"> <li>1</li> <li>2</li> <li>3</li> <li>4</li> <li>5</li> <li>6</li> <li>7</li> <li>8</li> <li>9</li> </ul></div>
抽取公共样式:
123456789101112131415161718192021
.square{ position: relative; width: 100%; height: 0; padding-bottom: 100%; /* padding百分比是相对父元素宽度计算的 */ margin-bottom: 30px;}.square-inner{ position: absolute; top: 0; left: 0; width: 100%; height: 100%; /* 铺满父元素容器,这时候宽高就始终相等了 */}.square-inner>li{ width: calc(98% / 3); /* calc里面的运算符两边要空格 */ height: calc(98% / 3); margin-right: 1%; margin-bottom: 1%; overflow: hidden;}
使用Flex的一个好处是不用再担心高度塌陷的问题,而且还可以轻松实现子元素横向竖向甚至按比例伸缩扩展的布局。
123456789101112131415161718
.flex{ display: flex; flex-wrap: wrap;}.flex>li{ flex-grow: 1; /* 子元素按1/n的比例进行拉伸 */ background-color: #4d839c; text-align: center; color: #fff; font-size: 50px; line-height: 2;}.flex>li:nth-of-type(3n){ /* 选择个数是3的倍数的元素 */ margin-right: 0;}.flex>li:nth-of-type(n+7){ /* 选择倒数的三个元素,n可以取0 */ margin-bottom: 0;}
Grid
对于网格布局来说,grid 比 flex 更为方便,代码量更少,可以处理更为复杂的结构。
12345678910111213
<div class="square"> <div class="square-inner grid"> <div>1</div> <div>2</div> <div>3</div> <div>4</div> <div>5</div> <div>6</div> <div>7</div> <div>8</div> <div>9</div> </div></div>
1234567891011121314
.grid{ display: grid; grid-template-columns: repeat(3, 1fr); /* 相当于 1fr 1fr 1fr */ grid-template-rows: repeat(3, 1fr); /* fr单位可以将容器分为几等份 */ grid-gap: 1%; /* grid-column-gap 和 grid-row-gap的简写 */ grid-auto-flow: row;}.grid>div{ color: #fff; font-size: 50px; line-height: 2; text-align: center; background: linear-gradient(to bottom, #f5f6f6 0%,#dbdce2 21%,#b8bac6 49%,#dddfe3 80%,#f5f6f6 100%);}
更多:CSS Grid布局指南
Float
浮动实现九宫格就不多说了,原理同上。
12345678910111213
<div class="square"> <ul class="square-inner float"> <li>1</li> <li>2</li> <li>3</li> <li>4</li> <li>5</li> <li>6</li> <li>7</li> <li>8</li> <li>9</li> </ul></div>
1234567891011121314151617181920
.float::after{ content: ""; display: block; clear: both; visibility: hidden;}.float>li{ float: left; background-color: #42a59f; text-align: center; color: #fff; font-size: 50px; line-height: 2;}.float>li:nth-of-type(3n){ margin-right: 0;}.float>li:nth-of-type(n+7){ margin-bottom: 0;}
除了浮动,这里 li 也可以使用display: inline-block;
实现同样的效果,不过要注意HTML代码非压缩情况下行块级元素之间会出现默认间隔,不同浏览器下表现还不一样,这时可以给父级元素设置font-size: 0;
Table
123456789101112131415161718192021
<div class="square"> <table class="square-inner table"> <tbody> <tr> <td>1</td> <td>2</td> <td>3</td> </tr> <tr> <td>4</td> <td>5</td> <td>6</td> </tr> <tr> <td>7</td> <td>8</td> <td>9</td> </tr> </tbody> </table></div>
123456789101112
.table{ border-collapse: separate; border-spacing: 0.57em; font-size: 14px; empty-cells: hide; table-layout: fixed;}.table>tbody>tr>td{ text-align: center; background-color: #889ed8; overflow: hidden;}
说下用表格实现九宫格有哪些瑕疵:
- 最后一行最后一列的单元格宽高与前面的不一致,虽然相差不大,但是还是有差异的;
- 与前面的两种方法不同,table 单元格之间的间隔是利用
border-spacing
属性实现的,且不支持百分比,单元格四周都有类似margin的外边距效果,如下图。
分析
综上来看,个人认为 FlexBox 适合用于移动端,PC端 IE10 以下不支持;Grid网格布局比较方便,但是规范还未成熟,主流浏览器厂商尚未推广,不推荐使用在项目中;浮动和行块级式声明可以兼容到IE6,移动端和PC端支持的都不错;Table 因为实现有瑕疵所以不推荐使用。