左右两列等高布局,到底咋搞才靠谱?

3,333字
14–21 分钟
in

碰到需要把页面劈成两半,左边一堆内容右边一堆内容,还要求俩边高度自动对齐的情况,是不是有点挠头?CSS里折腾这事儿的花样可不少,今儿就把这些老套路新玩法全抖搂出来,顺便唠唠哪个坑少、哪个真香。

目录

背景渐变唬人法

假装两边背景不一样,但又不想动太多HTML结构?渐变就能整活。给容器设个线性渐变,中间来个硬切,左一半颜色右一半颜色,视觉上就是两块。

.split-bg {
  background: linear-gradient(
    to right, 
    #f5a623 0%, 
    #f5a623 50%, 
    #e0951c 50%, 
    #e0951c 100%
  );
}

这个法子只用一个容器,省事儿是真省事儿。但里头的实际内容只能往这一个盒子里塞,想左右分开控制内容位置?那就得配合浮动或者定位来折腾了。写渐变的时候那个50%的位置要掐准,不然左右交界处会糊掉或者出现锯齿。另外不同浏览器渲染渐变硬边有细微差别,上线前最好在Chrome、Firefox、Safari都瞟一眼。

绝对定位分家

弄个老爹容器,里头塞俩儿子,给儿子设成绝对定位,左边left:0; width:50%,右边left:50%; width:50%。背景随便刷,俩儿子各自管自己的内容。

<div class="parent" style="position: relative; height: 300px;">
  <div class="left" style="position: absolute; left: 0; width: 50%; background: #ffe0b5;">左边料</div>
  <div class="right" style="position: absolute; left: 50%; width: 50%; background: #d9b48b;">右边料</div>
</div>

绝对定位就像个脾气大的主子,老爹必须得有明确的高度,不然俩儿子就不知道站哪儿了。而内容一变多、变少,老爹的高度不会自动撑开,下边其他东西就怼上来了。这种写法适合那种高度打死不变、内容字数固定的场景,比如后台管理面板的左右统计卡片。如果内容会动态增减,趁早换别的招。

表格大法显神威

老前端都记得表格布局的酸爽,不过现在用display: tabletable-cell完全是另一回事——不碰<table>标签,没有那些可访问性破事儿,但继承了表格单元格等高对齐的优良传统。

.parent-table {
  display: table;
  width: 100%;
}
.child-cell {
  display: table-cell;
  width: 50%;
  background: #cce5ff;
}
.child-cell:first-child {
  background: #ffcccc;
}

这种写法稳如老狗,从IE8到安卓古董机都认得。而且响应式也好改,在媒体查询里把.child-celldisplaytable-cell改成block,俩儿子就上下堆叠了。唯一要留神的是,table-cell的元素对margin和某些定位属性不太感冒,想给左右之间加间隔?用border-spacing或者给里头再包一层div。

浮动双雄

浮动是老朋友了,俩儿子各宽50%,都往左飘,就能并排站。但等高咋办?要么像第一个方法那样用渐变背景假装等高,要么上负边距黑魔法。

.float-parent {
  overflow: hidden; /* 清浮动 */
}
.float-left, .float-right {
  width: 50%;
  float: left;
  background: #b8e0d4;
  padding-bottom: 9999px;
  margin-bottom: -9999px;
}

这个负边距+大内边距的歪招,能让俩盒子的背景高度撑到一样高,实际内容还是各管各的。不过overflow: hidden会把超出的部分切掉,如果内容里有下拉菜单或者浮出去的提示框,就会被咔嚓掉。更稳妥的清浮动是在老爹后面加一个伪元素clear: both。用浮动还容易踩的坑是,两个盒子里内容高度差距太大的时候,矮盒子下边会留出一大片空白区域,得结合具体布局调那个9999px的值,够用就行别贪大。

行内块双煞

烦透了清浮动?display: inline-block能救场。俩儿子设成inline-block,宽度各50%,自然并排。

<div class="ib-parent">
  <div class="ib-left">左边文字</div><div class="ib-right">右边文字</div>
</div>
.ib-left, .ib-right {
  display: inline-block;
  width: 50%;
  background: #f7d794;
  vertical-align: top;
}

注意看HTML里</div><div>之间没有换行和空格,不然那点空白会变成真实宽度,导致右边被挤下去。可以用父容器font-size: 0再给儿子恢复font-size来破,但总觉得有点猥琐。行内块本身不管等高,需要自己给俩儿子设相同的min-height或者用vertical-align: top让它们顶部对齐,底部就不管了。适合那种左右背景颜色一样、只关心顶部对齐的卡片列表。

Flex弹性大法

Flex出来以后,等高布局直接进入躺平时代。父容器设display: flex,俩儿子自动被拉伸到一样高,啥额外属性都不用写,舒服。

.flex-parent {
  display: flex;
}
.flex-child {
  flex: 1;
  background: #c5e0b4;
  padding: 1rem;
}
.flex-child:first-child {
  background: #ffd966;
}

flex: 1的意思是剩余空间全占,并且均分。如果左边内容少、右边多,左边的高度会被右边撑开,完美对齐。Flex的兼容性从IE10开始,但IE10需要写-ms-flexbox前缀,现在基本上可以忽略。有一个小坑:如果儿子里面放了超长英文单词或者大图,可能会撑破容器,记得加overflow: hiddenword-break: break-word。另外想调整左右顺序?order属性随便改,比浮动不知道高到哪里去了。

Grid网格切分

Grid布局就像表格和Flex生了个加强版娃。父容器声明display: grid,再切两列,儿子自动填进去,高度也是等高的。

.grid-parent {
  display: grid;
  grid-template-columns: 1fr 1fr;
}
.grid-child {
  background: #f4acb7;
  padding: 1rem;
}

1fr是份数单位,两份各占一半。Grid还能轻松搞出更骚的操作,比如左边占1份、右边占2份,或者中间带间隙。等高的原理和Flex类似,子项默认align-items: stretch,自动撑满。Grid兼容性比Flex稍晚,但也是现代浏览器的标配了。新手容易犯的错是在Grid容器上同时写了display: flex,导致Grid属性失效,记得二选一。

锚点定位新玩意

2024年CSS推出了锚点定位,可以拿一个元素当锚,另一个元素贴上去,哪怕这俩在HTML里八竿子打不着。搞左右等高?拿左栏当锚,右栏通过绝对定位贴在它右边,高度可以动态跟随。

.anchor-left {
  anchor-name: --left-col;
  background: #bdd4e7;
}
.target-right {
  anchor-position: --left-col;
  position: absolute;
  left: anchor(right);
  top: anchor(top);
  bottom: anchor(bottom);
  width: calc(50% - 20px);
  background: #ffe5b4;
}

不过这东西目前还是未来战士,浏览器支持没铺开,生产环境谨慎使用。好处是左右内容在DOM上完全独立,不用嵌套在同一个父容器里,做复杂交互面板的时候特别灵活。等全面支持之后,很多以前需要JS算高度的破事儿就省了。现在想尝鲜可以开Chrome Canary,记得给不支持的浏览器准备降级方案。