转载

margin系列之圣杯拾遗

接续

我回翻了一下之前的几篇文章,发现在margin系列之布局篇 中还有些问题没说完,所以准备补全一下。

不过这2篇文章的时间的跨度有点,在阅读本文之前,大家可以先翻读一下3年前的那篇文章。

没填完的坑

在margin系列之布局篇 一文结尾时,我们谈到了圣杯布局,说这个布局的实现本身存在了一些问题:“在IE6/7下报废,不过不用慌,因为它可被修复”。

我一直以为写完了,然而今天看了下发现还有坑没填。所以本文会讲讲这些问题是什么以及如何修复。

圣杯布局的一些谈资

下面节选一段来自网路上对圣杯的描述(略有调整):

圣杯是宗教传说中的圣物,耶稣曾经用这个杯子吩咐门徒喝下里面象征他的血的红葡萄酒,借此创立了受难纪念仪式。因为这个特殊的原因,后来有些人认为这个杯子具有某种神奇的能力。很多传说相信,如果能找到这个圣杯而喝下其盛过的水就将返老还童、死而复生并且获得永生,这个传说广泛延续到很多文学、影视、游戏等作品中。

而所谓的圣杯布局也并不是一个具象的形容,更多的是指借希望于它能够实现某种特殊的布局。

这种特殊布局的需求是:侧边栏宽度固定,主内容栏宽度自适应,并且需要将主内容栏放在侧边栏前面,以便优先渲染(不论是两栏或者三栏,需求都是一样的)。

遗留的问题

在margin系列之布局篇 里,我们用圣杯布局做了 图0 的效果。

margin系列之圣杯拾遗

下面是我们在上篇文章中写的圣杯布局核心代码(当然,这个 #demo 容器你也可以利用 body 来取代):

HTML

<div id="demo">
<header id="hd">头部</header>
<div id="bd">
<div id="main">主内容栏自适应宽度</div>
<aside id="aside">侧边栏固定宽度</aside>
</div>
<footer id="ft">底部</footer>
</div>

CSS

#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,假设它的祖先元素 bc 都没有清除浮动,那么就会参照 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不变:

CSS

#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

#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 ),所以额外处理的间隙,但这其实并不影响其他浏览器。

最后

本文,更多的在于补全之前的那篇文章,算个简单的完结。本意其实并不在于说让大家去折腾那些古老而无趣的浏览器,而是希望看到的是对待任何事情,我们首先要觉得它可以解决,然后再抽丝剥茧的去实现它。未知并不可怕,可怕是恐惧未知。

原文  http://blog.doyoe.com/2016/04/28/css/margin系列之圣杯拾遗/
正文到此结束
Loading...