olNGIb4NkK5r2x7x4oG3GpEzizVpnY6KNCck9cym

如何使用 CSS 的 :not() 偽類(pseudo-classes)來排除特定的條件?

使用 CSS 的 :not() 偽類(pseudo-classes)可以匹配不符合一組選擇器的元素,或者說防止特定的元素被選中。

發現問題

筆者發現在日知紙簍上的程式碼區塊,都會在首行多出一個空白,反白後更為明顯。如下圖所示:

在日知紙簍上的程式碼區塊中,首行都會多出一個空白,反白後更為明顯
日知紙簍上的程式碼區塊中,首行都會多出一個空白,反白後更為明顯

問題排查

從前面的螢幕截圖中可以看到,由於反白後明顯可以看到有一小個空白無法被選中反白,可以初步推斷並不是在撰寫文章時誤植了空白字元

那麼問題可能就出在 CSS 樣式上了,使用瀏覽器的開發者工具(DevTool)檢查對應元素,可以更快速地定位與排查 CSS 樣式或是 JavaScript 腳本問題:

使用瀏覽器的開發者工具,定位並排查 CSS 樣式和 JavaScript 腳本問題
使用瀏覽器的開發者工具,定位並排查 CSS 樣式和 JavaScript 腳本問題

抓到兇手了!這個空白是來源於 <code> 元素的 ::before 偽元素選擇器(pseudo-element selector):

code::before, code::after {
    letter-spacing: -.2em;
    content: "\a0";
    display: contents;
}

這段 CSS 樣式是筆者從 HackMD 上借鑑而來,用來替行內的 <code> 元素添加左右的空白,使其排版更為整齊,但沒想到也影響到了程式碼區塊 <pre> 中的 <code> 了。

解決方案

既然知道了問題,那便容易解決了,由於程式碼區塊都會在外層使用 <pre> 來包裹內層的<code>,那麼便可以透過添加更具體的 CSS 選擇器規則來避免樣式不小心被套用或覆蓋。

有一種解決方案,是增加一組 CSS 樣式來明確地對 <pre> 下的 <code> 重置樣式:

pre code::before, pre code::after {
    all: unset; /* 移除所有 inherited 和 applied 樣式 */
}

但也可以換個角度思考:原本的樣式要排除外層使用 <pre><code> 元素的狀況,這個想法可以使用 :not() 偽類(pseudo-classes)來實現,由於 :not() 可以用來匹配不符合一組選擇器的元素,或者說防止特定的元素被選中,因此也被稱為反選偽類(negation pseudo-class)。

針對原來的問題,調整後的 CSS 樣式如下:

code:not(pre code)::before, code:not(pre code)::after {
    letter-spacing: -.2em;
    content: "\a0";
    display: contents;
}

張貼留言