display: none與visibility: hidden的區別

發表於2023-09-28

引言:

在前端面試中,一般比較側重JavaScript方面的考察,CSS佈局方面考察的內容會相對少一些,其中display: none與visibility: hidden的區別是較常見的考點,這兩個css配置都可以從視覺上隱藏DOM元素,那這兩者的使用上有什麼區別呢?

display: none

首先我們來看W3C中對display: none的描述:

'none'

​ The element and its descendants generate no boxes or text sequences.

​ Similarly, if a text node is defined to behave as display: none, it generates no text sequences.

Elements with either of these values do not have inner or outer display types, because they don’t generate any boxes at all.

NOTE: As these values cause affected elements to not generate a box, anonymous box generation rules will ignore the elided elements entirely, as if they did not exist in the box tree.

簡單翻譯一下:

元素及其後代不生成盒子或文字序列。

同樣,如果文字節點被定義為display: none,它就不會生成文字序列。

具有上述任一值(指none和contents,W3C的文件中這兩者放在同一小節)的元素都沒有內部或外部顯示型別,因為它們根本不會生成任何盒子。

注意:由於這些值會導致受影響的元素不生成盒子,因此匿名盒子生成規則會完全忽略被省略的元素,就好像它們不存在於盒子樹中一樣。

根據上面這段描述可以看出,給元素設定display: none後,該元素及其後代都不會生成盒子和文字序列,也就是在渲染樹上不會有這個元素對應的節點。

visibility: hidden

首先我們看visibility屬性本身的描述:

The visibility property specifies whether the box is rendered. Invisible boxes still affect layout.

意思是:

可見性屬性指定是否渲染盒子。不可見的盒子仍會影響佈局。

接著繼續看visibility: hidden的描述:

hidden

Any boxes generated by the element are invisible. Descendants of the element can, however, be visible if they have visibility: visible.

意思是:

該元素生成的任何盒子都是不可見的。然而,該元素的後代如果設定為visibility: visible,則就是可見的。

根據上面這段描述,可以看出,給元素設定為visibility: hidden後,該元素也會生成盒子,所以仍會影響佈局,只是不可見(沒有被繪製),並且後代元素可以控制自己是否可見;也就是說後代是可能被顯示到頁面上的,因此渲染樹上會有其對應的節點,只是這個元素本身是不可見的,類似透明。

我們可以看到W3C中還有下面一段描述:

Invisible boxes are not rendered (as if they were fully transparent), cannot be interacted with (and behave as if they had pointer-events: none), are removed from navigation (similar to display: none), and are also not rendered to speech (except when speak is always [CSS-SPEECH-1]). However, as with display: contents, their semantic role as a container is not affected, to ensure that any visible descendants are properly interpreted.

翻譯一下意思是:

隱形盒子不會被呈現(就像完全透明一樣)、無法與之互動(行為類似於設定了pointer-events: none)、被從導航中移除(類似於display: none),也不會呈現為語音(除非speak被設定為always [CSS-SPEECH-1])。不過,與display: contents一樣,它們作為容器的語義作用不會受到影響,以確保任何可見後代都能得到正確的解釋。

我看了一下,大概意思差不多理解,但是其中“被從導航中移除”這個不太理解,原本以為是設定錨點不能跳轉,但是嘗試了一下,發現是可以的,所以又查閱了一下MDN的說法:MDN-visibility

hidden

The element box is invisible (not drawn), but still affects layout as normal. Descendants of the element will be visible if they have visibility set to visible. The element cannot receive focus (such as when navigating through tab indexes).

大致意思就是:

不能被聚焦(比如透過tabindex這個屬性)。

tabindex這個屬性可以使HTML元素獲得焦點,比如:

tabindex

我們可以透過tab鍵使設定了tabindex的元素獲得焦點,也可以直接點選這個元素使它獲得焦點;那麼也就是說如果元素設定了visibility: hidden,即使設定了tabindex屬性,也無法獲取焦點。

另外後面這段內容,我覺得也可以幫助理解,

Using a visibility value of hidden on an element will remove it from the accessibility tree. This will cause the element and all its descendant elements to no longer be announced by screen reading technology.

它的意思是,設定了hidden的元素將從可訪問樹上被移除,其中的內容無法被無障礙閱讀裝置讀取,總體來說,就是將這個被設定了visibility: hidden的元素當做完全不存在,直接跳過,在頁面審查元素的時候,也無法選中這個元素。

兩者對比

從上述的定義中,可以看出,兩者在渲染時,主要有兩個區別:

  • 第一,是對後代元素是否可見的可控性,visibility: hidden無法完全控制後代的可見性
  • 第二,是是否參與佈局計算,從定義中完全可以看出,元素即使設定了visibility: hidden,依舊會生成盒子,會參與佈局的計算

那麼在使用上要怎麼選擇呢?

首先我們可以考慮,是否由元素完全控制其後代的可見性,如果答案是否,那麼display: none就可以排除了。

其次我們還要考慮一件事,就是通常來說,如果某個元素頁面上不需要展示,我們直接可以不寫,但既然我們寫了,那麼這個隱藏元素就可能出現在頁面上,也就是說顯示/隱藏的狀態會發生切換。

這個時候我們就要考慮迴流重排的問題,因為元素在設定display: none時不參與佈局的計算,在狀態切換為顯示時,又會參與佈局,這就會使渲染樹的節點發生改變,導致觸發瀏覽器迴流並重新生成渲染樹;頻繁的切換狀態就會導致頻繁地觸發迴流重排,影響頁面渲染效能,所以在有狀態切換的場景,尤其是頻繁切換,更推薦優先使用visibility: hidden

並且使用visibility: hidden還可以設定一些過渡的顯示效果(transition)。

還要注意一個問題,就是元素設定visibility: hidden後,就無法與之互動,如果遇到一些特殊的業務需求,比如需要與不可見元素髮生互動,或者能夠被無障礙閱讀裝置讀取,這個時候就不能這樣用了,這個時候可以考慮使用opacity: 0,將元素的透明度設定為0,這樣除了變成完全透明,其他方面與普通元素沒什麼不同。

display: none這麼多缺點,是不是就要拋棄不用呢? 那倒也不是這麼絕對,比如有種情況,在頁面載入的時候,根據某些值來判斷是否顯示某個元素,並且後續基本很少切換狀態,那簡單使用display: none也是沒有問題的。

可以用以下比喻來幫助理解

opacity: 0是玻璃(固態,有形),雖然看不見,但是摸得著(可以獲得焦點)

width: 0是水(液態),但摸得著(可以獲得焦點),觸碰它可以直接穿透,相當於沒有寬度

visibility: hidden是空氣(氣態,無形),雖然看不見也摸不著(無法獲得焦點),但是是真實存在的氣態物質

以上三種都是存在的物質,固體和液體可以摸得著(獲得焦點),但是氣體摸不著(無法獲得焦點);所以在渲染樹上都會存在。

而display: none可以理解成沒有肉身,只有能量態,比如一個按鈕被設為display: none,它仍舊可以被觸發點選事件,它這個能量(功能)還存在。

相關文章