CSS設計指南(頁面布局)

布局的基本概念


多欄布局有三種基本的實現(xiàn)方案:固定寬度、流動、彈性。

  • 固定寬度布局的大小不會隨用戶調(diào)整瀏覽器窗口大小而變化,一般是900 到1100像素寬。其中960 像素是最常見的,因為這個寬度適合所有現(xiàn)代顯示器,而且能夠被16、12、10、8、6、5、4 和3 整除,不僅容易計算等寬分欄的數(shù)量,而且計算結果也能得到?jīng)]有小數(shù)的像素數(shù)。流行的CSS 布局框架960 Grid(http://www.960.gs),就是基于960 像素寬的網(wǎng)格創(chuàng)建的。
  • 流動布局的大小會隨用戶調(diào)整瀏覽器窗口大小而變化。這種布局能夠更好地適應大屏幕,但同時也意味著放棄對頁面某些方面的控制,比如隨著頁面寬度變化,文本行的長度和頁面元素之間的位置關系都可能變化。Amazon.com 的頁面采用的就是流動中欄布局,在各欄寬度加大時通過為內(nèi)容元素周圍添加空白來保持內(nèi)容居中,而且現(xiàn)在的導航條會在布局變窄到某個寬度時收縮進一個下拉菜單中,從而為內(nèi)容騰出空間。今天,越來越多的瀏覽器都支持CSS 媒體查詢了。這就讓基于瀏覽器窗口寬度提供不同的CSS 樣式成為可能。在這種形勢下,適應各種屏幕寬度的可變固定布局,正逐步取代流動布局。這種可變的固定布局能夠適應最大和最小的屏幕,業(yè)界稱之為響應式設計。
  • 彈性布局與流動布局類似,在瀏覽器窗口變寬時,不僅布局變寬,而且所有內(nèi)容元素的大小也會變化,讓人產(chǎn)生一種所有東西都變大了的感覺。到目前為止,我還沒見過設計得非常好的彈性布局,主要是因為它太過復雜了。
布局高度與布局寬度

在實際地創(chuàng)建頁面布局之前,我想先說說應該怎么看待布局的高度和寬度,因為這兩者的控制方法實在太不一樣了。

布局高度
多數(shù)情況下,布局中結構化元素(乃至任何元素)的高度是不必設定的。事實上,我甚至想告訴你根本不應該給元素設定高度。除非你確實需要這樣做,比如在頁面中創(chuàng)造一個絕對定位的元素。
為什么正常情況下都應該保持元素height 屬性的默認值auto 不變呢?很簡單,只有這樣元素才能隨自己包含內(nèi)容的增加而在垂直方向上擴展。這樣擴展的元素會把下方的元素向下推,而布局也能隨著內(nèi)容數(shù)量的增減而垂直伸縮。假如你明確設定了元素的高度,那么超出的內(nèi)容要么被剪掉,要么會跑到容器之外——取決于元素overflow 屬性的設定。

布局寬度
與高度不同,我們需要更精細地控制布局寬度,以便隨著瀏覽器窗口寬度的合理變化,布局能夠作出適當?shù)恼{(diào)整,確保文本行不會過長或過短。如果隨意給元素添加內(nèi)邊距、邊框,或者元素本身過大,導致浮動元素的寬度超過包含元素的布局寬度,那浮動元素就可能“躲”到其他元素下方。
當然啦,即使必須設定欄寬,也不要給包含在其中的內(nèi)容元素設定寬度,應該讓這些內(nèi)容元素自動擴展到填滿欄的寬度。本書前面已經(jīng)講過了,這是塊級元素的默認行為。簡言之,就是讓欄寬限制其中內(nèi)容元素的寬度。

三欄-固定寬度布局


<!DOCTYPE html>
<html>

<head>
    <meta http-equiv="Content-Type" content="text/html;charset=UTF-8" />
    <title>HTML5 Template</title>
    <!-- <link rel="stylesheet" href="./css/test.css"> -->
    <style>
        * {
            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;
        }
    </style>
</head>
<body>
    <div id="wrapper">
        <header>
            標題
        </header>
        <nav>
            <ul>
                <li>無序列表1</li>
                <li>無序列表2</li>
                <li>無序列表3</li>
            </ul>
        </nav>
        <article>
            如圖5-2 所示,把兩欄容器元素的總寬度設定為外包裝的寬度(150 + 810 = 960), 并浮動它們,就可以創(chuàng)造出并肩排列的兩欄來。每一欄的長度取決于內(nèi)容多少。采 用同樣的方法,可以添加第三欄(或任意多個欄)。
        </article>
        <aside>
            文本
        </aside>
        <footer>
            footer
        </footer>
    </div>
</body>

</html>
為欄設定內(nèi)邊距和邊框

只要一調(diào)整各欄中的內(nèi)容,布局就可能超過容器寬度,而右邊的欄就可能滑到左邊的欄下方。一般來說,兩種情況下可能會發(fā)生這種問題。

  • 為了讓內(nèi)容與欄邊界空開距離,為欄添加水平外邊距和內(nèi)邊距,或者為了增加欄間距,為欄添加外邊距(只要開始給布局添加樣式,就一定會采用這里說的一種做法,甚至雙管齊下),導致布局寬度增大,進而浮動欄下滑。換句話說,右邊浮動的欄因為沒有足夠的空間與其他欄并列,就會滑到左邊欄的下方。
  • 在欄中添加大圖片,或者沒有空格的長字符串(如長URL),也會導致欄寬超過布局寬度。同樣,這種情況下右邊的欄也會滑到左邊的欄下方。

為固定寬度的元素添加水平外邊距、邊框和內(nèi)邊距,會導致元素盒子變寬

好在,我們也有三種方法來預防該問題發(fā)生。

  • 從設定的元素寬度中減去添加的水平外邊距、邊框和內(nèi)邊距的寬度和。
  • 在容器內(nèi)部的元素上添加內(nèi)邊距或外邊距。
  • 使用CSS3 的box-sizing 屬性切換盒子縮放方式,比如section {box-sizing:border-box;}。 應用box-sizing 屬性后,給section 添加邊框和內(nèi)邊距都不會增大盒子,相反會導致內(nèi)容變窄。

直接給欄應用內(nèi)邊距會導致內(nèi)容變窄,但不會影響布局。聽起來容易
的辦法總會有一個“但是”,這里的“但是”要說的是IE6 和IE7 不支持box-sizing
屬性。不過,有一個專門解決這個問題的膩子腳本(polyfill),名叫borderBoxModel.js。
你可以使用條件注釋(以便只有IE6 和IE7 加載)把它添加到HTML 標記之后、結
束的</body>標簽之前,以保證在加載DOM 之后再執(zhí)行該腳本:

<body>
  <!-- HTML 標記 -->
  <!-- 只讓IE8 之前的IE 加載它 -->
  <!--[if lt IE 8 ]>
  <script src="helpers/borderBoxModel.js"></script>
  <![endif]-->
</body>

然而,一欄之中可能會包含大量不同內(nèi)容的元素。假如將來又決定調(diào)整內(nèi)容與容器邊界的距離,就必須每個元素都要進行調(diào)整,這樣不僅麻煩,而且容易出錯。況且,給欄添加邊框同樣會增大欄寬,不可能通過為其包含的內(nèi)容元素逐個應用樣式來做到。
所以說,與其為容器中的元素添加外邊距,不如在欄中再添加一個沒有寬度的div,讓它包含所有內(nèi)容元素,然后再給這個div 應用邊框和內(nèi)邊距。如此一來,只要為內(nèi)部div 設定一次樣式,就可以把讓所有內(nèi)容元素與欄邊界保持一致的距離。而且,將來再需要調(diào)整時也會很方便。任何新增內(nèi)容元素的寬度都由這個內(nèi)部div 決定。

關于表現(xiàn)性標記的思考
HTML 的目的是語義,也就是給內(nèi)容賦予含義。而CSS 呢,是為了把表現(xiàn)性的樣式分離出來才發(fā)明的。不過,有些表現(xiàn)性標記是有害的,而有些則沒有副作用。使用表格來創(chuàng)建多欄布局,或者使用<br />標簽在段間換行,卻不使用<p>標簽,這種做法的確不值得提倡,因為這會造成內(nèi)容難以移植。比如說吧,用三個表格單元作為三欄,這種布局到哪都會顯示成表格,就算是在完全不合適的智能手機里也一樣。如果表現(xiàn)性標記無法用CSS 修改,或者在CSS 不可用時也要迫使用戶接受,那就是濫用HTML。可是,div 或span 這種中性的元素,對默認樣式?jīng)]有影響,除非你給它們應用樣式,否則它們就跟不存在一樣。所以,我認為添加這種元素達到表現(xiàn)性的目的是完全可以接受的。

子-星選擇符
所謂“子-星選擇符”就是一個組合選擇符,利用它可以不使用內(nèi)部div 就能設定一欄中所有元素的外邊距。
星號選擇符可以選擇“所有元素”,故而,在一個選擇符后面加個星號,比如someSelector *就可以選擇someSelector 所代表元素的所有后代元素。子選擇符可以選擇“某元素的子元素”,故而,把子選擇符放到星號前面,比如someSelector > *就會只選擇someSelector所代表元素的所有子元素,而非后代元素。這正好適用于選擇容器內(nèi)部的所有頂部元素,然后設定它們的外邊距。比如,對于section 欄,設定section > * {margin:0 10px;},就能為欄中所有子元素,不包括其他后代元素,各應用10 像素的左、右外邊距。 使用“子-星選擇符”要注意兩點。
第一,在為子元素設定垂直外邊距時,只能使用margin-top 和margin-bottom,不能使用簡寫的margin,否則會抵消用“子-星選擇符”應用給這些元素的水平外邊距。如果你想進一步縮進某個子元素的內(nèi)容,就應該給該子元素應用內(nèi)邊距。
第二,“子-星選擇符”有潛在性能問題,因為它會導致瀏覽器遍歷整個DOM 結構去查找所有匹配的元素。但我也發(fā)現(xiàn)這一點性能影響幾乎可以忽略不計。假如你的頁面真的包含幾千上萬個元素,那倒確實該考慮用ySlow 或其他性能度量工具測一測這個選擇符的影響。

預防過大的元素
設計一個將來可能由他人維護的動態(tài)網(wǎng)站時,需要考慮得更長遠一些。比如,應該預見到可能出現(xiàn)一些過大的元素。如果將來有一張比浮動欄更寬的圖片被放到欄中,就會導致布局變寬,而右邊的欄又會滑到下方。為此, 一個簡單的預防措施就是添加一條.inner img{max-width:100%;}聲明,以便限制圖片的寬度不超過其父元素(在此就是內(nèi)部div)。
另一個辦法是給每個欄(或者內(nèi)部div,如果你用了的話)添加overflow:hidden 聲明。這條聲明不會縮小圖片以適應父元素,而會將它(以及任何過大元素)超出容器邊界的部分剪切掉。動態(tài)網(wǎng)站中另一個潛在的問題是換行。HTML 只會在單詞間空格的地方換行。一些長URL,甚至一些長單詞,在欄比較窄的情況下,都會導致欄寬過大。因此,還應該給所有欄的外包裝元素應用word-wrap:break-word 聲明,以便所有欄及其內(nèi)容繼承這個設定。有了這條聲明,瀏覽器會把過長的詞斷開顯示在不同行上。只是word-wrap 沒有定義在哪里斷開,因此結果完全是隨機的,而且沒有連字符。不過,這條規(guī)則只在需要時才會起作用,而且能保護布局不會被長URL 頂?shù)弥щx破碎。建議你在每一欄中都用長URL、大圖片,以及包含內(nèi)容過多的元素測試一下布局,看看這些聲明到底會不會起作用,并發(fā)現(xiàn)更多需要事先考慮保護措施的其他漏洞。

三欄-中欄流動布局


實現(xiàn)中欄流動布局有兩種方法。一種是在中欄改變大小時使用負外邊距定位右欄,另一種是使用CSS3 讓欄容器具有類似表格單元的行為。負外邊距適合比較老的瀏覽器,而CSS 的table 屬性則要簡單得多。

用負外邊距實現(xiàn)

<!DOCTYPE html>
<html>
<head>
    <meta http-equiv="Content-Type" content="text/html;charset=UTF-8" />
    <title>HTML5 Template</title>
    <style>
        * {
            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%;
            /*把右欄拉到區(qū)塊外邊距騰出的位置上*/
            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;
        }
    </style>
</head>

<body>
    <div id="main_wrapper">
        <header>
            <!-- 頁眉-->
        </header>
        <!-- /*三欄外包裝(包圍全部三欄)*/ -->
        <div id="threecolwrap">
            <!-- /*兩欄外包裝(包圍左欄和中欄)*/ /*左欄*/ -->
            <div id="twocolwrap">
                <nav>
                    <!-- 導航 -->
                </nav>
                <!-- /*中欄*/ -->
                <article>
                    <!-- 區(qū)塊 -->
                </article>
                <!-- /*結束兩欄外包裝(twocolwrap)*/ /*右欄*/ -->
            </div>
            <aside>
                <!-- 側欄 -->
            </aside>
            <!-- /*結束三欄外包裝(threecolwrap)*/ -->
        </div>
        <footer>
            <!-- 頁腳 -->
        </footer>
    </div>
</body>
</html>

用CSS3 單元格實現(xiàn)

盡管利用HTML 的<table>標簽實現(xiàn)多欄布局是難以接受的,但使用CSS 讓布局形如表格則是絕對可以接受的。這種方法不會導致固定不變的表格布局,也不會出現(xiàn)難以重新應用樣式的問題(比如在手持設備上表現(xiàn)為一欄)。
我們知道,CSS 可以把一個HTML 元素的display 屬性設定為table、table-row 和table-cell。通過這種方法可以模擬相應HTML 元素的行為。而通過CSS 把布局中的欄設定為table-cell 有三個好處。

  • 單元格(table-cell)不需要浮動就可以并排顯示,而且直接為它們應用內(nèi)邊距也不會破壞布局。
  • 默認情況下,一行中的所有單元格高度相同,因而也不需要人造的等高欄效果了。
  • 任何沒有明確設定寬度的欄都是流動的。

CSS3 表格行為在IE7 及更低版本中并沒有得到支持,而且也沒有穩(wěn)妥的補救措施。如果你(或者你的客戶)愿意摒棄IE7,那么它就是一個既簡單又可靠,而且還很徹底的解決方案。

<!DOCTYPE html>
<html>
<head>
    <meta http-equiv="Content-Type" content="text/html;charset=UTF-8" />
    <title>HTML5 Template</title>
    <style>
        nav {
            display: table-cell;
            width: 150px;
            padding: 10px;
            background: #dcd9c0;
        }

        article {
            display: table-cell;
            padding: 10px 20px;
            background: #ffed53;
        }

        aside {
            display: table-cell;
            width: 210px;
            padding: 10px;
            background: #3f7ccf;
        }
    </style>
</head>

<body>
    <nav>
        <!-- 內(nèi)容 -->
    </nav>
    <article>
        <!-- 內(nèi)容 -->
        請注意,這個簡單、功能完備的布局對IE7 和IE6 可沒有任何膩子腳本,甚至連個退
化的后備方案都沒有。在這些瀏覽器中,三欄會上下堆疊在一起。因此,除非你下
定決心不再支持老版本的IE,否則就得使用本章前面講過的其他布局技術。等吧,
等到這些瀏覽器沒人用為止。
    </article>
    <aside>
        <!-- 內(nèi)容 -->
    </aside>
</body>
</html>

多行多欄布局


CSS選擇符的實際應用

隨著頁面變得越來越復雜,相同的HTML 元素(如section、article、nav,等等)會出現(xiàn)很多次——比如,前面布局中的article 就出現(xiàn)了7 次。為了選擇某個元素,必須區(qū)分這些相同的標簽名。為此,有些新手會給每個標簽都添加一個不同的類名。但這種做法是不值得提倡的。不僅因為類本身就不該這么用(類應該用于標記具有相同特征的元素),而且這么多類會把標記弄得很亂,讓CSS 也很難看懂。為了知道每個類代表哪個元素,你必須不斷地查看HTML。
更好的做法是給標記中每個主要區(qū)域的頂級元素添加一個ID,這也是使用ID 的正確方式,ID 就是標識頁面中唯一元素用的。然后,這些ID 就會成為HTML 標記中的
“路標”,放在上下文選擇符開頭的時候,它們就能起到框定后代元素的作用。這就是在標記中保持類和ID 屬性最少的秘訣。而且,相應的上下文選擇符也能清晰地傳達出路徑信息,讓人從CSS 中一眼就能看出它要選擇哪個元素。

 <div id="wrapper">
        <header>
            <h1>Full-width content</h1>
        </header>
        <nav>
            <p>Navigation menus go here</p>
        </nav>
        <section id="branding">
            <img src="images/grand_canyon.jpg" alt="Grand Canyon" />
        </section>
        <!-- branding 結束 -->
        <section id="feature_area">
            <article>
                <div class="inner">
                    <p>Lorem Ipsum text</p>
                </div>
            </article>
            <!-- 省略另外兩個 article 元素 -->
        </section>
        <!-- feature_area 結束-->
        <section id="promo_area">
            <article>
                <div class="inner">
                    <p>Lorem Ipsum text</p>
                </div>
            </article>
            <!-- 省略另外三個 article 元素 -->
        </section>
        <!-- promo_area 結束-->
        <footer>
        </footer>
    </div>
?著作權歸作者所有,轉載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內(nèi)容

  • 問答題47 /72 常見瀏覽器兼容性問題與解決方案? 參考答案 (1)瀏覽器兼容問題一:不同瀏覽器的標簽默認的外補...
    _Yfling閱讀 14,160評論 1 92
  • 閱讀CSS設計指南前5章節(jié)時所做的筆記,有些為摘錄書本中的原句,有些為自己的想法,如果有什么錯誤的地方還請大家指教...
    CodingEleven閱讀 403評論 1 2
  • 理解盒模型每一個元素都會在頁面上生成一個盒子。因此,HTML頁面實際上就是由一堆盒子組成的。先從每個元素盒子的屬性...
    xpwei閱讀 580評論 0 0
  • CSS 是什么 css(Cascading Style Sheets),層疊樣式表,選擇器{屬性:值;屬性:值}h...
    崔敏嫣閱讀 1,577評論 0 5
  • 姑娘們都喜歡拍婚紗照,想起當年我自己拍婚照的時候,溜溜整了一天,中午飯都是帶著厚厚的粉一邊吃,一邊生怕臉上的粉掛不...
    花團1220閱讀 350評論 0 1

友情鏈接更多精彩內(nèi)容