最近半年,我一直都沒在知乎上遇到好的前端問題,而這個問題,問到我心坎上了。
在過去一年的教學過程中,不斷有學生嘗試理性地理解 CSS,都以失敗告終。
我告訴他們 CSS 是沒有邏輯可言的!他們不信。
這個問答,能很好闡釋我的理解。以下是我的回答。
短答案:因為 CSS 不正交。
長答案:
我先來解釋一下什麼是正交。
你調過顯示器的「亮度」、「色調」和「飽和度」吧。
- 「亮度」就是明暗程度,值越大,螢幕越亮。
- 「色調」就是顏色,你通過調色調,可以把紅色調成綠色。
- 「飽和度」就是鮮豔程度,值越大越鮮豔。
「正交」就是,你調節這三者中的一個時,不影響其他兩個效果。
- 你調節「亮度」的時候「色調」和「飽和度」不會變化。
- 你調節「色調」的時候「亮度」和「飽和度」不會變化。
- 你調節「飽和度」的時候「亮度」和「色調」不會變化。
「正交」看起來應該是理所當然的,對吧。
想象一下「不正交」的情況:你調節「亮度」的時候,「色調」和「飽和度」會跟著無規律的變化。如果是這樣,你會調得想死,因為你很難調到你想要的效果。
而 CSS,就是「不正交」的。
我以 width 對 margin-left 的影響為例,假設有如下程式碼:
.parent 裡面的 .child 寬度為 300px,現在我給他新增一個 margin-left: -10px,會發現
整個 .child 左移 10 畫素
好的,於是我們知道 margin-left: -10px 會讓元素整體左移。
真的是這樣嗎?
這個時候把 width 去掉重新做實驗,這是在新增 margin-left 之前:
這是在新增 margin-left 之後:
我們發現 margin-left: -10px 並沒有讓整個元素左移,只是讓左邊緣左移了,右邊緣並沒有動。
現在來總結一下:
- 如果指定了 width,那麼 margin-left: 10px 會使元素整體左移
- 如果沒有指定 width,那麼 margin-left: 10px 只會使做邊緣左移(也就是寬度擴大)
看到這裡,你就會覺得 CSS 特別不正交:
- 為什麼 width 的存在與否會影響 margin-left 的作用?
- 有沒有 width 之外的其他屬性也會影響 margin-left 的作用?恐怕你無法得知。
這就是不正交的恐怖之處,你只有把所有屬性與 margin-left 一起用一下,才能得知 margin-left 的真正規律。
這還只是兩個屬性的影響,你能想象三個屬性互相影響的情況嗎?
我再舉第二個例子,大家都知道 position: fixed 是相對於視口(viewport)定位的。
但是這個「真理」會受另一個元素的影響……對,我知道你很震驚……
先看正常情況:
網頁右邊是一個 iframe,紅色的 .fixed 元素相對於 iframe 視口左上角定位,與我們預期一致。
接下來我在 .box 上面加一個 CSS3 中的屬性,就會改變你的認知:
父容器加了 transform 之後,fixed 定位的元素居然相對於父容器定位。
天知道以後 CSS 會不會加更多元素來影響我已經認為是真理的事情?
這就是「不正交」的噁心之處。
我再舉一個例子,大家都知道給固定寬度的 div 加 margin: 0 auto 可以讓它水平居中,很多人就問,那為什麼 margin: auto 0(注意 auto 和 0 的位置反過來了)不能做到垂直居中呢?
其實是可以的:
但是必須是在當前元素有 position: absolute; top: 0; bottom:0; 的情況下才能垂直居中。這就很「不正交」了。
怎麼學習「不正交」的東西?
只有一個辦法:試。
你試的組合情況越多,就越能瞭解各種奇怪的現象。
別無他法。
目前 CSS 常用的屬性就按 50 個算吧,兩種屬性組合一共有 50 * 49 / 2 = 1225 種情況,三種屬性組合一共有 50 * 49 * 48 / 6 = 19600 種情況。這還沒有算上父子元素的互相影響的情況。
少年,你有生之年,慢慢試吧。
目前我們知道
- 不同屬性的組合會有不同的效果;
- 新的屬性會影響你已經學會的知識;
- 這些規律,毫無邏輯可言,你只能試出來。 這,就是 CSS 難學的原因。
以我的經驗,越是理性的人,越難理解 CSS;越是感性的人,越容易理解 CSS。
這就是為什麼大部分後端程式設計師能學會 JS,卻學不會 CSS——他們太理性了。
你想用學程式設計的思路來學 CSS?放棄吧!
你需要用學畫畫的思路來學習 CSS——每天不停地畫畫,一個雞蛋用不同的方式畫一千遍,就成了。
所以我在課上說過一句話:
CSS 不是科學,CSS 是藝術。