<code>display: contents</code> 是一個比較陌生的屬性,雖然屬于 display 這個基本上是最常見的 CSS 屬性,但是 <code>contents</code> 這個取值基本不會用到。但是它早在 2016 年就已經得到了 Firefox 的支援。
本文将深入一下這個有意思的屬性值。
根據 W3C 對 <code>display: contents</code> 的定義。
The element itself does not generate any boxes, but its children and pseudo-elements still generate boxes and text runs as normal. For the purposes of box generation and layout, the element must be treated as if it had been replaced in the element tree by its contents (including both its source-document children and its pseudo-elements, such as ::before and ::after pseudo-elements, which are generated before/after the element’s children as normal).
簡單翻譯一下即是,将設定了該屬性值的元素本身将不會産生任何盒子,但是它的從保留其子代元素的正常展示。
看個簡單的例子。有如下簡單三層結構:
簡單的 CSS 如下:
表現如下:
這個非常好了解,但是如果,我們給中間層的容器添加上 <code>display: contents</code>,再看看效果:
可以看到,沒有了中間層的 <code>border: 2px solid red</code> 的紅色邊框,整個 <code>.wrap</code> div 好像不存在一樣,但是它的子元素卻是正常的渲染了。
重點,設定了<code>display: contents</code>的元素本身不會被渲染,但是其子元素能夠正常被渲染。
這個屬性我一直在思考有什麼非常适合的使用點。
總結來說,這個屬性适用于那些充當遮罩(wrapper)的元素,這些元素本身沒有什麼作用,可以被忽略的一些布局場景。
最近寫 React、Vue 的時候,發現這個屬性在寫 JSX 的時候能有很好的作用,并且也非常符合這個屬性本身的定位。
我們在寫 React、RN 時,經常需要輸出一段模闆。
我們隻是想輸出 <code>.wrap</code> div 内的内容,但是由于架構要求,輸出的 JSX 模闆必須包含在一個父元素之下,是以不得已,需要添加一個 <code>.wrap</code> 進行包裹,但是這個 <code>.wrap</code> 本身是沒有任何樣式的。
如果輸出的元素是要放在其他 <code>display: flex</code>、<code>display: grid</code> 容器之下,加了一層無意義的 <code>.wrap</code> 之後,整個布局又需要重新進行調整,麻煩。
一種方法是使用架構提供的容器 <code><React.Fragment></code>,它不會向頁面插入任何多餘節點。
在 Vue 中類似的是 <code><template></code> 元素, <code><template></code> 也是不會被渲染在 DOM 樹中,檢視頁面結構也無法看到,但是 <code>display: contents</code> 是存在于頁面結構中的,隻是沒有生成任何盒子。
這個多出來的父元素其實是沒必要的。這個時候,我們也可以添加上 <code>display: contents</code>,像是這樣:
這樣,它既起到了包裹的作用,但是在實際渲染中,這個 div 其實沒有生成任何盒子,一舉兩得。并且像一些 <code>flex</code> 布局、<code>grid</code> 布局,也不會受到影響。
Codepen Demo -- display: contents | display: flex 的穿透影響
考慮這個非常實際的場景,現在我們的頁面上充斥了大量的可點選按鈕,或者點選觸發相應功能的文字等元素。但是,從語義上而言,它們應該是一個一個的 <code><button></code>,但是實際上,更多時候我們都是使用了 <code><p>、<div>、<a></code> 等标簽進行了模拟,給他們加上了相應的點選事情而已。
像是下面這樣,雖然沒什麼問題,但是相對而言不那麼符合語義化:
我們不使用 <code><button></code> 的原因有很多,<code><button></code> 相對 div 而言沒那麼好控制,且會引入很多預設樣式。但是,有了 <code>display: contents</code>,我們可以讓我們的代碼既符合語義化,同時不需要去解決 <code><button></code> 帶來的一些樣式問題:
添加了 <code><button style="display: contents">Click Me</button></code> 的包裹,不會對樣式帶來什麼影響,button 也不會實際渲染在頁面結構中,但是頁面的結構語義上好了不少。
CodePen Demo -- Button with display: contents
對于對頁面結構、語義化有強迫症的一些同學而言,靈活運用這個屬性可以解決很多問題。
<code>display: contents</code> 并非在所有元素下的表現都一緻。
對于可替換元素及大部分表單元素,使用 <code>display: contents</code> 的作用類似于 <code>display: none</code>。
也就是說對于一些常見的可替換元素、表單元素:
<code><br></code>
<code><canvas></code>
<code><object></code>
<code><audio></code>
<code><iframe></code>
<code><img></code>
<code><video></code>
<code><frame></code>
<code><input></code>
<code><textarea></code>
<code><select></code>
作用了 <code>display: contents</code> 相當于使用了 <code>display: none</code> ,元素的整個框和内容都沒有繪制在頁面上。
與其他表單元素不一樣,正常而言,添加了 <code>display: contents</code> 相當于被隐藏,不會被渲染。但是實際運用過程中發現,<code><button></button></code> 如果包裹了内容,其一些可繼承樣式還是會被子内容繼承。這個實際使用的過程中需要注意一下。
在一些外文文檔中有一些讨論是關于 <code>display: contents</code> 的使用會影響到頁面的可通路性。例如作用了 <code>display: contents</code> 的容器及清單,會對頁面的可通路性帶來一些意外結果。
[css-a11y][css-display] display: contents; strips semantic role from elements
這個我看暫時沒有明确的結論,如果你的頁面對可通路性的要求很高,具體使用的此屬性的話也是需要注意一下這一點。
CSS 本身其實也在一直在努力,增加了各種屬性去讓我們在布局上有更多的空間與控制權。總而言之給我的感受是讓 CSS 更加的像是一個完整的工程而不僅僅隻是展現樣式。
類似的一些有意思的屬性:
CSS新特性contain,控制頁面的重繪與重排
看看相容性。
不算太慘淡,但也不算全面普及。考慮用在一些漸進增強的場景當中。
How display: contents; Works
CSS的display:contents
Display: Contents Is Not a CSS Reset
好了,本文到此結束,希望對你有幫助 :)
更多精彩 CSS 技術文章彙總在我的 Github -- iCSS ,持續更新,歡迎點個 star 訂閱收藏。
更多精彩有趣的 CSS 效果,歡迎來這裡看看 CSS 靈感 -- 在這裡找到寫 CSS 的靈感。
如果還有什麼疑問或者建議,可以多多交流,原創文章,文筆有限,才疏學淺,文中若有不正之處,萬望告知。