Web前端面试中遇到CSS的问题,难免会提到两栏或三栏布局,之前这是笔者的痛处,看过很多关于布局的文章,但总感觉不得其精要,现在秋招来临,不管怎么样,都要把之前遗留下的问题解决一下,于是有了这篇文章,与君共勉。
两栏布局和三栏布局最常见的就是左边或右边或两边都是固定的宽度,而中间栏自适应,下面就着重讲这方面的布局。
一、两栏布局(左边固定,右边自适应)
假如html与css初始代码如下,该如何实现该布局呢?
<div class="container" id="col-2-m*"> <aside class="aside"></aside> <main class="main"></main> </div>
*{ padding:0; margin:0; } .container{ background-color:#ccc; } .aside{ width:400px; height:400px; background-color:#fefedd; } .main{ background-color:#99fefe; height:400px; }
利用float+margin布局:
#col-2-m1 .aside{ float:left; } #col-2-m1 .main{ margin-left:400px; }
说明:将.aside设为左浮动,它就会浮动在.container的左边;然后为.main设置左外边距为侧边栏的宽度即可。我们知道float元素会脱离文档流,如果不设置.main的左外边距的话,.main子元素可能会覆盖到侧边栏。比如就文本来说,文本会围绕浮动元素,若.aside的高度不为.container的高度,.main文本会流到.aside下面。
利用float+bfc布局
#col-2-m2 .aside{ float:left; } #col-2-m2 .main{ overflow:auto;/*overflow不为visible就可以得到bfc*/ }
说明:另一种可使用的方法是利用bfc(块级格式上下文)的性质,关于bfc,我会在下一篇文章中讲解。当overflow不为visible就可以创建一个bfc,bfc的区域不与float box重叠,所以满足要求。
总结:如果你细心的话,可以发现我刚介绍的这两种布局的html结构顺序为侧边栏在main的上面,也就是当我们得到页面时会先渲染侧边栏,其实我们大多数时候都是要先渲染main栏的,因为侧边栏包含的一般都是不重要的东西,而主栏却是重要的东西。所以这中结构不够实用。不过这种结构的好处是布局方便简单。
好了,假如是以下html结构呢,该怎么做呢?
<div class="container" id="col-2-m*"> <main class="main"></main> <aside class="aside"></aside> </div>
上面的这种结构是大部分两栏布局采用的结构,布局方法有很多也很杂,我们慢慢来。
利用margin+position布局
#col-2-m3{ position:relative; } #col-2-m3 .aside{ top:0; left:0; position:absolute; } #col-2-m3 .main{ margin-left:400px }
说明:这种方法其实无关html结构,即.aside和.main谁先谁后都没问题,因为.aside是绝对定位在.container的左端的。只不过需要注意为父元素设置相对定位方式。需要注意的是,如果中间栏含有最小宽度限制,或是含有宽度的内部元素,则浏览器窗口小到一定程度,主面板与侧栏会发生重叠,特别是对三栏布局来说。
利用padding+负margin+float+postition布局
#col-2-m4{ padding-left:400px; box-sizing:border-box; } #col-2-m4 .aside{ float:left; margin-left:-100%; position:relative; left:-400px; } #col-2-m4 .main{ float:left; width:100%; }
分析:此为经典的圣杯布局。该种布局方式初看挺复杂的,其实仔细想想也就释然了(我也就想了好几天吧)。它与之前一个很大的不同的地方是.aside和.main都成了左浮动,而且用到了负外边距。.main没有用到margin-left,取而代之的是父元素设置了padding-left,padding-left也是为.aside留的区域。而.main宽度又是100%,所以.aside会被挤到下一行去,在下一行什么位置那?在main的正下方。要把.aside拉上去,使用
margin-left:-100%
可以做到,此时.aside就变成了同.main的左部完全对齐,但要把它变为和.container左边对齐,所以要设置一个相对定位,left为负的它的宽度,这样设置后就完成了两栏布局。其中我为.container设置了box-sizing:border-box;
是为了去掉水平方向的滚动条。因为让.main的宽度为100%(即.container的宽度也是浏览器窗口的宽度)再加上父盒子的左内边距就会使整个布局宽度大于浏览器窗口的宽度了,设置为border-box实际上就会使width=浏览器窗口宽度-左内边距,不会发生溢出。
注意:圣杯布局有个很大的缺点是,当.main的宽度小于.aside的宽度时布局就会发生错乱,就本例子而言,你可以试试将浏览器窗口宽度缩小到800px及一下就可以看到这种现象。这是因为将.aside拉到上一行时,上一行的宽度不足以支撑整个.aside,所以造成了布局混乱。如下图所示:
解决方法可以是为body设置min-width为大于2倍.aside的宽度,或使用其他方式的布局。感觉怎么样了?是不是渐入佳境了?接下来思考一下以下html结构:
<div class="container" id="col-2-m5"> <div class="main-wrap"> <main class="main"></main> </div> <aside class="aside"></aside> </div>
该种形式的html结构在.main的外围包裹了一层div,这种方法叫双飞翼布局,实际上是对上面圣杯布局的改进。该怎么做?
利用负margin+float+margin布局
#col-2-m5 .aside{ float:left; margin-left:-100%; } #col-2-m5 .main-wrap{ float:left; width:100%; } #col-2-m5 .main{ margin-left:400px; }
分析:此为双飞翼布局,它和圣杯布局相同点是对子盒子都用了浮动定位,不过它对于圣杯布局有了几点的改进,而且它解决了圣杯布局主栏宽度小于侧边栏宽度时的布局错乱问题。用了一层div包裹.main,它的好处是不需要.container使用padding-left了,而且.aside没必要相对定位了。为什么这么说呢?首先针对前者,因为在.main上包裹一层,所以可以直接使用.main的margin-left来实现留出固定宽度的区域给.aside,而.main-wrap可以直接设置宽度为100%;而因为.main-wrap和.container左边界是对齐的,.aside设置
margin-left:100%
也就直接到了上层的左边界,每必要再做相对偏移了。
注意:其实对于所有的负margin实现把.aside拉到上面一层去,都可用绝对定位来实现的。它们可以相互替换,自己做布局时不要拘泥于这些,只要做出来就好了。
最简单的方法要来了,相信你也想到了,那就是flex布局了。
针对上面的结构1、2,我们可以这样来实现:#col-2-m6{ display:flex; /*针对.aside在.main下面的html结构顺序还需加入以下语句: flex-direction:row-reverse; */ } #col-2-m6 .main{ flex:1; }
注意:因为flex布局默认按html结构顺序从左到右布局,所以针对.aside在.main下面的结构,为避免侧边栏在右边,还要加个
flex-direction:row-reverse;
,以使侧边栏恢复到左侧。
二、 三栏布局(左右固定,中间自适应)
如果你已经深入理解了两栏布局,三栏布局自然手到擒来了。三栏布局相对于二栏布局来说,相应的外边距和内边距都有加上右内边距了;而对于负外边距来说,右边栏的负外边距要设置为负的自身的宽度,以使其能准确靠在右侧。
下面是我的代码,仅供参考:
<h3>三列布局,一列自适应,两列固定宽度 margin+float</h3>
<div class="container col-3-m1">
<aside class="aside">aside1</aside>
<aside class="extra">aside2</aside>
<main class="main">main</main>
</div>
<h3>三列布局,一列自适应,两列固定宽度圣杯布局 负margin+float+padding+position</h3>
<div class="container col-3-m2">
<main class="main">main</main>
<aside class="aside">aside1</aside>
<aside class="extra">aside2</aside>
</div>
<h3>三列布局,一列自适应,两列固定宽度双飞翼布局 负margin+float+margin</h3>
<div class="container col-3-m3">
<div class="main-wrap">
<main class="main">main</main>
</div>
<aside class="aside">aside1</aside>
<aside class="extra">aside2</aside>
</div>
<h3>三列布局,一列自适应,两列固定宽度 flex布局</h3>
<div class="container col-3-m4">
<aside class="aside">aside1</aside>
<main class="main">main</main>
<aside class="extra">aside2</aside>
</div>
<h3>三列布局,一列自适应,两列固定宽度 float+position</h3>
<div class="container col-3-m5">
<aside class="aside">aside1</aside>
<main class="main">main</main>
<aside class="extra">aside2</aside>
</div>
*{
padding:0;
margin:0;
*box-sizing:border-box;
}
h3{
text-align:center;
margin:5px 0;
}
.container{
height:500px;
*border:2px solid red;
position:relative;
}
.aside{
width:150px;
height:100%;
*border:2px solid blue;
background-color:#88ff88;
}
.extra{
width:200px;
height:100%;
*border:2px solid green;
background-color:#88f;
}
.main{
height:100%;
*border:2px solid orange;
background-color:#ff8888;
}
.col-3-m1 .aside{
float:left;
}
.col-3-m1 .extra{
float:right;
}
.col-3-m1 .main{
margin-left:150px;
margin-right:200px;
}
.col-3-m2{
padding:0 200px 0 150px;
}
.col-3-m2 .aside{
float:left;
margin-left:-100%;
position:relative;
left:-150px;
}
.col-3-m2 .extra{
float:left;
margin-left:-200px;
position:relative;
right:-200px;
}
.col-3-m2 .main{
width:100%;
float:left;
}
.col-3-m3 .aside{
float:left;
margin-left:-100%;
}
.col-3-m3 .extra{
float:left;
margin-left:-200px;
}
.col-3-m3 .main-wrap{
width:100%;
float:left;
height:100%;
}
.col-3-m3 .main{
margin-left:150px;
margin-right:200px;
}
.col-3-m4{
display:flex;
}
.col-3-m4 .main{
flex:1;
}
.col-3-m5 .aside{
position:absolute;
top:0;
left:0;
}
.col-3-m5 .extra{
position:absolute;
top:0;
right:0;
}
.col-3-m5 .main{
margin-left:150px;
margin-right:200px;
}
参考资料:
CSS布局十八般武艺都在这里了