除錯 CSS 的方法

發表於2016-09-06

我經歷過許多 CSS 程式碼的除錯工作,有別人寫的也有自己寫的,有移動端平臺的也有標準桌面瀏覽器的,從陳舊的 IE 到最新的基於 Webkit 的每日構建。經驗告訴我,很多人並沒有一個標準的 CSS 除錯流程。

我發現在大多數情況下,擁有專業的解決問題的方法,能夠節省花在 bug 上的時間。

下面是我總結的經驗。

我不保證這是最適合的除錯 CSS 的方法,但是確實對我很有效。如何 CSS 不是你的主要程式語言,除錯它可能就像暗黑藝術一樣;遵循下面的指南能夠幫助你更有效地定位和解決 bug。

概括地說,我把除錯流程分為 3 個階段:

  • 評估並快速修復
  • 還原和重現
  • 定位根源並修復

我們挨個解釋每個階段並實踐一個例子。

評估並快速修復

如果 CSS 是你的主要工作語言,或者你對 CSS 有一定的理解和實踐經驗的話,解決 CSS 問題就有很多簡單的方法,否則的話,方法就少一些。

有經驗的 CSS 開發者可能都知道的一些 CSS 陷阱:

  • 圖片周邊存在有趣的空白?設定 display: block(圖片預設是內聯的,因此會有空白)。
  • 元素排列不正確?你可能有浮動的元素。
  • 絕對定位元素不顯示、位置錯誤或者被遮擋?你可能沒有設定父元素的 position 屬性或者用 transform 及 opacity 建立一個 z-index 上下文。
  • 偽元素不顯示?你可能忘記了設定 ‘content’的值。

這樣的 “bug” 有一大堆。實際上根本沒有 bug,更多的是開發者缺少對瀏覽器行為的理解。更準確地說,是 CSS 程式碼讓瀏覽器怎麼做。

對這些 CSS 特性熟悉的開發者能夠快速定位到問題並且修復。他們對 bug 的認識與那些對 CSS 不瞭解的人會產生分歧。這樣在解決 CSS bug 中對‘工作流’需求的重要性的認識就會因人而異。

對於‘快速修復’中沒有覆蓋的陌生問題,在開發者工具中靠猜來解決問題的方式已經沒什麼價值。即使運氣好問題被解決了,也很難判斷出問題到底是怎樣被解決的。

如果出現的問題不能被輕易解決,先確定問題區域的範圍,抓取 HTML 標籤(也就是拷貝 DOM),進入下一個除錯階段:還原和重現。

專業提示:大多數瀏覽器的開發者工具會讓你選擇包裹元素並拷貝 HTML 區塊。在 Chrome 的開發者工具中,要連同包裹元素一起拷貝,需要點選 ‘Copy > Copy OuterHTML’。

還原和重現

本階段的 CSS bug 修復在類似 Codepen 的幫助下異常簡單。我們目的主要是復現出此問題 – 也就是引起 bug 的程式碼。這能讓我們快速定位 bug,直搗黃龍。

為清晰起見,只把相關的 HTML 和 CSS 提取出來復現問題。你既可以手打 HTML 對應的 CSS,也可以複製真實的程式碼。如果可能的話,不用把所有 CSS 程式碼一股腦拷貝過去重現問題,保證最精簡的要素即可。保持逐步增加 CSS 的習慣,問題就會自己找到你。

在快要接近真相時,往往只需要一個特殊的 CSS 屬性的改變就能讓 bug 暴露出來。

相對應的做法是,把所有 CSS 都扔進入復現問題,然後每次移除一點,直到問題出現。在實踐中,我發現這略笨,不用也因人而異,你可能有不同的見解。

逐步地增加或刪除 CSS 程式碼已經是重現問題和定位故障的固定套路了。

那麼 HTML 標籤呢?

假設使用最少 CSS 程式碼復現問題時,效果有如原始程式碼一樣。這也是有用的,我們現在看 HTML 標籤。

第一件事要做的,也是不能跳過的,就是檢查標籤的有效性。即使報告出我們不關心的問題(比如 meta),至少能保證它不會以某種方式破壞美感。我們希望能發現未閉合的標籤、沒有引號的屬性,以及其它任何可能影響瀏覽器解析的問題。建議你使用 W3C validator

一旦標籤檢查通過,將有助於消除瀏覽器引入意外樣式的可能性。這樣做:

首先,把所有元素改成 div(塊級元素)和 span(行內元素),保證它們只被 CSS 的類選擇器選中。也許有必要把額外的選擇器移除,如把 a.link 改為 .link

通過使用固定的標籤我們消除了瀏覽器針對特定元素引入預設樣式的可能性。表單元素是個特例(馬上會在例子中見到)。

如果把所有元素改成 div 和 span,問題消失了,那麼瀏覽器引入預設樣式的嫌疑就被確定了。現在在 computed styles 皮膚中尋找瀏覽器增加了什麼樣式,想辦法覆蓋它。總之就是要看計算後的樣式。

定位根源並修復

如果簡化 HTML 標籤也沒有找到問題,並且是可穩定復現的,那麼就該換個瀏覽器試一試。是否同樣的問題出現在 Chrome,IE,Safari 和 Firefox 上?如果不是,哪個的表現是正確的?如果只有一個瀏覽器是錯的,那麼就值得去搜尋一下對應的 bug 跟蹤系統了:

是某瀏覽器的問題嗎?或者是某瀏覽器的特定版本的問題?問題是否在修復中?有沒有不影響其它瀏覽器的解決方案?實在不行你可以為特定的瀏覽器編寫修復程式碼嗎?

過去我曾詳細描述過如何向瀏覽器提 bug,在 2011 年 Lea Verou 也寫過一份描述提 bug 流程的文章

另一種情況是可能需要‘無害的’hack。例如,我最近遇到的一個場景是在一個塊級元素後面的元素必須是絕對定位的才能顯示出來。 left: 100% 只有在 IE 瀏覽器(移動端是Windows Phone 8,8.1 和 10)中不生效。IE 中在兩個元素之間總有一個空隙。看起來像是一個亞畫素渲染問題,因此 left: 99.99% 就能解決問題而不會影響其它瀏覽器。這是個 hack 手段,但我們知道原理(有的瀏覽器會舍入,其它則不會),標註在 CSS 的註釋中,沒有任何危害。

微軟的 Greg Whitworth 告訴我了關於亞畫素舍入的更多細節。WebKit 和 Blink 核心舍入 1/64,Gecko 核心舍入 1/60,Edge 舍入 1/100(感謝 Webkit 開發者 ‘smfr’)。

計算後樣式

開發者工具中比較容易被忽視的是 computed styles 皮膚。如果你對 computed styles 不熟悉的話,顧名思義,就是真正應用到元素上的樣式。這很重要,因為你寫的樣式不一定會生效。同樣,你寫的樣式也不是所有生效的樣式。下面的例子將解釋我的意思:

對應的 CSS 是:

outer 的寬度會是多少?如果你認為是 max-width 的 400px,我會原諒你的。但是不是我們看到的寬度,看 Ben Frain 編寫的 codepen

什麼情況?為什麼不是 max-width 的值?給你個思路,開啟 Computed Styles 皮膚。

找到問題的根源了嗎?

我來給你解釋下。預設地,fieldset 元素的寬度會等於其內容的寬度。在 Chrome 的Computed Styles 皮膚中,min-width 的值是一個新的 min-content

min-width 設定一個新值來“修復”它。這個例子中,min-width: 0 就會讓 max-width 按照我們期望的那樣進行工作。

這正是開發者工具的 Computed Styles 皮膚中看到的值。記住你寫的程式碼不一定是瀏覽器計算後的。

討論

頁面出現異常的原因可能很多並且不盡相同。不同瀏覽器對規範的實現存在差異是普遍存在的現象。相比於編寫一個瘋狂的瀏覽器 bug 目錄,解決問題的最有效流程還是始終保持條理性。總結來看有效的措施包括:

  • 評估 bug,執行快速修復
  • 使用最少的程式碼重現問題
  • 利用工具和 bug 追蹤描述原因
  • 使用更靈活的程式碼修復問題,或者使用註釋過的hack手段,亦或拷貝副本修復

相關文章