最近在內部討論關于“完美三欄”的話題,看到一篇“in search of the holy grail”,相當的好.故此翻譯之.
in search of the holy grail
很抱歉我沒有將這篇文章命名.我不是想夸大他的重要性或是輕視其他的 holy grails.但是確實是這么稱呼,我們都明白它的含義.
三欄,一個是固定寬度的導航欄,另一個是google廣告,或是filckr圖片展示,就像fancy的松露巧克力一樣,和一個重要的柔滑的夾心。在這個博客流行的黃金年代它是相當適用的,加之相當大的實現難度,理所應當的獲取了”圣杯”的名稱.
有很多文章是討論關于”圣杯”的,也有很多很好的模板可以使用。但是,所有的方案都是以犧牲以下特性為代價的:合理的內容順序,寬度自適應,合理的標簽。這三者往往是這難以達到合理的布局中要折中的元素
在最近的一個項目中我終于找到了傳說中的圣杯。我在不會改變您的代碼和靈活性的前提下盡可能的描述他。他會是:
站在巨人的肩膀上
這個技術的靈感來自于alex robinson’s的 one true layout 。他曾經在他的文章里闡述過”圣杯”的若干問題,但是他的解決方法需要兩個包裝并且要求每一欄都需要一個父級div,否則很難寫內在結構.
另一方面則是由 eric meyer’s的寫法 想到的,他利用了多種類型的單元混合定位。它的例子中也是用了固定的側邊欄和自適應的中心層,可是不幸的是,他依賴于近似的百分比,不是固定的值,而且填充了一部分隨屏幕分辨率不同而自適應寬度的層。
言歸正傳,看看代碼
代碼是很直觀而且很優雅的。
(為了直觀起見,我們使用了非語義化的”中心”、”左”和”右”來闡述我們的代碼,但是我們建議您在您的代碼中使用語義化的標簽 -ed.)
<div id="header"></div>
<div id="container">
<div id="center" class="column"></div>
<div id="left" class="column"></div>
<div id="right" class="column"></div>
</div>
<div id="footer"></div>
一個額外的div包含著我們的三個層,這樣的結構符合我集中內容上一體的標記為一體的習慣(obsessive compulsive markup habits. 翻譯的不準確)
樣式很也簡單,左邊側欄是200px,右邊是150,為了簡便標示用lc,rc,cc分別代表左邊,右邊和中間,樣式如下:
body {
min-width: 550px; /* 2x lc width + rc width */
}
#container {
padding-left: 200px; /* lc width */
padding-right: 150px; /* rc width */
}
#container .column {
position: relative;
float: left;
}
#center {
width: 100%;
}
#left {
width: 200px; /* lc width */
right: 200px; /* lc width */
margin-left: -100%;
}
#right {
width: 150px; /* rc width */
margin-right: -150px; /* rc width */
}
#footer {
clear: both;
}
/*** ie6 fix ***/
* html #left {
left: 150px; /* rc width */
}
重新度量你想要的模型的價值,你會發現其實很簡單.這布局能在opera, firefox, and ie6(需要在最后一句hack).ie5.5則需要hack css盒模型。剛好也給讀者一個練習的機會(orz).
再看一遍這段優化代碼( 例子 )
原理
其實訣竅很簡單,讓左邊欄與右邊padding,右邊欄與左邊padding,中間剛好剩下自適應的內容層.
讓我們一步一步來演示這個過程
第一步:創建父級容器
包括header, footer, and container.
<div id="header"></div>
<div id="container"></div>
<div id="footer"></div>
我們讓container(包容的容器)padding-left,padding-right,padding的值分別是左邊欄和右邊欄的值.
#container {
padding-left: 200px; /* lc width */
padding-right: 150px; /* rc width */
}
我們的布局大致是這樣的:
第二步:添加欄
我們現在已經有了父級容器,現在來插入里面的三欄
<div id="header"></div>
<div id="container">
<div id="center" class="column"></div>
<div id="left" class="column"></div>
<div id="right" class="column"></div>
</div>
<div id="footer"></div>
現在我們要添加寬度和浮動屬性以使他們在一行,并且在footer清除浮動
#container .column {
float: left;
}
#center {
width: 100%;
}
#left {
width: 200px; /* lc width */
}
#right {
width: 150px; /* rc width */
}
#footer {
clear: both;
}
你會留意到中間的層已經和外面的層一樣寬了(除去padding值的情況下),一會我們就可以看見所有的層都在一起并且也是100%寬。現在,層的順序就是我們想要的順序了,但是因為中間的層占據了100%的寬度,所以左邊和右邊的層就掉了下來。
第三步:把左邊欄放在左邊
現在我們要干的是怎么樣讓這三欄在一行,中間的容器已經完全符合我們的要求了,我們只需要關注剩下的兩個,先從左邊的開始。
需要兩步讓左邊欄在左邊.第一步:讓他以100%的負margin(margin-left: -100%;)穿過中間的層,記住一定要用100%,因為中間的層的寬度是100%.
#left {
width: 200px; /* lc width */
margin-left: -100%;
}
現在左邊欄和中間欄重合,左邊重合,右邊欄浮動過來(雖然還是掉下去了),現在的結果變成了下面的樣子:
第二步:現在要把左邊欄拉到正確的位置,我們使用相對定位來抵消左邊欄的寬度
#container .columns {
float: left;
position: relative;
}
#left {
width: 200px; /* lc width */
margin-left: -100%;
right: 200px; /* lc width */
}
因為設置了right: 200px;所以左邊欄被中心層的右邊推遠了200px,剛好到了左邊。(the right property pushes it 200px away from the right edge; that is, to the left.)現在左邊欄剛好完美的到了他應該到的位置
第四步:把右邊欄放到右邊
剩下的事就是把右邊欄放在右邊,需要把他拉出容器放在容器的padding上,我們依然利用負邊距。
#right {
width: 150px; /* rc width */
margin-right: -150px; /* rc width */
}
現在所有的東西都在他該在的位置上了,沒有誰”掉隊”,呵呵。
第五步:補充設計
如果瀏覽器重新調整大小,中間的容器小于左邊欄的時候,整個布局都會被破壞掉,所以要設置一個min-width來保持我們的布局不被片破壞,雖然在ie6上這個屬性是不起作用的,但是沒關系。
body {
min-width: 550px; /* 2x lc width + rc width */
}
沒有任何一個布局是不要對ie增加一些額外的工作就能完成的。(-_-!) 在ie6中負邊距會使左邊欄離的太遠(在全寬瀏覽下),我們要用右邊欄的寬大把他向右拉回來。并且用* html來屏蔽其他瀏覽器執行他.
* html #left {
left: 150px; /* rc width */
}
至于為什么要用右邊欄的寬度,要牽涉到一些算法。當然我不會用去解釋這些,我們只要知道這樣是起作用的,我們甚至可以認為這個是ie的眾多”魔法”之一。
padding, please
我不是設計師,但是上面的布局實在是觸犯了我的審美觀。沒有邊距的欄目是很分辨和閱讀的,我們需要空隙!
使用one true layout這樣百分比布局的的弊端之一就是會造成中間層的padding困難,百分比的padding在某些分辨率下會變的很難看。可以用固定寬度的padding,但是需要給每一欄內部嵌套一個div。
用這個布局padding不是問題,他可以直接給左、右邊欄添加padding,只需響應的調整即可。例如我們要給上面的例子添加10px的padding,并且保持他(width+padding)還是200px,只需要小小的改動
#left {
width: 180px; /* lc fullwidth - padding */
padding: 0 10px;
right: 200px; /* lc fullwidth */
margin-left: -100%;
}
要給中心層加padding需要一個技巧,但是不需要額外的結構,只需額外的一小段css。加的100%的寬度導致中心層和外部的的padding以外(non-padded width)的寬度一樣寬。為了達到我們想要的效果,我們需要增加右邊的margin,使其等于padding值的總和,這樣就保證了他會是我們想要的大小。
我們改變了中間層的改變成現在這樣后,左邊欄需要移動更多的距離才能在正確的位置上,這也是技巧所在。我們需要給右邊框的抵消值(上面的right值)增加上中間層的padding值。
為了更具體的說明,我繼續以上面的例子為例,現在給每一個邊框增加10px的padding值(合計20px),中間層增加20px的padding值(合計40px),新的樣式如下:
body {
min-width: 630px; /* 2x (lc fullwidth +
cc padding) + rc fullwidth */
}
#container {
padding-left: 200px; /* lc fullwidth */
padding-right: 190px; /* rc fullwidth + cc padding */
}
#container .column {
position: relative;
float: left;
}
#center {
padding: 10px 20px; /* cc padding */
width: 100%;
}
#left {
width: 180px; /* lc width */
padding: 0 10px; /* lc padding */
right: 240px; /* lc fullwidth + cc padding */
margin-left: -100%;
}
#right {
width: 130px; /* rc width */
padding: 0 10px; /* rc padding */
margin-right: -190px; /* rc fullwidth + cc padding */
}
#footer {
clear: both;
}
/*** ie fix ***/
* html #left {
left: 150px; /* rc fullwidth */
}
當然上下的padding值能很方便的添加,具體請參看nicely padded version 中的例子
這個布局也能在em下很好的工作,但是不能在混合em和px的時候工作,選什么你來定,但是要選的正確。
等高欄
這個布局能保證所有的欄目都是等高的,實現方式是來著與one true layout的adapted wholesale。所以我不詳細解釋,增加和修改代碼如下
#container {
overflow: hidden;
}
#container .column {
padding-bottom: 20010px; /* x + padding-bottom */
margin-bottom: -20000px; /* x */
}
#footer {
position: relative;
}
我特別在下面留了10個像素
附加的說明,請注意在opera上存在一個bug,即overflow:hidden會讓你所有的欄目都變大,在one true layout上有詳細的解決辦法。你可以使用這個方法或是等opera 9(被修復了這個bug)來測試。
另一個問題是,在ie中如果內容的高度沒有背景圖片的高度高,背景不會被剪掉,他會超出footer。如果你沒有獨立的footer或是內容比背景高,這也不是個問題。如果你仍然需要一個footer,不要怕還是有解決的辦法的。用一個div把footer封裝一下就可以。
<div id="footer-wrapper">
<div id="footer"></div>
</div>
現在依然用上面我們使用到的讓各個欄目對齊的技巧來讓footer超過封裝的div,來顯示我們想要顯示的內容
* html body {
overflow: hidden;
}
* html #footer-wrapper {
float: left;
position: relative;
width: 100%;
padding-bottom: 10010px;
margin-bottom: -10000px;
background: #fff; /* same as body
background */
}
現在解決了所有的問題,得到了我們想要的結果和很少量冗余的代碼。
哦,還需要說明的
完美主義者有可能是想知道是否有一個更好的方式來做到這一點?我之前聲明過,我引用了一個非語義化的包含容器(div),確實,我們不應該包含一個額外的結構來打亂我們完美的結構.
如果你像我一樣,象知道這樣怎么去實現,不需要更多的結構,我向你介紹“wrapper-free holy grail (沒有包含的圣杯)”,其最抽象的特殊之處在于,用一個div實現了各部分 — 不多也不少,語義化,不愧于”圣杯”的美名.其原理是相似的.主體直接應用padding不需要多余的容器,而用負邊距來延伸header和footer 使其剛好達到想要的寬度.
這種布局能在所有的瀏覽器上正常工作,甚至(令人震驚)是在ie上,但是不是等高的.而且在非常小的窗口中是會”破掉”的,使用他的時候一定要謹慎.
最后
雖然文中提到的例子很具體,但是這項技術的使用范圍確實很大.為什么不能有兩個活動的中心層,為什么不調換層的順序.這些引用都超出了本文的表述范圍,但是要實現他們只需要很小的改動即可.使用圣杯是明智的.他可以成為你使用css的技巧之一.
新聞熱點
疑難解答