我回翻了一下之前的几篇文章,发现在margin系列之布局篇 中还有些问题没说完,所以准备补全一下。
不过这2篇文章的时间的跨度有点,在阅读本文之前,大家可以先翻读一下3年前的那篇文章。
在margin系列之布局篇 一文结尾时,我们谈到了圣杯布局,说这个布局的实现本身存在了一些问题:“在IE6/7下报废,不过不用慌,因为它可被修复”。
我一直以为写完了,然而今天看了下发现还有坑没填。所以本文会讲讲这些问题是什么以及如何修复。
下面节选一段来自网路上对圣杯的描述(略有调整):
圣杯是宗教传说中的圣物,耶稣曾经用这个杯子吩咐门徒喝下里面象征他的血的红葡萄酒,借此创立了受难纪念仪式。因为这个特殊的原因,后来有些人认为这个杯子具有某种神奇的能力。很多传说相信,如果能找到这个圣杯而喝下其盛过的水就将返老还童、死而复生并且获得永生,这个传说广泛延续到很多文学、影视、游戏等作品中。
而所谓的圣杯布局也并不是一个具象的形容,更多的是指借希望于它能够实现某种特殊的布局。
这种特殊布局的需求是:侧边栏宽度固定,主内容栏宽度自适应,并且需要将主内容栏放在侧边栏前面,以便优先渲染(不论是两栏或者三栏,需求都是一样的)。
在margin系列之布局篇 里,我们用圣杯布局做了 图0
的效果。
下面是我们在上篇文章中写的圣杯布局核心代码(当然,这个 #demo
容器你也可以利用 body
来取代):
<div id="demo">
<header id="hd">头部</header>
<div id="bd">
<div id="main">主内容栏自适应宽度</div>
<aside id="aside">侧边栏固定宽度</aside>
</div>
<footer id="ft">底部</footer>
</div>
#demo {
width: 80%;
}
#bd {
*zoom: 1;
overflow: hidden;
padding-left: 210px;
}
#main {
float: left;
width: 100%;
}
#aside {
_display: inline;
float: left;
position: relative;
left: -210px;
width: 200px;
margin-left: -100%;
}
大家可以使用各种浏览器来测试一下这个示例 圣杯:左栏固定主内容自适应
一般情况下,你会发现除了 IE6
外,其它的浏览器看起来都算正常,然而问题的范围可能并不仅限于这些,大部分问题没被看出只不过是因为没到达边界。
问题列表:
IE6
布局错乱,侧边栏位置不对; IE7
resize窗口时,侧边栏会跳动; IE7及其它浏览器
,当窗口缩小到主内容栏的宽度小于侧边栏的宽度时,布局错乱; 上面这几个问题,大家其实都可以自己去测测看,应该是当前的实现中都存在的。
对于第3点,我们看看上述的代码实现,还是能非常轻松的理解的。因为侧边栏定义了 margin-left: -100%
,在这个场景中, 100%
其实就等同于主内容栏的宽度。如果主内容栏的宽度小于侧边栏,那么侧边栏偏移了一个比自己小的宽度,自然是放不下自己的。
对于第1点,这个就有点意思了,基本上这又算是IE6的一个Bug,描述一下这个Bug的现象:
在IE6中,假定是处于默认的书写模式下,当一个浮动的元素定义了margin的值是一个百分比,那么此时,浮动元素的margin百分比参照最近的清除了浮动的包含块的父元素的宽度进行计算,或者参照body。(然而标准描述只是参考包含块的宽度进行计算,详情请参阅我之前的文章margin系列之百分比)
我会用一段伪代码来详述这个事,代码如下:
body > c > b > a
假设上述代码中的 a
就是我们说的浮动元素,正常情况下 a
设置了一个百分比的margin,百分比是要参考 b
的宽度进行计算的。
然后 IE6
并没有实现这个规则,它的特征是:
a
定义了百分比的margin,假设它的祖先元素 b
和 c
都没有清除浮动,那么就会参照 body
的宽度进行百分比换算; b
清除了浮动,那么就会参照 c
的宽度进行百分比换算; 对于这个Bug,我写了一个示例,大家可以对照着描述来看这个例子: 浮动margin百分比在ie6上的Bug
好了,知道了在 IE6
中有这个Bug之后,关于问题列表中的第1点,我们就也能够理解了,因为 position: relative; left: -210px;
这个定义对于 IE6
来讲,其实是多余的。
对于第2点,应该是在resize过程中,不断的重绘造成的,它需要不断的去计算这个百分比的使用值。
所以如果想使得圣杯布局变得更靠谱一些,我们要么就是见招拆招,修复这个问题(比如说为 IE6
重置掉 position: relative; left: -210px;
定义),要么就避免遇上这些问题,我更喜欢第二种的方式。
我们如何做才能避免遇上这些问题?
其实我们可以细看一下,问题列表中的几点,其实都是因浮动元素的margin百分比引发的。既然浮动元素的margin百分比,在各浏览器下需要差异化处理,那么干脆弃用百分比,改用固定值(复杂度其实并没有上升,因为用百分比的时候,还得给 left
定义一个固定的偏移量)。
那么,新的问题来了。如果改用margin固定值,我们要如何知道这个固定值是多少?比如在这个布局中我们的容器宽度是视窗的 80%
,我们无法得到侧边栏需要偏移的固定值是多少,除非我们使用运算表达式 calc()
,但是它的兼容性并不是我们想要的。
这是因为主内容栏和侧边栏都是左浮动,并且侧边栏浮动在主内容栏后面,所以我们需要让侧边栏偏移 #main + #aside
的宽度,才能让侧边栏出现在正确的位置。
所以,其实我们可以转变一下思路,让主内容栏和侧边栏朝不同的方向浮动,这样的话,侧边栏只需要偏移自身的宽度就能出现在正确的位置上,不在需要使用margin百分比值。
我们按照前面说的将代码调整一下,HTML不变:
#demo {
width: 80%;
}
#bd {
*zoom: 1;
overflow: hidden;
padding-left: 210px;
}
#main {
float: right;
width: 100%;
}
#aside {
_display: inline;
float: left;
width: 200px;
margin: 0 10px 0 -210px;
}
我们来看看这个 进化的圣杯:左栏固定主内容自适应 效果,你会欣喜的发现,问题列表中的3个问题都被我们跳过了,这是一个更健康的实现。
当然,它也是可以任意调整列呈现顺序的,我们只需要这样就行:
#demo {
width: 80%;
}
#bd {
*zoom: 1;
overflow: hidden;
padding-right: 210px;
}
#main {
float: left;
width: 100%;
}
#aside {
_display: inline;
float: right;
width: 200px;
margin: 0 -210px 0 10px;
}
于是我们就得到了一个 进化的圣杯:右栏固定主内容自适应 的布局。
总体来讲,圣杯布局只是有能力达成我们的需求,但就其本身来讲并不是太先进的布局,灵活性相对局限。
另外,你可能关注到了代码中出现的 margin
定义,它并不是一个单纯的负值,而是多了一个 10px
,这其实是为了解决 IE6/7
右浮动子元素的向右负偏移量最大只能是自身宽度的问题(感兴趣的童鞋可以看看这个测试: 右浮动margin-right负值在ie67上的bug ),所以额外处理的间隙,但这其实并不影响其他浏览器。
本文,更多的在于补全之前的那篇文章,算个简单的完结。本意其实并不在于说让大家去折腾那些古老而无趣的浏览器,而是希望看到的是对待任何事情,我们首先要觉得它可以解决,然后再抽丝剥茧的去实现它。未知并不可怕,可怕是恐惧未知。