CSS 中新單位的問題

2024-02-07
預估閱讀時間: 5 分鐘

雖然新的 CSS 新的 viewport 單位如 svh、dvh 和 lvh 為開發者帶來了不錯的改進,可以幫助他們確定手機上的 viewport 多大。因為有些瀏覽器下面會有工具欄,因此 viewport 的大小會不一樣。但我注意到那些單位有時會在 iOS 裝置上發生一些問題。在這篇文章中,我會深入探討我發現的那些 bug,並提出利用 JavaScript 和 CSS 的簡單解決方案。

兩年前,大約在 iOS 15 推出時,Safari 跟其他手機瀏覽器的 UI 發生了一個小變化。許多瀏覽器開始把網址列移到下面去了. 不確定這在 Android 是什麼時候發生的, 但對於 iPhone 來說, 我記得大概是 2021 年隨著 iOS 15 的推出。我認為這個想法的背後的原因是,隨著時間的推移,手機的螢幕變得越來越大,因此蘋果要重新思考如何設計更好的瀏覽器 UI – 因為確實如果網址欄在下面,這樣可以更容易打字。另外一方面, 這讓我想起 Steve Jobs 對於 3.5寸是手機最理想的螢幕大小的預測是完全錯誤的。 當於 2007 年第一代 iPhone 推出時,他曾說:

「我們為您的手設計了一個美妙的東西,非常美妙。」

Steve Jobs 還堅持認為 iPhone 不應該超過3.5寸,這樣子使用者可以用一隻手操作。雖然我同意他的觀點,儘管能夠單手使用手機是方便的,但我無法想像有了 iPhone 13 之後回到我舊的 iPhone SE2。我認為一旦買更大的螢幕的手機,你就無法回到比較小的大小。最後我覺得,單手操作跟螢幕大小比起來,我會選擁有更大的螢幕。

無論如何,Android 和 iOS 都把 Chrome 跟 Safari 的網址欄移到下面去,然後這導致了許多網頁設計時和開發者遇到了一些奇怪的 CSS 的問題,比如通常跨越 100% 高度的 HTML 元素,包含主頁橫幅(hero)、 漢堡選單(hamburger menu)、彈跳窗(popup), 會超過 100% 的高度或當你往下滑動時,整個hero會卡卡的。這是因為現在 100vh 規則裡面包含網址欄,所以它的高度會比實際的高度更高。

新的CSS單位: dvh、svh 和 lvh

我知道目前可以使用所謂的 dvh、svh 和 lvh 的單位,不過有一個小問題。由於我的客戶關心他們的網站在通訊軟體打開以後的外觀,測試的時候我發現了其實那些單位有時候沒辦法使用。比如說,在台灣最流行的通訊軟體是 Line,然後如果你在 Line 裡面打開連結,在 iOS 上那個連結應該直接是用Safari處理的,可是我發現了在 Line 裡面 dvh、svh 和 lvh 的單位還是沒有作用。如果用 Safari 打開是正常的,但用 Line 或其他瀏覽器(我也測試過Kagi) 的話,那些單位沒有作用。這很奇怪,因為在 iOS 上一切連結都是被 Safari 處理的,然後根據 caniuse 網站的資料,從 2022 年以來 iOS 上的 Safari 完全支援這些新的單位。例如,在以下的影片,我們可以看到一個有主頁橫幅 (hero) 的簡單HTML網站。Hero 的容器是一個 100svh 的 div,因此在往下滑動的時候,它的高度應該要保持不變。不過當我往下滑動時,整個東西發上了高度變化,就像跟dvh一樣:

Safari – 很順
Line – 很卡

我知道這可能不是很常見的問題,但對我來說,我需要我的網站在各種通訊應用程式保持一樣的外觀和正確的大小。 我的客戶會常常把他們網站的連結用 Line 傳給其他人。 我認為如果你不在乎通訊程式,那麼我想你應該可以直接用 svh、dvh 和 lvh。

從我的經驗來看,dvh 比較適合用在漢堡菜單。這就是因為漢堡菜單會在不同的 scrollbar 的位置打開。比如說,一開始 loading 的時候我們菜單的高度,跟我們往下滑動以後的高度是不一樣的。不過對於主要橫幅 (hero) 我認為你不應該用 dvh 因為往下滑動的時候,他會卡卡的(看上面的 video 就知道)。Lvh 也不適合用在hero上面,應為這樣子高度會超過螢幕的高度。所以對於 hero 來說我建議用 svh。

有趣的是, navigator.userAgent 可以告訴你,你在哪一個應用程式打開連結. 比如說, 對於 Line 它返回這樣的 value:


Mozilla/5.0 (iPhone; CPU iPhone OS 16_5 like Mac OS X)
AppleWebKit/605.1.15 (KHTML, like Gecko) 
Mobile/15E148 Safari Line/14.1.1

所以目前,為了解決這個問題我寫了一個非常 hacky 的 JavaScript 程式,然後用 Line 的時候,它可以覆蓋 hero 的高度:


const hero = document.querySelector(".hero");

if (navigator.userAgent.includes("Line") && hero) {
  hero.style.setProperty("height", `${window.innerHeight}px`, "important");
}

這個不會造成任何 CLS,因為一開始 loading 的時候,它的大小時正確的,只有往下滑動的時候才會卡卡的:


.hero {
  height: 100svh;
}

也許這不是最漂亮的解決方法,但可以解決這個問題。希望之後通訊應用程式和其他瀏覽器對 dvh、svh 和 lvh 的支持會進步。讓我在以下的留言的 section 知道你對這個東西的想法,也許你有更好的解決方法!

發佈留言