Kalan's Blog

Kalan 頭像照片,在淡水拍攝,淺藍背景

四零二曜日電子報上線啦!訂閱訂起來

本部落主要是關於前端、軟體開發以及我在日本的生活,也會寫一些對時事的觀察和雜感
本部落格支援 RSS feed(全文章內容),可點擊下方 RSS 連結或透過第三方服務設定。若技術文章裡有程式碼語法等特殊樣式,仍建議至原網站瀏覽以獲得最佳體驗。

目前主題 亮色

我會把一些不成文的筆記或是最近的生活雜感放在短筆記,如果有興趣的話可以來看看唷!

HTML 和 CSS 能解決很多問題,但 JS 也很重要

在 2017 年時曾經讀過一篇文章 – Effective前端1:能使用html/css解决的问题就不要使用JS,第一次閱讀時覺得深有同感,也學到很多當時還不熟的技巧,很推薦大家看看。雖然 JavaScript 幾乎可以解決大部分問題,但從無障礙設計的角度、效能跟 bundle size 的角度來看,能夠用 CSS 解決一定是比較好的。然而,盡量不要用 JS 不代表完全不用 JS,這兩者還是有些差別的。這篇文章會重新閱讀一次上面提到的文章,並且點出一些我覺得能夠改善的地方。

透過 :hover 做提示樣式

的確,透過 :hover來讓使用者知道這個 UI 元件是可互動的,這對前端工程師來說算是基本常識了。文章中還有提到可以透過 :hover 來達到下拉式選單的效果。

在 hover 時改變 display: block,一般狀況則是 display: none。這點看似沒有太大問題,但如果使用者不是使用滑鼠做導航呢?如果使用者透過鍵盤來導航,:hover是沒有效果的。而且也會侷限在 DOM 的架構裡,要觸發的 UI 元素需要跟下拉列表相鄰。

因此我的建議是在用 :hover 來提示使用者元素可以互動時,要考量如果使用者不是透過滑鼠來導航的話應該要如何互動。包含:

  • 額外加上 :focus 或是監聽 click 事件讓使用者觸發下拉式選單
  • 加入 aria-expanded 告知螢幕閱讀器目前選單的開啟狀態為何,並加入鍵盤導航讓使用者可以透過上下鍵控制選項

這個範例裡除了透過 hover 來觸發 Dropdown 之外,也搭配 JavaScript 監聽 focus 以及 mouseover 事件來調整 aria-expanded。鍵盤導航跟本篇主題不同因此沒有實作。額外一題,如果使用 aria-expanded 的話,也可以將 CSS 調整為:

.dropdown-item:hover + .item,
.item[aria-expanded="true"]
{
  /* style */
}

透過 :checked 與相鄰選擇器做客製化樣式

如果要實作客製化的 checkbox 或是 radio,應該會需要用到文章中提到的技巧,透過 pseudo class 加上相鄰選擇器方便實作客製化 checkbox。

使用 :checked 的好處在於我們不需要額外註冊事件監聽器去 toggle 類別,如果要做客製化的 checkbox 或是 radio 的話盡可能優先選擇此做法,而不是從頭用 <div> 做一個,不僅要考慮的東西很多之外,可用性也不一定比得上有點醜但至少能用的 checkbox。

不過用這種方式要注意幾點:

  • 使用 aria-label 讓螢幕閱讀器知道這個 checkbox 或 radio 的用途。(或是用 aria-labelledby
  • 使用 <div role="status"></div> 或其他方式來通知值的變化(如果需要的話)
  • 實作 focus 的樣式處理

螢幕閱讀器在閱讀 checkbox 時,只會將 label 名稱跟是否選取唸出來,如果 checkbox 的目的不是選取跟未選取的話(例如暗色主題的切換),另外加入提示會讓螢幕閱讀器較容易理解。

為了讓 input 隱藏但保有 focus 的效果,所以不直接用 display: none,而是使用其他 CSS 屬性把它隱藏起來。同時為了讓鍵盤導航時有 focus 的樣式,這邊加入了 :focus-visible,這樣一來只有在使用鍵盤導航(例如 tab)時才會套用規則,點擊的時候就不會看到 focus 的框框跑出來。

多列等高

這篇文章是在 2016 年撰寫的,裡頭講到的方法雖然可行,但有點 old-school,在 2022 年 flex 支援度已經很高的情況下我們可以直接使用 flexbox 來解決,如果要粒度更細一點的話則可以使用 grid。

原理是透過 flex 排版的特性,align-items 在預設情況下會是 stretch,容器的高度會以同一個 row 當中最高的為主。要注意的地方是如果需要多個 row 的話,記得加入 flex-wrap: wrap,不然預設情況下 flexbox 會試圖塞進同一個 row 裡頭。

表單提交

我很認同作者提到很多人都忽略了其實原生的表單 <form> 已經存在好幾十年了,透過 <form> 來定義表單內容可以省下大量的 JavaScript 程式碼。

裡頭有提到可以透過瀏覽器原生的表單驗證機制搭配 :invalid pseudo class 來做樣式處理,範例當中是在 invalid 狀態時將繳交按鈕設為 opacity: 0.5。不過應該是因為範例的關係,作者是使用 <span> 而非 <button>。實作上應該會使用 <button>,並透過 JavaScript 在輸入值不合法時加入 disabled

如果對 form 還不熟悉的話可以參考下面兩篇文章:

善用 pseudo class

作者有提到像是 :checked:focus:invalid 等等,善用這些偽類別可以省下不必要的 JavaScript,閱讀起來也會比較輕鬆。

關於 pseudo class 我也有寫一篇文章介紹一些相對比較新的 pseudo class,有興趣的話可以參考。排版時有用的 Pseudo 類別

結語

2017 年時恰好是自己剛入門前端的時期,對細節的處理掌握度不高。現在回頭審視一遍之後發現要實作一個體驗良好的 UI 需要考量到很多細節,不是單純去套用 CSS 就能夠完事的,很多時候為了無障礙設計的考量,JavaScript 往往是不可或缺的角色。

上一篇

排版時有用的 Pseudo 類別

下一篇

2022 Advent Of Code: Cathode-Ray Tube

如果覺得這篇文章對你有幫助的話,可以考慮到下面的連結請我喝一杯 ☕️ 可以讓我平凡的一天變得閃閃發光 ✨

Buy me a coffee