每當瀏覽器要顯示一份文件時,它得先為文件內容穿上樣式,這會歷經許多程序,我們已經列在下方了。記得喔,這只是非常簡化的版本,不同的瀏覽器會有自己的做法,不過原則上就是這樣。
DOM 有著一個樹狀結構,每個標記語言中的元素、屬性,以及文字片段都會是這個樹狀結構裡的 節點 。每個節點與其他節點間的關係都有定義:若節點有子節點(child),則自己是他們的父節點(parent);若子節點為複數,則這些子節點稱彼此為兄弟/姊妹節點(sibling)。
了解 DOM 對於設計、除錯以及維護 CSS 有相當大的助益,因為 DOM 正是 CSS 與文件內容的交會之處。當你要利用瀏覽器的開發者工具(DevTools)來查看元素套用的規則時,你就會見到它們。
我們就別絮絮叨叨了,直接看個簡單的例子,來瞭解 HTML 片段是如何轉換成 DOM 的吧。
以下列 HTML 原始碼為例:
<p> Let's use: <span>Cascading</span> <span>Style</span> <span>Sheets</span> 在 DOM 中,<p> 元素對應到的節點是一個父節點,它的子節點有一個純文字節點以及三個 <span> 元素節點,而 SPAN 節點也是有著純文字子節點的父節點: ├─ "Let's use:" ├─ SPAN | └─ "Cascading" ├─ SPAN | └─ "Style" └─ SPAN └─ "Sheets" 這就是瀏覽器如何解析上段的 HTML 片段的 — 它轉譯了以上的 DOM 樹,並產生了以下的輸出: margin: 0;
在 DOM 中,<p> 元素對應到的節點是一個父節點,它的子節點有一個純文字節點以及三個 <span> 元素節點,而 SPAN 節點也是有著純文字子節點的父節點:
<p>
<span>
SPAN
這就是瀏覽器如何解析上段的 HTML 片段的 — 它轉譯了以上的 DOM 樹,並產生了以下的輸出:
讓我們在上例中加入一些 CSS 來增添樣式。同樣地,HTML 如下:
<p> Let's use: <span>Cascading</span> <span>Style</span> <span>Sheets</span> 假設我們把以下 CSS 套用上去: cssspan { border: 1px solid black; background-color: lime; 瀏覽器會先解析 HTML 並產生 DOM 樹,然後再解析 CSS。因為這個 CSS 中只有使用 span 選擇器,所以瀏覽器可以很快地完成分類!接著它會將這個規則套用到每一個 <span> 上,並在螢幕上繪製出最終的畫面。 現在輸出變成這樣: 在下個主題裡的為 CSS 除錯中我們將會使用瀏覽器的 DevTools 來為 CSS 除錯,屆時我們將會學到更多瀏覽器解析 CSS 的方法。
假設我們把以下 CSS 套用上去:
span { border: 1px solid black; background-color: lime; 瀏覽器會先解析 HTML 並產生 DOM 樹,然後再解析 CSS。因為這個 CSS 中只有使用 span 選擇器,所以瀏覽器可以很快地完成分類!接著它會將這個規則套用到每一個 <span> 上,並在螢幕上繪製出最終的畫面。 現在輸出變成這樣: 在下個主題裡的為 CSS 除錯中我們將會使用瀏覽器的 DevTools 來為 CSS 除錯,屆時我們將會學到更多瀏覽器解析 CSS 的方法。
瀏覽器會先解析 HTML 並產生 DOM 樹,然後再解析 CSS。因為這個 CSS 中只有使用 span 選擇器,所以瀏覽器可以很快地完成分類!接著它會將這個規則套用到每一個 <span> 上,並在螢幕上繪製出最終的畫面。
span
現在輸出變成這樣:
在下個主題裡的為 CSS 除錯中我們將會使用瀏覽器的 DevTools 來為 CSS 除錯,屆時我們將會學到更多瀏覽器解析 CSS 的方法。
瀏覽器遇到不認識的 CSS 時會發生什麼事?
在先前的課程中,我們曾提過瀏覽器並不會一次實作全部的新 CSS。此外,很多人都不是使用最新版的瀏覽器。要知道 CSS 是與時俱進的,會超出瀏覽器可辨認的範圍是很正常的事,所以啦,你可能會很好奇,當瀏覽器遇到它看不懂的 CSS 選擇器或宣告時會發生什麼事呢?
答案就是裝作沒看到,繼續往下解析其它的 CSS! 如果瀏覽器在解析規則時,遇到它不認識的屬性或值,它會忽略它,並繼續解析下一個宣告。因此當它這麼做的時候,如果不是你拼錯字了,那就是那個屬性或值太新奇了,所以你的瀏覽器還沒有支援它。 同樣地,如果瀏覽器遇到一個它不認識的選擇器時,它會忽略整條規則,並繼續解析其他規則。 下面的例子使用英式英語來拼寫 color (也就是 colour),進而導致該屬性失效,因為現在瀏覽器看不懂它了。也因此下面的段落無法以藍字顯示,不過其他的 CSS 還是成功地套用上去了,只有無效的會被忽略掉。 html <p>I want this text to be large, bold and blue.</p> cssp { font-weight: bold; colour: blue; /* incorrect spelling of the color property */ font-size: 200%; 這樣做有個很大的好處,就是你可以放心地利用新 CSS 做出很炫炮的效果,而不用擔心瀏覽器不支援時會出錯 — 反正差別只在於那個新特性有或沒有而已。再加上 CSS 層疊 (cascade) 的天性,只要你提供兩條具有相同具體程度(specificity)的規則,就能讓不支援的瀏覽器套用另一條規則。 這在想要使用某個剛推出的值,但它還未普及時非常有用。舉個例子,一些老舊的瀏覽器不支援以 calc() 來當作值,所以當我想要用它來決定寬的時候,可能會先寫一個備用的寬(以像素為單位的值),然後再寫一個值為 calc(100% - 50px) 的寬。這樣一來,老舊的瀏覽器會使用像素版本 ,並忽略 calc() 版本,因為它們看不懂這個;而新的瀏覽器則會先解析像素版本,然後再將 calc() 版本覆寫上去,因為它比較晚出現。 css.box {
答案就是裝作沒看到,繼續往下解析其它的 CSS!
如果瀏覽器在解析規則時,遇到它不認識的屬性或值,它會忽略它,並繼續解析下一個宣告。因此當它這麼做的時候,如果不是你拼錯字了,那就是那個屬性或值太新奇了,所以你的瀏覽器還沒有支援它。
同樣地,如果瀏覽器遇到一個它不認識的選擇器時,它會忽略整條規則,並繼續解析其他規則。
下面的例子使用英式英語來拼寫 color (也就是 colour),進而導致該屬性失效,因為現在瀏覽器看不懂它了。也因此下面的段落無法以藍字顯示,不過其他的 CSS 還是成功地套用上去了,只有無效的會被忽略掉。
<p>I want this text to be large, bold and blue.</p> cssp { font-weight: bold; colour: blue; /* incorrect spelling of the color property */ font-size: 200%; 這樣做有個很大的好處,就是你可以放心地利用新 CSS 做出很炫炮的效果,而不用擔心瀏覽器不支援時會出錯 — 反正差別只在於那個新特性有或沒有而已。再加上 CSS 層疊 (cascade) 的天性,只要你提供兩條具有相同具體程度(specificity)的規則,就能讓不支援的瀏覽器套用另一條規則。 這在想要使用某個剛推出的值,但它還未普及時非常有用。舉個例子,一些老舊的瀏覽器不支援以 calc() 來當作值,所以當我想要用它來決定寬的時候,可能會先寫一個備用的寬(以像素為單位的值),然後再寫一個值為 calc(100% - 50px) 的寬。這樣一來,老舊的瀏覽器會使用像素版本 ,並忽略 calc() 版本,因為它們看不懂這個;而新的瀏覽器則會先解析像素版本,然後再將 calc() 版本覆寫上去,因為它比較晚出現。 css.box {
p { font-weight: bold; colour: blue; /* incorrect spelling of the color property */ font-size: 200%; 這樣做有個很大的好處,就是你可以放心地利用新 CSS 做出很炫炮的效果,而不用擔心瀏覽器不支援時會出錯 — 反正差別只在於那個新特性有或沒有而已。再加上 CSS 層疊 (cascade) 的天性,只要你提供兩條具有相同具體程度(specificity)的規則,就能讓不支援的瀏覽器套用另一條規則。 這在想要使用某個剛推出的值,但它還未普及時非常有用。舉個例子,一些老舊的瀏覽器不支援以 calc() 來當作值,所以當我想要用它來決定寬的時候,可能會先寫一個備用的寬(以像素為單位的值),然後再寫一個值為 calc(100% - 50px) 的寬。這樣一來,老舊的瀏覽器會使用像素版本 ,並忽略 calc() 版本,因為它們看不懂這個;而新的瀏覽器則會先解析像素版本,然後再將 calc() 版本覆寫上去,因為它比較晚出現。 css.box {
這樣做有個很大的好處,就是你可以放心地利用新 CSS 做出很炫炮的效果,而不用擔心瀏覽器不支援時會出錯 — 反正差別只在於那個新特性有或沒有而已。再加上 CSS 層疊 (cascade) 的天性,只要你提供兩條具有相同具體程度(specificity)的規則,就能讓不支援的瀏覽器套用另一條規則。
這在想要使用某個剛推出的值,但它還未普及時非常有用。舉個例子,一些老舊的瀏覽器不支援以 calc() 來當作值,所以當我想要用它來決定寬的時候,可能會先寫一個備用的寬(以像素為單位的值),然後再寫一個值為 calc(100% - 50px) 的寬。這樣一來,老舊的瀏覽器會使用像素版本 ,並忽略 calc() 版本,因為它們看不懂這個;而新的瀏覽器則會先解析像素版本,然後再將 calc() 版本覆寫上去,因為它比較晚出現。
calc()
calc(100% - 50px)
.box {