這是《CSS設計指南》的讀書筆記,用于加深學習效果。
display 屬性
display
是 CSS 中最重要的用于控制布局的屬性。每個元素都有一個預設的 display 值。對于大多數元素它們的預設值通常是 block 或 inline 。一個 block 元素通常被叫做塊級元素。一個 inline 元素通常被叫做行内元素。
block
div
是一個标準的塊級元素。一個塊級元素會新開始一行并且盡可能撐滿容器。其他常用的塊級元素包括
p
、
form
和HTML5中的新元素:
header
、
footer
、
section
等等。
inline
img
是一個标準的行内元素。你可以把兩個
<img>
标簽寫在兩行,但這并不影響圖檔再浏覽器中的顯示效果,它們會并列出現在一行上。而且标簽直接的空白(标記中的兩個
标簽雖然分别位于兩行,但這并不影響圖檔在浏覽器中顯示時的效果。圖檔是行内元素,是以它們顯示的時候就會并列出現在一行上。而且,标簽之間的空白(包括制表、回車和空格)都會被浏覽器忽略。
a
元素是最常用的行内元素,它可以被用作連結。
none
另一個常用的 display 值是 none。一些特殊元素的預設 display 值是它,例如
script
。display:none 通常被 JavaScript 用來在不删除元素的情況下隐藏或顯示元素。把display設定為 none,該元素及所有包含在其中的元素,都不會在頁面中顯示。它們原來占據的空間也會被回收。
相對的屬性是 visibility
,這個屬性常用的值是 visible(預設)和 hidden。把元素的 visibility 設定為 hidden,元素會隐藏,但它占據的空間仍然存在。
其他 display 值
還有很多的更有意思的 display 值,幾乎所有HTML元素的display屬性值要麼為block,要麼為inline。最明顯的一個例外是table元素,它有自己特殊的display屬性值。這裡有一份詳細的清單。
塊級元素(比如标題和段落)會互相堆疊在一起沿頁面向下排列,每個元素分别占一行。而行内元素(比如連結和圖檔)則會互相并列,隻有在空間不足以并列的情況下才會折到下一行顯示 。
塊級元素和行内元素是可以互相轉化的:
/*預設為塊級元素*/
p {display: inline;}
/*預設為行内元素*/
a {display: block;}
複制
屬性了 display 屬性之後,我們來看下頁面布局:
布局的基本概念
多欄布局有三種基本的實作方案:
固定寬度
、
流動
、
彈性
。
- 固定寬度布局的大小不會随使用者調整浏覽器視窗大小而變化,一般是900到1100像素寬。其中960像素是最常見的,因為這個寬度适合所有現代顯示器,而且能夠被16、12、10、8、6、5、4和3整除,不僅容易計算等寬分欄的數量,而且計算結果也能得到沒有小數的像素數。
- 流動布局的大小會随使用者調整浏覽器視窗大小而變化。這種布局能夠更好地适應大螢幕,但同時也意味着放棄對頁面某些方面的控制,比如随着頁面寬度變化,文本行的長度和頁面元素之間的位置關系都可能變化。Amazon.com的頁面采用的就是流動中欄布局,在各欄寬度加大時通過為内容元素周圍添加空白來保持内容居中,而且現在的導覽列會在布局變窄到某個寬度時收縮進一個下拉菜單中,進而為内容騰出空間。
- 彈性布局與流動布局類似,在浏覽器視窗變寬時,不僅布局變寬,而且所有内容元素的大小也會變化,讓人産生一種所有東西都變大了的感覺。
布局的高度
多數情況下,布局中結構化元素(乃至任何元素)的高度是不必設定的。事實上,我甚至想告訴你根本不應該給元素設定高度。除非你确實需要這樣做,比如在頁面中創造一個絕對定位的元素。
為什麼正常情況下都應該保持元素height屬性的預設值auto不變呢?很簡單,隻有這樣元素才能随自己包含内容的增加而在垂直方向上擴充。這樣擴充的元素會把下方的元素向下推,而布局也能随着内容數量的增減而垂直伸縮。假如你明确設定了元素的高度,那麼超出的内容要麼被剪掉,要麼會跑到容器之外——取決于元素overflow屬性的設定。
布局的寬度
與高度不同,我們需要更精細地控制布局寬度,以便随着浏覽器視窗寬度的合理變化,布局能夠作出适當的調整,確定文本行不會過長或過短。如果随意給元素添加内邊距、邊框,或者元素本身過大,導緻浮動元素的寬度超過包含元素的布局寬度,那浮動元素就可能“躲”到其他元素下方。應該讓這些内容元素自動擴充到填滿欄的寬度。(這是塊級元素的預設行為)
三欄-固定寬度布局
我們先從一個簡單的居中的單欄布局開始吧。看下面 HTML 代碼,主要标記的 ID 是 wrapper:
<div id="wrapper">
<article>
<h1>Single-Column Layout</h1>
<p>這是第一段</p>
<h2>This is a Second-Level Heading</h2>
<p>這是第二段</p>
</article>
</div>
複制
布局相關 css 如下:
#wrapper {
width:960px; margin:0 auto; border:1px solid;
}
article {
background:#ffed53;
}
複制
如圖所示,通過給外包裝設定寬度值,并将其水準外邊距設定為 auto,這個單欄布局在頁面上居中了。随着向裡添加内容,這一欄的高度會相應增加。外包裝中的article元素本質上就是一個沒有寬度的塊級盒子(關于“沒有寬度的盒子”,請參見3.2節),它水準擴充填滿了外包裝。
下面,我們再向外包裝裡添加一個導航元素,讓它作為第二欄。
HTML 代碼如下:
<div id="wrapper">
<nav>
<ul>
<li><a href="#">Link 1</a></li>
<li><a href="#">Link 2</a></li>
<li><a href="#">Link 3</a></li>
</ul>
</nav>
<article>
<h1>Single-Column Layout</h1>
<p>這是第一段</p>
<h2>This is a Second-Level Heading</h2>
<p>這是第二段.</p>
</article>
</div>
複制
這裡我們将兩欄都添加
float: left
,以讓它們并排顯示。
#wrapper {
width:960px;
margin:0 auto;
border:1px solid;
overflow:hidden;
}
nav {
width:150px;
float:left; /*浮動*/
background:#dcd9c0;
}
nav li {
/*去掉清單項目符号*/
list-style-type:none;
}
article {
width:810px;
float:left; /*浮動*/
background:#ffed53;
}
複制
這裡我們把兩欄的總寬度設定為外包裝的寬度(150+810=960),并浮動它們,就可以創造出并肩排列的兩欄來。每一欄的長度取決于内容多少。
接下來我們添加第三欄。
<div id="wrapper">
<nav>
<ul>
<li><a href="#">Link 1</a></li>
<li><a href="#">Link 2</a></li>
<li><a href="#">Link 3</a></li>
</ul>
</nav>
<article>
<h1>Single-Column Layout</h1>
<p>這是第一段</p>
<h2>This is a Second-Level Heading</h2>
<p>這是第二段.</p>
</article>
<aside>
<h3>This is the Sidebar</h3>
<p>這是側邊欄.</p>
</aside>
</div>
複制
接下來我們調整一下 article 這一欄的寬度,為第三欄騰出空間
#wrapper {
width:960px; margin:0 auto; border:1px solid; overflow:hidden;
}
nav {
width:150px;
float:left;
background:#dcd9c0;
}
article {
width:600px;
float:left;
background:#ffed53;
}
aside {
width:210px;
float:left;
background:#3f7ccf;
}
複制
如圖所示,通過把三個浮動容器的總寬度設定為恰好等于外包裝的寬度(150+600+210=960),就有了三欄布局的架構。
現在我們再添加一個頁眉和頁腳:
<div id="wrapper">
<header>
<h1>A Fixed-Width Layout</h1>
</header>
<nav>
<ul>
<li><a href="#">Link 1</a></li>
<li><a href="#">Link 2</a></li>
<li><a href="#">Link 3</a></li>
</ul>
</nav>
<article>
<h1>Single-Column Layout</h1>
<p>這是第一段</p>
<h2>This is a Second-Level Heading</h2>
<p>這是第二段.</p>
</article>
<aside>
<h3>This is the Sidebar</h3>
<p>這是側邊欄.</p>
</aside>
<footer>
<p>This is the footer. Phasellus pretium gravida interdum. Nam interdum posuere tempus. Ut commodo laoreet dolor, non hendrerit mi dictum vitae. Nam nec egestas libero.</p>
</footer>
</div>
複制
為了讓頁腳在最下一欄不浮動到
aside
後邊,我們為頁腳應用
clear:both
,以組織它向上移動。css 樣式如下:
* {margin:0; padding:0;}
#wrapper {
width:960px;
margin:0 auto;
border:1px solid;
}
header {
background:#f00;
}
nav {
width:150px;
float:left;
background:#dcd9c0;
}
nav li {
list-style-type:none;
}
article {
width:600px;
float:left;
background:#ffed53;
}
aside {
width:210px;
float:left;
background:#3f7ccf;
}
footer {
clear:both;
background:#000;
}
複制
現在效果如圖:
現在各欄太擁擠,每欄的高度也都由文本内容決定,我們現在修改一下,為内容間加上空白。
為欄設定内邊距和邊距
為了讓内容與欄邊界空開距離,為欄添加水準外邊距和内邊距,但這樣會導緻布局寬度增大,進而浮動欄下滑。比如,我們給 article 增加内邊距:
article {
width: 600px;
float: left;
background: #ffed53;
padding: 10px 20px;
}
複制
效果如圖:
由于增加了内邊距導緻
article
的總寬度增加,導緻右邊的欄不能再與前兩排并列在一起。有三種方法來預防改問題發生:
- 從設定的元素寬度中減去添加的水準外邊距、邊框和内邊距的寬度和。
- 在容器内部的元素上添加内邊距或外邊距。
- 使用CSS3的box-sizing屬性切換盒子縮放方式,比如section {box-sizing:border-box;} 。 應用box-sizing屬性後,給section添加邊框和内邊距都不會增大盒子,相反會導緻内容變窄。
重設寬度以抵消内邊距和邊框
一個代代相傳的解決方案是通過數學計算。CSS開發者需要用比他們實際想要的寬度小一點的寬度,需要減去内邊距和邊框的寬度。
比如我們給600像素寬的中間欄增加了20像素的内邊距,為了抵消增加的内邊距,可以把欄減少40像素而設定為560像素。
值得慶幸地是你不需要再這麼做了...
給容器内部元素應用内邊距和邊框
把外邊距和内邊距應用到内容元素上确實有效,不過這樣的前提是這些元素沒有明确的設定寬度,這樣内容才會随内外邊距的增加而縮小。與其為容器中的元素添加外邊距,不如在欄中再添加一個沒有寬度的div,讓它包含所有内容元素,然後再給這個div應用邊框和内邊距。如此一來,隻要為内部div設定一次樣式,就可以把讓所有内容元素與欄邊界保持一緻的距離。而且,将來再需要調整時也會很友善。任何新增内容元素的寬度都由這個内部div決定。
下面我們用這種方法修複上面第三欄浮動到下邊的問題。
<article>
<div class="inner">
<!-- 這裡是各種内容 -->
</div>
</article>
複制
接下來,我們不僅要給内部 div 應用内邊距,還要給她應用外邊距和邊框。
/*更新 css*/
article {
width:600px;
float:left;
background:#ffed53;
}
article .inner {
margin:10px;
border:2px solid red;
padding:20px;
}
複制
效果如圖:
以上措施使布局有了明顯改觀。就這麼簡單的幾下,布局就顯得更專業了。處理欄及其内部div的關鍵在于,浮動欄并設定欄寬,但不給任何内容元素設定寬度。要讓内容元素擴充以填充它們的父元素——内部div。這樣,隻要簡單地設定内部div的外邊距和内邊距,就可以讓它們以及它們包含的内容與欄邊界保持一定距離。
使用 box-sizing:border-box
人們慢慢的意識到傳統的盒子模型不直接,是以他們新增了一個叫做 box-sizing 的CSS屬性。當你設定一個元素為 box-sizing: border-box; 時,此元素的内邊距和邊框不再會增加它的寬度。這裡有一個與前一頁相同的例子,唯一的差別是兩個元素都設定了 box-sizing: border-box;
nav {
-webkit-box-sizing:border-box;
-moz-box-sizing:border-box;
box-sizing:border-box;
width:150px;
float:left;
background:#dcd9c0;
padding:10px 10px;
}
article {
-webkit-box-sizing:border-box;
-moz-box-sizing:border-box;
box-sizing:border-box;
width:600px;
float:left;
background:#ffed53;
padding:10px 20px;
}
aside {
-webkit-box-sizing:border-box;
-moz-box-sizing:border-box;
box-sizing:border-box;
width:210px;
float:left;
background:#3f7ccf;
padding:10px 10px;
}
複制
這是目前為止最好的解決方法了,那最簡單有效的方法就是在 css 裡添加這樣一條規則:
* {
-webkit-box-sizing:border-box;
-moz-box-sizing:border-box;
box-sizing:border-box;
}
複制
三欄-中欄流動布局
中欄流動布局的目的是在螢幕變窄時,中欄變窄,左欄和右欄寬度不變。這裡我們使用
負外邊距
實作。
用負外邊距實作
實作三欄布局且讓中欄内容區流動(不固定)的核心問題是處理右欄的定位,并在中欄内容區大小改變時控制右欄與布局的關系。
這裡我們使用
Ryan Brill
給出的控制兩個外包裝容器的外邊距的解決方案。其中一個外包裝包圍三欄,另一個外保障包圍左欄和中欄。
html代碼示例如下:
<div id="main_wrapper">
<header>
<!-- 頁眉-->
</header>
<div id="threecolwrap">/*三欄外包裝(包圍全部三欄)*/
<div id="twocolwrap">/*兩欄外包裝(包圍左欄和中欄)*/ /*左欄*/
<nav>
<!-- 導航 -->
</nav> /*中欄*/
<article>
<!-- 區塊 -->
</article>
</div>/*結束兩欄外包裝(twocolwrap)*/ /*右欄*/
<aside>
<!-- 側欄 -->
</aside>
</div>/*結束三欄外包裝(threecolwrap)*/
<footer>
<!-- 頁腳 -->
</footer>
</div>
複制
css規則如下:
* {
margin: 0;
padding: 0;
}
body {
font: 1em helvetica, arial, sans-serif;
}
div#main_wrapper {
min-width: 600px;
max-width: 1100px;
/*超過最大寬度時,居中布局*/
margin: 0 auto;
/*背景圖檔預設從左上角開始拼接*/
background: url(images/bg_tile_150pxw.png) repeat-y #eee;
}
header {
padding: 5px 10px;
background: #3f7ccf;
}
div#threecolwrap {
/*浮動強制它包圍浮動的欄*/
float: left;
width: 100%;
/*背景圖檔右對齊*/
background: url(images/bg_tile_210pxw.png) top right repeat-y;
}
div#twocolwrap {
/*浮動強制它包圍浮動的欄*/
float: left;
width: 100%;
/*把右欄拉到區塊外邊距騰出的位置上*/
margin-right: -210px;
}
nav {
float: left;
width: 150px;
background: #f00;
padding: 20px 0;
}
/*讓子元素與欄邊界保持一定距離*/
nav>* {
margin: 0 10px;
}
article {
width: auto;
margin-left: 150px;
/*在流動居中的欄右側騰出空間*/
margin-right: 210px;
background: #eee;
padding: 20px 0;
}
/*讓子元素與欄邊界保持一定距離*/
article>* {
margin: 0 20px;
}
aside {
float: left;
width: 210px;
background: #ffed53;
padding: 20px 0;
}
/*讓子元素與欄邊界保持一定距離*/
aside>* {
margin: 0 10px;
}
footer {
clear: both;
width: 100%;
text-align: center;
background: #000;
}
複制
基本原理:
上面兩幅圖展示了流動中欄布局。三欄中的右欄是210像素寬。為了給右欄騰出空間,中欄article元素有一個210像素的右外邊距。包圍左欄和中欄的兩欄外包裝上210像素的負右外邊距,會把右欄拉回article元素右外邊距(在兩欄外包裝内部右側)創造的空間内。中欄aticle元素的寬度是auto,是以它仍然會力求占據浮動左欄剩餘的所有空間。可是,一方面它自己的右外邊距在兩欄外包裝内為右欄騰出了空間,另一方面兩欄外包裝的負右外邊距又把右欄拉到了該空間内。
百分比寬度
上面的例子中,我們用到了
百分比寬度
,百分比是一種相對于包含塊的計量機關。你還能同時使用 min-width 和 max-width 來限制最大或最小寬度!
你可以用百分比做布局,但是這需要更多的工作。如果我們上邊的例子中 nav 用百分比寬度做布局,當視窗寬度很窄時 nav 的内容會以一種不太友好的方式被包裹起來。
inline-block 布局
上面的例子我們實作多欄并列的方式是使用
float
,不過我們也可以使用
inline-block
。下邊是我們把 float 替換為
inline-block
的例子。
nav {
width:150px;
display: inline-block;
vertical-align: top;
background:#dcd9c0;
}
article {
word-spacing:0;
width:600px;
display: inline-block;
vertical-align: top;
background:#ffed53;
}
aside {
word-spacing:0;
width:210px;
display: inline-block;
vertical-align: top;
background:#3f7ccf;
}
複制
使用
inline-block
,有一些事情需要你牢記:
- vertical-align 屬性會影響到 inline-block 元素,你可能會把它的值設定為 top 。
- 你需要設定每一列的寬度
- 如果HTML源代碼中元素之間有空格,那麼列與列之間會産生空隙
特别是第三條,如果我們不做任何修改,兩個 block 之間會存在空格,像這樣:
因為列與列之間産生了空格,是以 aside 跑到了下邊。這裡最簡單的解決辦法是:
<nav>
導航
</nav><article>
内容
</article><aside>
第三欄
</aside>
複制
其他解決方案可以參考這篇文章 Fighting the Space Between Inline Block Elements
其他布局方式
初次之外,css 還提供了
column
、
flexbox
等布局方式,這些以後有機會再介紹吧。
總結
這篇文章我們介紹了用浮動的有寬度的元素來建立多欄布局、如何讓固定布局在頁面上居中以及讓它們在一定範圍内可以伸縮。同時也了解了如何使用内部div在浮動元素中生成間距,而又不會改變布局的總寬度。
參考連結
- CSS入門指南-2:盒子模型、浮動和清除
- CSS入門指南-3:定位元素
- 學習CSS布局
- Fighting the Space Between Inline Block Elements
- Flex 布局教程:文法篇
最後,感謝女朋友支援。