2010/11/16

[入門] HTML 表格全解析 1/2

在網頁設計的領域中, 我覺得 HTML 表格大概是最受人誤用/誤解的網頁技術了。一方面, 我們時常可以看到在某些網頁上有數都數不盡的、層層套疊的 <tr> <td> 元素, 另一方面, 也時常聽到有人極力主張禁用 table 元素。這到底是怎麼一回事呢?

老狗學不了新把戲?

無可諱言的, 或許在 HTML 剛受到世人重視的時代 (恰巧也是 CSS 尚未出現的時代), HTML 表格 (以及後來漸漸不流行的 Frames 元素) 是網頁組版的重要工具。在那個時代, 使用表格來組版或許是最好, 也是唯一的方法。但是隨著技術的演進, 當 CSS 慢慢為大家所接受, 甚至已經成了用來表現網頁外觀的主要方法之際, 很多人依舊仰賴他/她所熟悉的、過時的表格, 來做組版的工作。

同樣的事情, 也發生在文字處理程式的演進歷史。幾乎自從有 PC 開始, 文字編輯程式 (Text Editor) 就開始存在。但是隨後文書處理程式 (Word Processor) 的出現, 許多人還是忘不了在文字編輯程式時代的習慣。其中最讓人受不了的, 就是使用空格來對齊文字這件事情。照理說, 如果你使用的是文書處理程式 (例如 Microsoft Word), 你應該使用尺規 (Ruler) 來設定首行縮排 (indent) 或者跳格 (tab) 以讓文字對齊。同樣的, 我相信到現在還是有人看過自己的親戚或同事在使用強迫斷行的方法在處理段落, 而不是透過尺規讓文字在段落中自動流動 (flow)。

幫 Table 申寃

我聽過某位極力為自己濫用表格組版而辯解的網頁設計師說, 他之所以這麼喜愛表格, 是因為他想用來「保護」自己的網頁, 如此才不會隨便被別人複製。老實說, 這實在是我所聽過最荒唐的事情了。如果你讚同他的說法, 我建議你去看看 [入門][Regex] Regular Expression 詳論 這一篇文章, 那麼你將會了解, 純文字資訊可以輕易的被解析。就算你的表格套了一百層, 結果你還是什麼都防不了。如果你要保護自己的網頁, 有很多其它的方法, 但偏偏裡面就沒有使用表格這一種。

如果毫無節制的使用表格來組版, 你只會造成自己 (呃... 以及接替你的工作的繼任者 - 如果這是你的邪惡目的的話) 不方便而已。我必須很慎重的強調, 表格當然可以拿來組版, 但是表格最好還是被拿來繪製表格。如果你要使用表格來組版, 那麼你最好很謹慎的限制表格的深度, 不要讓層層套疊的表格元素破壞了網頁的精簡。

網頁設計並不只是技術, 它也是藝術。網頁是給人看(或聽)的, 網頁設計者應該同時考量一般人類對其內容的接受程度; 所以網頁從一開始就是以一頁一頁翻閱的方式設計的; 請不要企圖把所有的東西都丟進一頁裡面。我看過很多不懂設計的、自作聰明的人, 很堅持要把想得到的東西都塞在同一頁裡面 (好像他們跟上一頁和下一頁按鈕有仇似的)。如果這種人只是設計給自己看, 那麼請便; 但如果是為眾多閱聽人而設計的, 那麼就請考量大眾的方便性, 而不是自己的。

相對的, 我也聽過很多偏執的人堅持在網頁中不可以出現任何表格, 彷彿是表格跟他/她有不共戴天之仇似的。我覺得過多的表格的確妨礙了網頁的精短和維護性, 但是若因此就要禁用表格, 也未免太過於因噎廢食了。

一定要用表格組版嗎?

HTML 表格有其存在的目的和價值, 不能因為某些人的「感覺」而偏執的瘋狂濫用或絕對的禁止。而如同上面已經說過的, 你應該使用表格在呈現表格狀的資料, 而不是只用來組版。

在某些情況下, 尤其是針對某種版型, 使用表格組版比使用其它方法更能夠表現某種特殊的效果。以下我將舉幾個例子:

範例一: 可以彈性調整大小的標題列

範例一: 可以彈性調整大小的標題列

如上圖所示, 我們想製作一個靠右邊對齊的 Menu Bar, 但我們又希望保留最左邊的空間作為其它用途, 例如跑馬燈, 或是搜尋框等等; 同時, 這個標題列卻又可以隨著瀏覽器視窗的放大/縮小而維持其位置和尺寸。

如果使用者把瀏覽器視窗縮小了, 我們可以看到 Menu Bar 的部份仍然維持在相對的位置:

範例一: 可以彈性調整大小的標題列

在這種情況下, 無論使用者的螢幕大小, 或者他/她如何調整瀏覽器的視窗尺寸, 你的 Menu Bar 將始終維持在畫面的右側。而左側目前標示為 (Empty Space) 的部份, 則會動態的變動其大小。

若使用 HTML 表格來組版, 其程式碼如下:

<style type="text/css">
    #table1
    { width: 100%; }
    #table1 .filled
    { width: 1px; }
</style>

<table id="table1">
    <tr>
        <td>
            (Empty Space)</td>
        <td class="filled">
            <a href="#">Menu #1</a></td>
        <td class="filled">
            <p>Menu #2</p></td>
        <td class="filled">
            <div>Menu #3</div></td>
    </tr>
</table>

在範例中, td 元素裡的文字都必須以其它種元素包住, 如同我在這裡所展示的 a, p, div 等等。如果你希望這些文字在 IE 中不會被斷行的話, 這是我們不得不採取的技巧(我們通常把這種技巧稱為 IE Hack); 而且我們必須為這些元素標注 white-space: nowrap; 這個 CSS 屬性。在 IE 下, 這個屬性在 td 裡的 Inner Text 並不起作用 (我想應該沒有人知道為什麼), 所以必須強迫以其它元素包住(就像我所展示的 a, p, div 等等; 反正就是不能不被什麼東西包住就對了)。所以, 顯而易見地, 你不一定要使用 a, p, div 的順序來把這三個 td 內的文字包住; 我覺得你應該夠聰明到可以看出這一點, 而不需要再來問我為什麼。

除 IE 之外, Google Chrome 會自作聰明的設定 p 元素的 margin-top 和 margin-bottom 的值, 導致這個範例的結果長得跟在 IE 底下不一樣(一整行都會變得特別高)。我跟其他人不一樣, 我不會把所有奇怪的行為都歸罪到微軟的產品, 因此我為此所做的調整, 我把它稱為 Chrome Hack。

為了閱讀方便, 我在這個範例程式中把這些額外的 Hacks 都省略掉了, 所以請無須在上述短短的程式中進行搜尋。在本文的最後面, 我會列出完整的程式碼以供參考。

範例二: 可以彈性調整大小的標題列(不使用表格)

若想達到如範例一的效果, 一定要使用表格嗎? 如果我就是不想使用表格, 該怎麼做?

我們當然可以使用其它方法, 達成類似的結果。程式碼如下:

<style type="text/css">
    #header #leftPane
    { display: inline; }
    #header #rightPane
    { float: right;}
</style>
 

<div id="header">
    <div id="rightPane">
        <a href="#">
            Menu #1</a>
        <a href="#">
            Menu #2</a>
        <a href="#">
            Menu #3</a>
    </div>
    <div id="leftPane">
        (Empty Space)</div>
</div>

在這個範例中, 我們光採用 div 把所要的文字包住, 再套用 CSS 的 float: right; 以及 display: inline; 這兩屬性就可以把事情搞定, 而且也沒有什麼特別的 Hacks 須要處理。唯一需要注意的是, 如範例中的 rightPane 和 leftPane, 這兩個 div 的位置不能調換。如果你把這個 div 的位置對換了, 那麼在 IE 底下, rightPane 會跑到 leftPane 的下方, 這就不是我們所想要的效果了。

範例三: 可以彈性調整大小的網頁主體

接著, 我們來看一個稍為複雜一點的版面:

範例三: 可以彈性調整大小的網頁主體

同樣的, 當使用者調整瀏覽器大小時, 網頁中不同部份必須維持其相對位置:

範例三: 可以彈性調整大小的網頁主體

我們先來看看程式碼:

<style type="text/css">
    #table2
    { width: 100%; vertical-align: top;  }
    #table2 #navigation
    { width: 10%; vertical-align: top;  }
    #table2 #content
    { width: auto; vertical-align: top; }
    #table2 #ads
    { width: 10%; vertical-align: top; }
</style>
<table id="table2">
    <tr>
        <td id="navigation">
            Navigation Bar</td>
        <td id="content">
            <div>
                <h1>Content</h1>
                <h2>Content</h2>
                <p>Content</p>
                <h3>Content</h3>
                <p>Content</p></div></td>
        <td id="ads">
            Ads Bar</td>
    </tr>
</table>

和範例一相同, 我們都是運用表格中對於寬度的不明確定義 (就像本範例中的  10%, auto, 10%) 而達到彈性伸縮版面的效果, 而且不需要依靠 JavaScript 動態的計算實際寬度。

在這個範例中, 左側的 Navigation Bar 和右側的 Ads Bar 看似只有畫面 10% 的寬度, 實際上當我們「塞」東西進入這兩邊以後, 這裡的 10% 空間就會變成類似 HTML5 的 min-width 屬性一樣。假設我們後來把 Navigation Bar 和 Ads Bar 兩者的實際空間撐大為 200px (例如塞入寬度為 200px 的圖), 那麼不管使用者怎麼拉動瀏覽器視窗, 只有 Content 部份會變動大小。假設我們只把 Navigation Bar 的實際空間撐大為 200px, 而把 Content 的實際寬度撐大為固定的 600px, 那麼又會製造出另外一種效果。

我們也可以運用這種組版方式, 但是放棄 Navigation Bar 和 Ads Bar 這兩塊, 讓它們留白, 然後把實際網頁內容填入原本的 Content 部份, 如此就可以製造出網頁置中擺放的效果。

範例四: 可以彈性調整大小的網頁主體(不使用表格)

如範例三, 我們同樣可以不要使用表格:

<style type="text/css">
    #bodyPart #navigation2
    { float: left; width: 10%; }
    #bodyPart #ads2
    { float: right; width: 10%; }
    #bodyPart #content2
    { display: inline-block; }
</style>
<div id="bodyPart">
    <div id="navigation2">
        Navigation Bar</div>
    <div id="ads2">
        Ads Bar</div>
    <div id="content2">
                <h1>Content</h1>
                <h2>Content</h2>
                <p>Content</p>
                <h3>Content</h3>
                <p>Content</p></div>
</div>

使用表格組版有什麼特別的好處嗎?

從以上四個範例程式中, 我們可以看出, 若光從程式的精簡程度而言, 採用不採用表格, 似乎並沒什麼差距。如果你把版面設計得太複雜, 那麼層層套疊的 div 和層層套疊的表格元素, 似乎也沒什麼不同。

但是表格有個先天上的優勢, 那就是表格中每一個 Cell 都不可能重疊。當使用者把瀏覽器畫面縮小到某一程度之後, 採用表格的版面仍然可以閱讀, 但是其它方法就會導致破版了:

採用與不採用表格的差異

在上圖中, 分別是範例一、二、三、四的結果, 我們可以看到, 當我們把版本擠壓到極限程度時, 採用表格的做法似乎還能維持其優雅的形狀, 但其它做法就會漏出破綻了。

不過話說回來, 如果你的版面設計不像如以上範例那麼複雜的話, 你真的不需要使用表格來組版; 使用簡單的 div 並配合 CSS 就非常足夠了。

在下一篇文章中, 我們將讓表格元素回歸到表現表格狀資訊的本業, 並重頭把 HTML 所提供的表格元素講解一遍。

以下則是範例一到四的完整程式碼, 把它儲存為一個 HTML 檔案即可:

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
 "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">

<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en">
<head>
    <title>[入門] HTML 表格全解析</title>
    <style type="text/css">
        td
        {
            border: 1px solid black;
            background-color: Navy;
            color: Gold;
        }
        #table1
        {
            width: 100%;
            border: 1px solid black;
        }
        #table1 td p
        {
        /* Hack for Chrome - there are default margin-top and margin-bottom values for p element in Chrome */
            margin-top: 0px;
            margin-bottom: 0px;
        }
        #table1 .filled
        {
            width: 1px;
            text-align: center;
        }
        #table1 .filled p, div, a
        /* Hack for IE - the "nowrap" attribute won't work on IE in the td element; you must enclose the content in an a, p or div, etc. element */
        {
            white-space: nowrap;
        }
       
        td, span, div, p, a
        {
            border: 1px solid #aaa;
        }
       
        #header
        {
            background-color: Navy;
            color: Gold;
            padding: 5px;
        }
        #header #leftPane
        {
            display: inline;
        }
        #header #rightPane
        {
            float: right;
        }
               
    #table2
    {
        width: 100%;
        border: 1px solid black;
        vertical-align: top; 
    }
    #table2 #navigation
    {
        width: 10%;
        vertical-align: top;  
    }
    #table2 #content
    {
        width: auto;
        vertical-align: top;           
    }
    #table2 #ads
    {
        width: 10%;
        vertical-align: top;           
    }

    #bodyPart
    {
        background-color: Navy;
        color: Gold;
        padding: 5px;
    }
    #bodyPart #navigation2
    {
        float: left;
        width: 10%;
    }
    #bodyPart #ads2
    {
        float: right;
        width: 10%;
    }
    #bodyPart #content2
    {
        display: inline-block;
    }
</style>

</head>
<body>

    The following example demostrates an elapstic layout using table elements:
    <table id="table1">
        <tr>
            <td>
                (Empty Space)
            </td>
            <td class="filled">
                <a href="#">Menu #1</a>
            </td>
            <td class="filled">
                <p>Menu #2</p>
            </td>
            <td class="filled">
                <div>Menu #3</div>
            </td>
        </tr>
    </table>

    <br />

    The following example demostrates an elapstic layout using div and spans:
    <div id="header">
        <div id="rightPane">
            <a href="#">
                Menu #1</a>
            <a href="#">
                Menu #2</a>
            <a href="#">
                Menu #3</a>
        </div>
        <div id="leftPane">
            (Empty Space)</div>
    </div>

    <br />

    The following example demostrates a more complex elapstic layout using table elements:
    <table id="table2">
        <tr>
            <td id="navigation">
                Navigation Bar
            </td>
            <td id="content">
                <div>
                    <h1>Content</h1>
                    <h2>Content</h2>
                    <p>Content</p>
                    <h3>Content</h3>
                    <p>Content</p>
                </div>
            </td>
            <td id="ads">
                Ads Bar
            </td>
        </tr>
    </table>

    <br />

    The following example demostrates a more complex elapstic layout using div and spans:
    <div id="bodyPart">
        <div id="navigation2">
            Navigation Bar
        </div>
        <div id="ads2">
            Ads Bar</div>
        <div id="content2">
                    <h1>Content</h1>
                    <h2>Content</h2>
                    <p>Content</p>
                    <h3>Content</h3>
                    <p>Content</p>
        </div>
    </div>
</body>
</html>

>>  [入門] HTML 表格全解析 2/2

沒有留言:

張貼留言