具代表性的面試主題

前端面試題:如何用 transition-behavior 處理離散 UI 狀態?

前端中等
Offer.cc 編輯團隊發佈 更新

題幹

為什麼 display: none 不能直接平滑過渡?請用 transition-behavior 設計一個相容的彈窗進入和退出方案。

1. 題目與適用場景

一個對話框透過 display: none 隱藏,產品希望它淡入、淡出並在動畫結束後離開版面。團隊只給 opacity 加上 transition,結果開啟時沒有進入動畫,關閉時元素立即消失。請解釋離散屬性的限制,設計 CSS 方案,並說明瀏覽器降級、焦點管理和 prefers-reduced-motion

2. 面試官考察點

  • 是否知道 displaycontent-visibility 等離散屬性不會產生連續中間值。
  • 是否能正確使用 transition-behavior: allow-discrete 或 shorthand,而不是把它誤當作動畫時長。
  • 是否理解進入和退出時的翻轉時刻不同,且 @starting-style 負責建立初始樣式。
  • 是否把相容性、無障礙、焦點、減少動效和真實渲染測試納入答案。

3. 回答前需要釐清的問題

  1. 元件是普通元素、原生 dialog 還是 popover 頂層元素?
  2. 隱藏後是否必須離開版面、不可聚焦,還是只需跳過繪製?
  3. 支援的瀏覽器範圍和無法使用離散過渡時的降級是什麼?
  4. 動畫期間焦點、Escape、點擊遮罩和減少動效偏好如何處理?

4. 30 秒回答框架

display 是離散屬性,預設不會在兩個值之間產生 transition。用 transition-behavior: allow-discrete 明確允許離散屬性參與過渡,並同時給 opacitytransform 設定非零時長。隱藏到顯示時要讓元素在整個進入動畫期間可見,顯示到隱藏時要讓它保持可見直到動畫結束;@starting-style 可提供首次插入時的起始值。舊瀏覽器保留 opacity/transform 的連續動畫,並接受瞬時 display 切換;同時管理焦點、減少動效和可存取狀態。

5. 分步驟深入解答

第一步:區分連續與離散屬性

opacitytransform 可以計算連續中間值;display 只有取值切換。沒有 allow-discrete 時,瀏覽器會在狀態變化時立即切換 display,所以關閉動畫還沒開始元素就被移除,開啟動畫也沒有可見的初始幀。transition-behavior 只決定是否允許離散屬性過渡,仍需要 transition-property 和非零 transition-duration

第二步:為普通面板建立雙向過渡

先寫連續屬性的相容宣告,再寫帶 allow-discrete 的宣告。退出時 display 應在 100% 附近翻轉,確保內容保持可見;進入時應儘早翻轉到可見值。不要只寫 transition-behavior 而忘記 opacity 或 transform 的目標狀態。

css
.panel {
  opacity: 0;
  transform: translateY(-0.5rem);
  display: none;
  transition: opacity 180ms ease, transform 180ms ease;
  transition: opacity 180ms ease, transform 180ms ease, display 180ms allow-discrete;
}

.panel[data-open="true"] {
  opacity: 1;
  transform: translateY(0);
  display: block;
}

第三步:處理首次進入和 popover

動態插入或 popover 首次開啟時,元素可能沒有舊的可見樣式可供比較。@starting-style 可宣告開啟狀態的起始 opacity 和 transform。原生 popover 還會進入 top layer,必須把 displayoverlay 納入離散過渡,否則退出時會直接從頂層移除。不同瀏覽器對這些組合的支援要用目標版本實測。

第四步:設計降級與能力偵測

舊瀏覽器會忽略未知的 allow-discrete,但應仍保留基礎 opacity/transform 過渡。可以用 @supports (transition-behavior: allow-discrete) 包住增強規則,避免把整組基礎樣式放進去。降級狀態必須仍可開啟、關閉、聚焦和讀取,不應因為動畫功能缺失而把內容永久隱藏。

第五步:無障礙、效能與驗證

動畫不等於可存取狀態。關閉後的對話框需要同步 hiddenaria-hidden 或原生 dialog 狀態,並把焦點返回觸發按鈕;開啟時把焦點移入並支援 Escape。prefers-reduced-motion: reduce 下縮短或移除時長。驗證時檢查版面、繪製、鍵盤焦點、螢幕閱讀器、快速重複開關和中斷動畫,並記錄低階裝置的合成與版面成本。

6. 高品質示範回答

display 是離散屬性,不能像 opacity 一樣產生連續中間值。我的基礎規則先過渡 opacity 和 transform,再用 transition-behavior: allow-discrete 把 display 納入增強路徑;進入時先切到可見值,退出時保持可見直到動畫末尾。首次開啟用 @starting-style 提供起始幀,popover 還要處理 top layer 的 display 和 overlay。舊瀏覽器忽略增強宣告但仍有基礎動畫。程式碼之外,我會同步焦點、Escape、隱藏語義和 reduced-motion,並用多瀏覽器和鍵盤測試確認不會閃爍或把內容留在可聚焦狀態。

7. 常見錯誤

  • 只給 opacity 寫 transition → display 立即切換 → 使用 allow-discrete 並保留連續屬性動畫。
  • transition-behavior 當作 duration → 離散屬性仍無動畫 → 同時宣告 property 和非零 duration。
  • 關閉時 display 先變 none → 使用者看不到淡出 → 讓離散翻轉與退出時長對齊。
  • 忽略首次插入 → 第一次開啟沒有進入動畫 → 用 @starting-style 建立初始狀態。
  • 只測滑鼠和現代瀏覽器 → 鍵盤、讀屏或舊瀏覽器狀態錯誤 → 加入 supports 降級、焦點和 reduced-motion 驗證。

8. 追問及應對

追問一:為什麼關閉時 display 要在動畫結束才切換?

如果提前切換成 none,元素會離開版面和繪製樹,opacity 的剩餘時長沒有可見內容。讓它保持可見到 100% 附近,才能完成淡出,再進入隱藏狀態。

追問二:allow-discrete 會讓 display 逐幀插值嗎?

不會。它允許離散值參與 transition,但值仍在特定時間點翻轉;連續視覺應由 opacity、transform 等屬性提供。

追問三:怎樣讓舊瀏覽器仍可用?

先宣告不包含 allow-discrete 的基礎 transition,再用 @supports 或後續宣告增強。功能缺失時接受 display 瞬時切換,但保持按鈕、焦點和隱藏語義正確。

追問四:如何處理 reduced-motion?

prefers-reduced-motion: reduce 下把 duration 降到很短或設為零,同時保留狀態同步、焦點移動和可關閉操作;不要只隱藏動畫而留下不可見但可聚焦的元素。

公開來源

同類題目