尋根問底之——元素隱藏你知多少?

老生常談之display: none

相信小夥伴們都被問過這樣一個問題:讓一個元素隱藏起來,有多少種方法呢?常規來說,咱們有三種方法display: noneopacity: 0visibility: hidden,基於display: none的反作用,已是個被說爛的問題,主要是有如下缺點:css

1、切換顯隱時會致使reflow(迴流),從而引發repaint(重繪),當頁面中reflow增多至必定程度時,會致使cpu使用率飆高。html

2、沒法對元素設置過渡動畫,也沒法進行方位測量(包括clientWidth, clientHeight, offsetWidth, offsetHeight, scrollWidth, scrollHeight, getBoundingClientRect(), getComputedStyle())前端

緣由是:瀏覽器會解析HTML標籤生成DOM Tree,解析CSS生成CSSOM,而後將DOM Tree和CSSOM合併生成Render Tree,最後才根據Render Tree的信息佈局來渲染界面,但設置了display: none的元素,是不會被加入Render Tree中的,天然也沒法渲染過渡動畫。瀏覽器

3、用它來設置顯隱切換時,會由於與display: flexdisplay: grid衝突而令人困擾。微信

你真的瞭解opacity和visibility嗎

如此說來,咱們設置元素顯隱時,使用opacity或visibility彷佛是更好的選擇,但小夥伴們有沒有考慮過,opacity: 0visibility: hidden這二者又有何具體區別呢? 既然標題寫着尋根問底,那麼咱們就經過幾輪PK來深挖一下這二者的具體區別:ide

第一輪:動畫屬性

常見的動畫效果中,使用最普遍的應該就屬淡入和淡出了,這時候,咱們應該只有一種選擇:opacity配合animation,由於visibility這個屬性是沒法進行動畫過渡的,要知足動畫過渡,必須在兩個值之間存在接二連三的值,即連續區間,visibility顯然不知足,由於在可見/不可見兩個狀態之間不存在中間態,它是「布爾隱藏」的。佈局

第二輪:子元素的表現

設置了opacity: 0visibility: hidden的元素,它們的子元素會受到怎樣的不一樣影響呢?flex

首先,opacity屬性是不能夠被子元素繼承的,而visibility屬性能夠被繼承,詳見CSS3規範opacityvisibility中的屬性介紹。動畫

其次,一旦父級元素設置了opacity,那麼子元素的最大透明度將沒法超過父級,意味着,父級的opacity爲0.5,那麼子級的opacity就算設置爲1,其實際透明度也會是0.5 * 1 = 0.5,因此,只要父級透明度爲0,那麼子級沒有任何辦法能夠從新設置爲可見;ui

但visibility的子級卻仍有「翻身」的機會,即便父級元素設置了visibility: hidden,子元素仍可經過visibility: visible從新設置爲可見。

第三輪:層疊上下文(Stacking Context)

HTML中的元素都有自身的層疊水平,可是某些狀況下,元素會造成層疊上下文(接下來用SC代替),直接「拔高」自身以及子元素的層疊水平。而元素間不一樣的層疊水平,在它們發生重疊的時候,就會決定誰將在Z軸上更高一籌,也就是誰離用戶的眼睛更近。

至於什麼狀況下元素會造成SC,能夠參考MDN文檔的詳細說明。而在這份文檔中咱們能夠看到:當元素的opacity屬性值小於1時,會造成SC。咱們能夠觀察以下代碼:

<div style="position: relative;">
    <div style="position: absolute;background: green; top: 0;width: 200px;height: 200px">
    </div>
    <div style="background: red;width: 100px;height: 100px"></div>
</div>
複製代碼

這種狀況下,設置了絕對定位的綠色方塊造成了SC,因此其層疊水平天然比紅色方塊高,因此此時咱們看不到紅色方塊:

而當咱們爲紅色方塊設置了opacity屬性後,好比:

<div style="position: relative;">
    <div style="position: absolute;background: green; top: 0;width: 200px;height: 200px">
    </div>
    <div style="opacity: 0.5;background: red;width: 100px;height: 100px">
    </div>
</div>
複製代碼

此時,紅色方塊會層疊在綠色方塊之上。由於紅色方塊的opacity小於1,造成了SC,且二者都未設置z-index,屬於相同層疊水平,因此按照後來居上的原則,紅色方塊就會疊在上方,如圖所示:

同理,opacity爲0的元素也會建立SC,而visibility屬性則不會建立SC,也不會影響到元素的層疊水平。

說了半天,有人可能會問,既然元素都隱藏了,看不見了,誰還管它在上在下呢?一般狀況下是如此,但通過第四輪的PK後,你就會知道,有時候你的確不能忽視這個問題。

第四輪:可交互性/可訪問性

這一輪咱們比較的是可交互性/可訪問性,先說visibility: hidden,設置了這個屬性的元素,其綁定的監聽事件將會忽略event.target爲自身的事件觸發。這句話比較拗口,通俗點說就是,這個元素會接收到子元素的事件冒泡,但沒法觸發自身的事件,能夠經過這個在線demo體驗一下這個效果。

固然,除了沒法觸發自身的事件以外,它還沒法經過tab鍵訪問到,也就是沒法focus;此外,它還會失去accessibility,也就是不能進行無障礙訪問,好比屏幕閱讀軟件將沒法訪問到這個元素。

反觀設置了opacity: 0的元素,則徹底沒有以上的限制。如今你知道咱們爲啥不能忽視上一輪提出的問題了,由於設置了opacity: 0的元素即便看不見了,它仍然能夠被點擊被訪問,有時會產生意料以外的bug。

取長補短

既然二者都有各自的優缺點,咱們可否將其結合,並取長補短呢?

答案是固然能夠。但首先要明確咱們想取什麼長,補什麼短。通常來說,咱們既但願元素可使用淡入淡出的動畫效果,又但願在消失後不要保留可交互性/可訪問性,其實作法很簡單:

.box {
  animation: fade 0.5s linear 0s forwards;
}
@keyframes fade {
  0% {
    opacity: 1;
  }
  100% {
    opacity: 0;
    visibility: hidden;
  }
}
複製代碼

咱們仍然使用opacity來作動畫過渡,但在最後一個動畫幀,咱們把visibility: hidden加上,就能夠達到咱們想要的效果了。此時,當元素淡出後,也不會意外地觸發事件了。而且,在使用opacity屬性進行動畫效果時,瀏覽器還會將該元素提高爲composite layer(合成層),使用gpu進行硬件加速渲染,一箭雙鵰~

固然,若是你的確須要這個元素保留頁面中的佔位,就不能這樣作了。

總結

總而言之,若是你沒有動畫需求,使用visibility進行顯隱切換可能更省心,但若是有動畫需求,則最好使用二者結合的方式。另外,之後會有更多的尋根問底系列的文章,目的就是要對小的知識點也進行深刻剖析,從而得到更加系統性的認識,而不是停留在表面。

ps:歡迎關注微信公衆號——前端漫遊指南,會按期發佈優質原創文章和譯文,關注公衆號福利:回覆666能夠得到精選前端進階電子書,感謝~

相關文章
相關標籤/搜索