CSS入門指南-4:頁面佈局

goodspeed發表於2019-03-03

這是《CSS設計指南》的讀書筆記,用於加深學習效果。

display 屬性

display是 CSS 中最重要的用於控制佈局的屬性。每個元素都有一個預設的 display 值。對於大多數元素它們的預設值通常是 block 或 inline 。一個 block 元素通常被叫做塊級元素。一個 inline 元素通常被叫做行內元素。

block

div 是一個標準的塊級元素。一個塊級元素會新開始一行並且儘可能撐滿容器。其他常用的塊級元素包括 pform 和HTML5中的新元素: headerfootersection 等等。

inline

img 是一個標準的行內元素。你可以把兩個 <img> 標籤寫在兩行,但這並不影響圖片再瀏覽器中的顯示效果,它們會並列出現在一行上。而且標籤直接的空白(標記中的兩個CSS入門指南-4:頁面佈局標籤雖然分別位於兩行,但這並不影響圖片在瀏覽器中顯示時的效果。圖片是行內元素,所以它們顯示的時候就會並列出現在一行上。而且,標籤之間的空白(包括製表、回車和空格)都會被瀏覽器忽略。

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 增加內邊距後的效果圖
article 增加內邊距後的效果圖

由於增加了內邊距導致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;
}複製程式碼

使用 box-sizing: border-box; 的效果圖
使用 box-sizing: border-box; 的效果圖

這是目前為止最好的解決方法了,那最簡單有效的方法就是在 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 還提供了 columnflexbox等佈局方式,這些以後有機會再介紹吧。

總結

這篇文章我們介紹了用浮動的有寬度的元素來建立多欄佈局、如何讓固定佈局在頁面上居中以及讓它們在一定範圍內可以伸縮。同時也瞭解瞭如何使用內部div在浮動元素中生成間距,而又不會改變佈局的總寬度。

參考連結


最後,感謝女朋友支援。

歡迎關注(April_Louisa) 請我喝芬達
歡迎關注
歡迎關注
請我喝芬達
請我喝芬達

相關文章