# 问题
一侧定宽、一侧自适应,尽量多的方案实现。
# 回答
有下面几种方案能实现一侧定宽一侧自适应。
- 使用flex布局,这也是目前主流方式。父容器设置display:flex,定宽字元素设置flex-basis或者width,自适应的子元素设置flex: 1,或者flex-grow: 1。
- 使用grid布局。父容器设置 display: grid,使用grid-template-rows 和 grid-template-columns 设置格子;子项使用grid-column 和 grid-row 设置在格子中的位置。 grid布局对浏览器的版本要求比较苛刻,IE基本不支持。
- 如果需要适配低端IE,可使用下面的传统的浮动布局。如果不但要适配低端IE,在布局时对DOM的结构顺序也有要求,比如对于侧边栏在左侧的布局希望自适应的元素DOM位置更靠前,则可以考虑使用圣杯布局或者双飞翼布局。
- 传统的浮动布局。定宽元素设置左浮动或者右浮动,自适应的元素响应的设置左margin或者右margin预留空间。需要注意的是为了避免父元素高度塌陷需要给父元素加清除浮动.clearfix::after{ content: ''; display: block; clear: both; }
- 圣杯布局。定宽元素和自适应元素都设置左浮,自适应元素设置宽度100%,定宽元素设置固定宽度,定宽元素因为DOM位置靠后会被挤下去。给定宽元素加上margin:-100%,此时定宽元素会上去和自适应元素的左侧重叠。给容器设置padding-left值为定宽元素宽度,给定宽元素设置position:relative,left设置负的自己的宽度。即可实现两栏布局。圣杯布局本质上利用了浮动和负margin的特性。但圣杯布局有个要求,就是自适应元素的宽度最小不能小于固定宽度侧边栏的宽度,否则就会出现错乱。
- 双飞翼布局。为了解决圣杯布局自适应元素宽度小于固定宽度侧边栏时样式出现错落的bug。原理是给自适应宽度内再多加一层div。定宽元素和自适应元素都设置左浮,自适应元素设置宽度100%,定宽元素设置固定宽度,定宽元素因为DOM位置靠后会被挤下去。给定宽元素加上margin:-100%,此时定宽元素会上去和自适应元素的左侧重叠。给自适应元素内的div加margin来规避重叠。
总结:圣杯布局和双飞翼布局都太老了,并且为了让自适应宽度DOM顺序靠前用了各自复杂操作既增加了代码的复杂度,也没有完美解决问题。推荐使用flex或者传统的两栏浮动方案。
# 解析
这种题目其实难在用口述的方式讲清楚这布局的使用方式,特别是老古董圣杯布局和双飞翼布局。当场动手写代码反而更友好。 不过不用过分担心自己的表达,面试官如果对这几种布局都很熟悉,你即使表达不清楚面试官也知道你是其实是会用的。如果面试官自己也仅仅熟悉一两种,你怎么回答都是对的。
flex两栏布局
<section>
<main>main</main>
<aside>aside</aside>
</section>
<style>
section {
display: flex;
}
aside {
width: 200px;
background: blue;
}
main {
flex-grow: 1;
order: 1;
background: red;
}
</style>
https://codepen.io/jirengu/pen/rNMVdaL
grid 两栏布局
使用grid布局,先用grid-template-columns/grid-template-rows 给容器画虚拟格子,再把元素通过 grid-column/grid-row 塞到格子里。
<section>
<main>main</main>
<aside>aside</aside>
</section>
<style>
section {
display: flex;
}
aside {
width: 200px;
background: blue;
}
main {
flex-grow: 1;
order: 1;
background: red;
}
</style>
https://codepen.io/jirengu/pen/PoGqRGV
传统浮动布局
老浏览器适用。需要注意的是浮动的侧边栏aside
的DOM位置需要在自适应宽度的正常元素main
的上方。
<section>
<aside>aside</aside>
<main>main</main>
</section>
<style>
section::after {
content: '';
display: block;
clear: both;
}
aside {
float: left;
width: 200px;
background: red;
}
main {
margin-left: 200px;
background: blue;
}
</style>
圣杯布局
- 特点:main在aside的DOM元素位置上方
- 缺点:main的宽度不能小于aside
- 演示:按照下方代码注释的step顺序,一行行复制并查看效果变化
<div class="section">
<div class="main">main</div>
<div class="aside">aside</div>
</div>
<style>
.section {
padding-left: 100px; /*step 4*/
*zoom: 1;/*兼容IE6/7*/
}
.section:after {/*IE8不支持::*/
content: '';
display: block;
clear: both;
}
.main {
width: 100%;
height: 80px; /*step 1*/
background: blue; /*step 1*/
}
.aside {
width: 100px; /*step 1*/
background: red; /*step 1*/
}
.main, .aside {
float: left; /*step 2*/
}
.aside {
margin-left: -100%; /*step 3*/
position: relative; /*step 5*/
left: -100px; /*step 5*/
}
</style>
https://codepen.io/jirengu/pen/vYXORdQ
双飞翼布局
- 特点:main在aside的DOM元素位置上方
- 缺点:main 里面还得加一层wrap
- 演示:按照下方代码注释的step顺序,一行行复制并查看效果变化
<div class="section">
<div class="main">
<div class="wrap">main</div>
</div>
<div class="aside">aside</div>
</div>
<style>
.section {
*zoom: 1; /*兼容IE6/7*/
}
.section:after { /*IE8不支持::*/
content: '';
display: block;
clear: both;
}
.main {
width: 100%; /*step 3*/
}
.main .wrap {
margin-left: 100px; /*step 5*/
background: yellow; /*step 5*/
min-height: 200px; /*step 5*/
}
.aside {
width: 100px; /*step 1*/
background: red; /*step 1*/
}
.main, .aside {
float: left; /*step 2*/
}
.aside {
margin-left: -100%; /*step 4*/
}
</style>
https://codepen.io/jirengu/pen/mdrJxKo
前端剑指offer系列已更文章mp.weixin.qq.com
关注公众号 【编程公子】,早7天获取文章更新,每日更新一篇,刷遍大厂题。下图是更新预告