在 一文结尾时,我们谈到了圣杯布局,说这个布局的实现本身存在了一些问题:“在IE6/7下报废,不过不用慌,因为它可被修复”。
圣杯布局的一些谈资
下面节选一段来自网路上对圣杯的描述(略有调整):
圣杯是宗教传说中的圣物,耶稣曾经用这个杯子吩咐门徒喝下里面象征他的血的红葡萄酒,借此创立了受难纪念仪式。因为这个特殊的原因,后来有些人认为这个杯子具有某种神奇的能力。很多传说相信,如果能找到这个圣杯而喝下其盛过的水就将返老还童、死而复生并且获得永生,这个传说广泛延续到很多文学、影视、游戏等作品中。
而所谓的圣杯布局也并不是一个具象的形容,更多的是指借希望于它能够实现某种特殊的布局。
这种特殊布局的需求是:侧边栏宽度固定,主内容栏宽度自适应,并且需要将主内容栏放在侧边栏前面,以便优先渲染(不论是两栏或者三栏,需求都是一样的)。
遗留的问题
在 里,我们用圣杯布局做了 图0
的效果。
下面是我们在上篇文章中写的圣杯布局核心代码(当然,这个 #demo
容器你也可以利用 body
来取代):
HTML
12345678 |
|
CSS
1234567891011121314151617181920 | #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。(然而标准描述只是参考包含块的宽度进行计算,详情请参阅我之前的文章 )
我会用一段伪代码来详述这个事,代码如下:
1 | body > c > b > a |
假设上述代码中的 a
就是我们说的浮动元素,正常情况下 a
设置了一个百分比的margin,百分比是要参考 b
的宽度进行计算的。
然后 IE6
并没有实现这个规则,它的特征是:
- 浮动元素
a
定义了百分比的margin,假设它的祖先元素b
和c
都没有清除浮动,那么就会参照body
的宽度进行百分比换算; - 假设
b
清除了浮动,那么就会参照c
的宽度进行百分比换算;
对于这个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不变:
CSS
123456789101112131415161718 | #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个问题都被我们跳过了,这是一个更健康的实现。
当然,它也是可以任意调整列呈现顺序的,我们只需要这样就行:
CSS
123456789101112131415161718 | #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
右浮动子元素的向右负偏移量最大只能是自身宽度的问题(感兴趣的童鞋可以看看这个测试:),所以额外处理的间隙,但这其实并不影响其他浏览器。
最后
本文,更多的在于补全之前的那篇文章,算个简单的完结。本意其实并不在于说让大家去折腾那些古老而无趣的浏览器,而是希望看到的是对待任何事情,我们首先要觉得它可以解决,然后再抽丝剥茧的去实现它。未知并不可怕,可怕是恐惧未知。