但一旦 & 脫離了嵌套環(huán)境,它就不再參與選擇器拼接,而只是簡單地指向作用域根。在沒有 @scope 的情況下,這個根就是 <html>——這也是它成為一個“隱藏選擇器”的原因之一。
溫馨提示:如果你對 CSS 的嵌套與作用域機制感興趣,尤其是 & 和 @scope 的用法,可以進一步閱讀《CSS 的嵌套和作用域:& 和 @scope》,會有更深入的理解。
之所以可行,是因為在規(guī)范上,<html> 元素只應該包含 <head> 和 <body> 這兩個直接子元素(有點像那種“非黑即白”的設定)。如果你在 <html> 里寫入其他標簽,那屬于無效 HTML,雖然瀏覽器通常會“幫你收拾殘局”,把這些元素自動移動到 <head> 或 <body> 中。
更關鍵的一點是,在標準結構中,沒有其他元素可以包含 <head> 或 <body> 。因此,當我們寫 :has(head) 或 :has(body) 時,理論上只會匹配到 <html> 元素本身(除非你刻意寫出錯誤的嵌套結構,但那顯然不是正常用法)。
這種方式實用嗎?其實并不太實現(xiàn)。但它很好地展示了 :has() 的能力,同時也順帶幫你復習了一下什么才是“合法的 HTML 結構”。
溫馨提示:如今,:has() 選擇器為 CSS 帶來了前所未有的能力,它讓我們可以完成許多過去必須依賴 JavaScript 才能實現(xiàn)的效果。如果你對這些更進階的用法感興趣,那么下面這幾節(jié)課的內容非常值得花時間深入了解。
:not(* *)
除了前面那些方法,我們還可以利用一個很有意思的事實: <html> 是頁面中唯一沒有父元素的節(jié)點?;谶@一點,可以寫出一個略顯“花哨”的選擇器:
代碼高亮:
:not(* *) {
background-color: lightblue;
}
這里的 * * 表示“所有被其他元素包含的元素”,而 :not(* *) 就是把這些元素全部排除掉。最終剩下的,正是那個不被任何元素包含的 <html>。順便一提,* 被稱為“通配選擇器”,可以匹配任意元素。
你也可以在中間加入子代組合符 > :
:not(* > *) {
background-color: lightblue;
}
當然,圍繞這些思路,我們還可以繼續(xù)組合出更多“技巧”的寫法,例如:
:is(&) {}
:where(&) {}
&& {}
&&&& {} /* 沒錯,& 可以無限疊加 */
:has(> body)
:has(> head)
:has(body, head)
/* 等等... */
這些寫法有實際價值嗎?大多數(shù)情況下并沒有。但作為一次探索 CSS 選擇器能力邊界的練習,它們既有趣,也能幫助你更深入地理解選擇器背后的機制。
小結
到這里,我們用各種“非常規(guī)”的方式,把 <html> 元素從頭到尾“折騰”了一遍。從最常見的 html 和 :root,到利用回退行為的 :scope 和 &,再到借助結構關系的 :has(),甚至是通過“排除一切”的 :not(),你會發(fā)現(xiàn):選中 <html> 的方法,遠比想象中要多。
但更重要的并不是這些寫法本身,而是它們背后所體現(xiàn)的規(guī)則——選擇器的匹配邏輯、作用域的概念、優(yōu)先級的影響,以及 CSS 在不同上下文中的行為方式。這些才是真正值得理解的部分。
當然,在實際項目中,我們依然應該優(yōu)先使用簡單、清晰、可維護的寫法,比如 html 或 :root。那些“奇技淫巧”更多是一種探索和練習,它們的價值在于幫助你建立更扎實的底層認知,而不是直接拿來用在生產(chǎn)環(huán)境中。
如果說這篇內容有什么收獲,那大概就是:CSS 遠不只是“寫樣式”這么簡單,它本身也是一門可以被不斷挖掘和玩出花樣的語言。
參考文章:原文鏈接?