[Layout] Web Design Part 10

由於我沒有娘家可以回,所以大年初二在家寫寫部落格也是很合理的。當然也因為這樣,省下不少紅包錢買來的紅包袋只用了兩個,實在有點太說不過去了。

小的時候很期待回去外婆家,長大了,外婆家原本的地址已不復在。然而,我卻有種近鄉情卻的憂愁。


SELECTOR

上一回聊到了一些選擇器,雖然我好像有很多都沒有交待到,但是,基本上選擇器的用法都如出一轍。雖然說 w3c 提供了那麼多的選擇器,但是,由於瀏覽器支援的程度差異,導致這些東西未必全部都可以使用(IE 表示:)。

我們以一個簡單的例子來說明各種選擇器的使用時機,

<div id="container">

    <header id="header">
        <h1 class="slogan"></h1>
        <h4 class="description"></h4>
    </header>

    <nav id="navigation">
        <ul>
            <li><a href=""></a></li>
            <li><a href=""></a></li>
        </ul>
    </nav>

    <div id="main">
        <article class="entry">
            <h2></h2>
            <p class="categories"></p>
            <p class="content">
            </p>
        </article>
    </div>

    <aside id="sidebar">
        <div class="sidebar-element">
            <h3></h3>
            <div class="content">
            </div>
        </div>
        <div class="sidebar-element">
            <h3></h3>
            <div class="content">
            </div>
        </div>
    </aside>

    <footer id="footer">
        <p></p>
    </footer>

</div>

面對一個切版完成的 HTML 來說,你必須要知道的事情是,

  1. 你的 DOM Tree 結構,是為了要達成視覺設計的結果
  2. 你的 CSS 樣式表,能夠順利操作 DOM 來達到上述目的
  3. 最終輸出的結果可能會因為瀏覽器,而發生各自表述的問題

倘若我們在不考慮第三點的情況下,那你大抵上可以任意使用各種新式的選擇器。

#container {
}

#header {
}

#footer {
}

#aside {
}

#main {
}

大的結構其實很簡單,把 ID 選擇器挑出來就好了。那其他的部份呢,

#header > h1.slogan {
}
#header > h4.description {
}

#main > article.entry {
}

#main > article.entry > h2 {
}

#main > article.entry p {
}

#main > article.entry p.categories {
}

#main > article.entry p.content {
}

#sidebar > div.sidebar-element {
}

#sidebar > div.sidebar-element > h3 {
}

#footer > p {
}

眼尖的你應該發現了,我並沒有特別使用什麼特殊的選擇器。這是習慣性使然,這種事情寫久了,你就會下意識的避開某些,瀏覽器無法實現或是實作有問題的選擇器。這,當然就要歸功於我們偉大的 IE6/7/8 了。

適用時機

選擇器是這樣,沒有人規定你什麼不能用、什麼可以用,這會隨著你的使用習慣而有所不同。不過,雖然樣式表的表述方式因人而異,不過還是會有一些事情是我們不小心會遇上的。

  • 重複的樣式定義
  • 過多選擇器
  • 選擇器權重陷阱
  • DOM Tree 陷阱

雖然網路上有很多文章在教你怎麼寫出,或是避免掉一些 CSS 選擇器的問題,不過,那也是經驗累積而來,沒有真正遇到,你也不會有深刻體驗的。

這裡有篇老文章,但是值得參考:Taming Advanced CSS Selectors

#×重複定義

最早開始寫樣式表的時候,時常會遇到這種情況。這並不是什麼缺點,而是一種會讓你的後續維護,變得相當困難的一件事情。

article.entry p.description {
    color: blue;
}
article.entry p.content {
    color: blue;
}

對於 p.descriptionp.content 來說,顏色雖然都是藍色(blue),但是,其實我們可以換一個方式來作。

<p class="categories text-blue"></p>
<p class="content text-blue">
</p>
.text-blue {
    color: blue;
}

也許你會這麼問,為什麼不直接使用 article.entry p 來定義藍色就好?相信我,當你的 DOM Tree 愈趨複雜時,這會是場惡夢。

<p class="categories text-blue"></p>
<p class="content text-blue">
</p>
<p class="footer-note">
</p>

如果你使用 article.entry p 來定義,那麼 .footer-note 也會跟著變成藍色。你就得多消費一組設定,將顏色改回來。

#×過多選擇器

以往我們在觀察 DOM Tree 的時候,因為我們可能需要定義到相對複雜的子元件上,所以會用了相當多的選擇器,來達到我們所要得結果,

<aside id="sidebar">
    <div class="sidebar-element">
        <h3></h3>
        <div class="content">
            <img class="author-icon" src="" width="" height="" alt="" />
            <p class="content">
            </p>
            <p class="back-to-top">
                <a class="top-link" href=""></a>
            </p>
        </div>
    </div>
</aside>

如果我想要定義 a.top-link 這個標籤,讓他變成灰色(gray),那麼我們會怎麼做?最直覺的作法是,

.top-link {
    color: gray;
}

問題來了,我除了 #sidebar 裡面有這種設定外,我在 #main 裡面也有的話,該怎麼辦?這個時候,我們通常會用選擇器來限制這個項目的適用性。

#sidebar .top-link {
    color: gray;
}

然後我們可能又會發現,我的 #sidebar 裡面,不只有一個地方用上 .top-link,然後我們繼續加上選擇器,最後變成了,

#sidebar .sidebar-element .content p.back-to-top a.top-link {
    color: gray;
}

你算得出來總共用了幾個嗎?答案是 7 個!我們為什麼要避免這種事情?

  • 維護困難
  • 權重陷阱
  • 倘若修改切版,樣式會失效無用
  • 複用性太低

#×權重陷阱

CSS Selectors 有一種優先權(specificity)的設定。

Calculating a selector's specificity

然而為了比較簡易的計算,所以我們會這樣用,

  • 一個 HTML 我們給 1
  • 一個 Selectors 我們給 10
  • 一個 ID Selector 我們給 100
  • 一個行內設定(style="")我們給 1000

那,我們拿剛剛的例子來加加看:

#sidebar .sidebar-element .content p.back-to-top a.top-link {
    color: gray;
}

這個設定有 1 個 ID Selector,4 個類別選擇器,2 個標籤,所以結果就是:100 * 1 + 10 * 4 + 2 * 1 = 142,這樣方便的計算讓你知道優先權值是多少。所以,我們如果要覆蓋上面的灰色的數值,那麼優先權值就必須大於 142!

所以,當我們想要覆蓋這樣的設定時,就必須多加上一組選擇器,但是,這樣的惡性循環下,只會讓你的選擇器越來越複雜。而這樣複雜的樣式表,除了維護上的困難之外,切版的 DOM Tree 如果改變,那麼勢必也是無法使用的。

#×DOM Tree 陷阱

切版輸出的方式,通常直接左右你的樣式表的組成。而,我們為了要因應一些改變,勢必會修改我們的切版輸出。當完整的樣式表設定完成之後,切版輸出的改變,就會干擾到樣式表的套用狀況。

舉個簡單的例子來說,

<div id="container">
    <header id="header">
        <h1 class="slogan"></h1>
        <h4 class="description">
            <p class="website-memo"></p>
            <p></p>
        </h4>
    </header>
</div>
#header > .slogan {
}
#header > .description {
}
.description > p {
}
.description > .website-memo {
}

如果不巧我們的切版修改了,

<div id="container">
    <header id="header">
        <h1 class="slogan"></h1>
        <h4 class="description">
        </h4>
    </header>
</div>

那麼,原先套用在 .description 底下的兩組 .website-memop 就會相繼失效。當然,我們熟知修改切版的情況,勢必要對樣式表有所更動,但,問題是,如果樣式表有如上述例子般的簡易,那麼修改上是不會有什麼問題的。

但是倘若,

<aside id="sidebar">
    <div class="sidebar-element">
        <h3></h3>
        <div class="content">
            <img class="author-icon" src="" width="" height="" alt="" />
            <p class="content">
            </p>
            <p class="back-to-top">
                <a class="top-link" href=""></a>
            </p>
        </div>
    </div>
</aside>
#sidebar .sidebar-element .content p.back-to-top a.top-link {
    color: gray;
}
#sidebar .sidebar-element p.content + p.back-to-top {
    background-color: white;
}

如果我們修改了切版,把 <p class="back-to-top"></p> 這個區塊移除了,

<aside id="sidebar">
    <div class="sidebar-element">
        <h3></h3>
        <div class="content">
            <img class="author-icon" src="" width="" height="" alt="" />
            <p class="content">
            </p>
            <a class="top-link" href=""></a>
        </div>
    </div>
</aside>

這樣你知道樣式表設定上的嚴重性了嗎?

適用而選

選擇器的宗旨,並不是照著切版的 DOM Tree 來選擇,而應該是適用而選。我們從切版了解我們的目的,並從選擇器當中,挑選適切的方式,來呈現出我們所想要的結果。這樣才是在這麼多選擇器當中,使用你所想要的唯一的方式。

當然,要解決上述這些疑難雜症,也不是沒有方法。我的經驗告訴我,可以參考,但活用在你,

  • 多重選擇器,倒不如用 ID SELECTOR
  • 多重選擇器,但無法使用 ID SELECTOR,那就利用多類別來套用
  • 重複性質高的屬性,用單一類別選擇器定義,用多類別來套用
  • DOM Tree 依賴性高的選擇器,擇一而用,人多必嘴雜
  • 分類你的樣式表,將同區塊、同功能的東西放在一起

不要再說你不懂選擇器了,他很易懂,只是我們太複雜。

Hina Chen
偏執與強迫症的患者,算不上是無可救藥,只是我已經遇上我的良醫了。
Taipei