表單 Practice

Form Validation

這邊是我最近對於表單的一些作法,因為內化還不夠,每次都會漏掉一些,所以花了些時間整理整理,適合的情境不是 single page application 就是了,比較偏傳統形式網頁的表單,然後可能也包括不少大家早就知道(?)的細節就是了。

首先,我現在偏好不用 JavaScript 做表單檢查,而是先做好最基本的 server side 檢查,然後加上HTML5 的表單檢查,會這樣決定的主因是:

  1. JavaScript 的表單檢查 library 用起來都不太順手,而且不想花時間處理串接,且能少點 library 總是好的;
  2. 幾個常用的 type,像是 email, url 比較不需要擔心檢查的 pattern 有不周全的地方,我想很多人都有上網搜尋過這些欄位的 regular expression pettern 的經驗;
  3. 支援度已經不是大問題了,事實上我的工作上還需要支援 IE 10, 9 之類的,其實這些非 modern browser 的量都已經非常少了,所以就靠個 server side 檢查對付他們就好,使用體驗稍微差一點也還可以接受,這也是種 graceful degradation(優雅降級);
  4. HTML5 的表單檢查可以說是 web developer 當年對抗網路標準發展遲緩一大勝利指標,當然應該要好好用一下。

而用 HTML5 表單檢查還有個意外的好處是基本的錯誤訊息自動有翻譯(看使用者瀏覽器的語言),另外如果有自製的輸入元件,也有API可以串接,當然訊息就要自己提供就是了。

用 HTML5 表單檢查當然也不是完全沒有問題,例如目前 email 欄位還沒有瀏覽器支援IDN domain 的信箱;另外就是上傳檔案的 file input 的值不能從 server 端直接給,這限制是因為會有安全性問題,而這限制所衍生的問題是:表單送到 server side 檢查後發現有錯誤時(例如 captcha 錯),使用者就一定要重新選取上傳的檔案,對於使用者體驗算是個扣分(而且上傳檔案大的話很花時間,然後另外還有個上傳檔案大小限制、就又是另外一個議題了),要解決這問題一般來說就是靠 JavaScript 做些加強,例如針對 captcha 可以先用 ajax call 檢查 captcha ,正確的話就換個 session token 之類的回來,不過即使這樣,還是逃不了完整的 server side 表單檢查,所以也還要處理 ajax submit 後的表單錯誤訊息顯示。

不管是 server side 檢查後產生的錯誤訊息,還是 ajax call 之前檢查產生的錯誤訊息,理所當然都會放在欄位附近,不過還要讓訊息和欄位之間建立關聯,才好進一步做一些處理,例如使用者有更新欄位值之後會把錯誤訊息隱藏之類的,或許很多人會用父層 DOM 節點加上特殊的 class 包起來找,不過我比較偏好用aria-describedby,大概會看起像是:

<input id="mail" name="mail" type="email" aria-describedby="mail-field-info" />
<span id="mail-field-info">Required field!</span>

這樣只要找的到#mail欄位,就可以透過他的aria-describedby屬性找到該欄位的相關訊息的 DOM 節點,另外值得注意的是,aria-describedby 值的格式是IDRefList,不是單一個 ID,而是一個用空白切分的 ID 指標們,所以如果有這種情形,還可以在錯誤訊息的那個 DOM 節點加上role="alert"給它用來辨識,其實就算只有一個 ID 也還是可以加上 role 屬性啦。如果真的需要用透過父層 DOM 節點來找的話,之前研究的結論是可以在預期的父層標籤用role="section"來方便定位,用 jQuery 大概會像是:

$fieldSection = $field.closest('[role="section"]');

這個標籤下應該會包括欄位的標籤(label)、欄位的 input element 以及相關的資訊(說明、錯誤訊息)等。

另外還有一點,就是要用 ajax 上傳檔案的話,需要有支援FormData的瀏覽器,並且如果用 jQuery 送 FormData 的話記得要加點設定:

contentType: false,
processData: false

還有就是 ajax 送表單的目標 URL,我目前比較喜歡的作法是讀<form>action屬性,也就是和瀏覽器自己送的 URL 一樣,然後透過 HTTP content negotiation 機制來決定回傳的格式,比較正確的作法是看Accept,以 jQuery 來說,如果要 server 回 JSON 格式的話,可以加上:

dataType: 'json'

這樣送出的 request 就會帶上正確的Acceptheader,向 server 端要求application/json,不過Accept的值解析起來比較麻煩些,其實是可以送出說 client 端可以接受多種格式,然後還加上個優先度的,也因此也有很多人是看X-Requested-With,一般 library 如果是透過 XHR 發的 request 都會有這個 header;還有就是送出的資料格式(Content-Type),即使是 ajax call,我目前也都不用 JSON 了,還是用application/x-www-form-urlencoded為主,另外要上傳檔案的話當然一定要用multipart/form-data,主要是因為:

  1. 送 JSON 的話就不會是simple request了,有些時候會比較麻煩,例如 Cross Origin 時會需要發 preflight,然後就可能遇到 AWS 以前不支援 preflight request 的 bug;
  2. 用這幾個老的 Content-Type 支援度還是比較高,對於 server 端實做和 client 端實做其實都相對友善一點,例如 jQuery 預設就依然是 form-urlencoded,没特别需求還是用標準一點的格式,特殊需求是例如 GraphQL,不過一般表單發送應該不會走 GraphQL 吧。

其實 JSON 雖然已經有 RFC 規範了,不過在 Web 標準的世界還沒相當深入內化,不知道以後有沒有機會更加的內化整合進去。

前面有提到 ajax call 送出的目標 URL 我會偏好從,<form>裡面讀,不過或許有的情境會讓 ajax call 必須要自己用不一樣的 API URL,這時候我建議還是把 API URL 寫在<form>的屬性裡面,這樣可以讓 JavaScript 的邏輯比較乾淨,也不用作什麼 mapping 或是常數來儲存 API 的 URL,維護修改時也不用兩邊檢查,屬性名稱可以用例如:data-action之類的屬性,data-*屬性正好適合來做這些事情,不但有 DOM API 支援,jQuery 也可以用.data()method 來讀取,命名上,如果覺得有個標準參考比較好,可以看看jQuery-ujs的設計,雖然比較長一點,它用的是:data-ujs:submit-button-formaction,我是覺得有些不正確啦,畢竟要送出表單不一定是點擊 submit button。

其實假設送出表單的動作都是滑鼠點擊 submit button 這是個親和力問題,如果只把 ajax call 送的函式 bind 在 submit button 的 click 事件上,這其實是不太好的,因為其實瀏覽器預設的行為是可以在很多地方用鍵盤送出表單,例如在 text input 上按下 Enter 鍵,或是在 submit button 上按下空白鍵之類的,所以針對表單還是要去 bind form submit 事件才是正解,至於 jQuery-ujs,其實也是這樣做的,它是用 delegate event 的形式去監聽傳遞到 document 上的 submit 事件,然後才去做後續的處理,只是命名上讓人覺得不太正確。

最後一項,前面說不用 JavaScript 做表單檢查(不看自訂輸入元件的話),其實有一個例外,就是上傳檔案的大小檢查,因為沒做對使用體驗的影響比較大,然後就是要還要記得針對 ajax call 送表單加上HTTP 413Status Code 的錯誤訊息處理。


Graceful Degradation Background

Rockman

昨天解了一個其實困擾蠻久的問題,就是在蘋果開始支援 Retina Display(HiDPI)之後,網站上使用的圖片也跟著要提升解析度,一般網頁會使用的圖片基本上就是<img />標籤的圖片和用 CSS 設定的背景圖,對於<img />標籤來說,提供高解析度的圖片並不會有向下相容的問題,當然瀏覽器和作業系統不支援的,抓了比較大的圖下來是比較浪費,也為此有<picture>標籤、srcset屬性和Client Hints等標準來處理這個問題,不過我的需求只要圖片都能正常且正確的顯示。

<img />標籤只要有設定寬高,圖片顯示出來就會是預期的大小,但是background-image就不是了,在background-size出來前,背景圖多大,它在網頁上就會照那樣的尺寸下去畫,所以在不支援background-size的瀏覽器(IE6-8)上用 HiDPI 的背景圖就會很悲劇,雖然可以用 media query 來處理,不過解析度相關的 media query,如果要考慮到以前的瀏覽器,變化有點多,從早期的 device pixel ratio 到 dppx 到現在 resoulution 用的 dpi,還要加上 vendor prefix,自己寫起來有點痛苦,而且原始碼變醜很多,當然,如果有用 PostCSS 就可以用 autoprefixer 解決,不過其實我不太喜歡寫 Media Query,所以都是能少則少(針對 CSS 的 polyfill 也是不太喜歡用),並且有些舊的專案沒有 PostCSS,所以就一直有這個問題,以前的處理方法就是幾種:

  • 真的寫 Media Query
  • <img />標籤模擬背景圖,也很麻煩(也可以用 polyfill,不過個人不想用)
  • 大家一起用標準解析度的圖

這些方法對我來說都算是 workaround 的方式,一直想找個漂亮乾淨一點的解法,直到昨天終於想到了,首先整理一下我的需求:

  • 可以有兩張圖不同解析度的圖
  • 不要用 Media Query
  • 有機會支援 HiDPI 的環境就用 HiDPI 的圖
  • 舊瀏覽器顯示正確

其實這些需求可以簡化成:**不支援background-size的就乖乖看低解析度的圖片吧**,其中隱含著一個現實狀況是,不支援background-size的瀏覽器都只能在不支援 HiDPI 的環境下跑,實做上基本的構想是先寫好一個基本背景的定義,然後再加上一個只有新瀏覽器看的懂得定義,這構想有了很久,卻到昨天才想到怎麼寫,結果如下:

background: #123456 url('the-bg.png') center center;
background: #123456 url('the-bg@2x.png') center center/32px 32px;

用了background可以把全部背景相關屬性寫在一起,而且如果解析失敗就整條失效的特性,以前因為我都會background-size單獨寫(有另外的原因),這種寫法就落入我的盲區,所以過了這麼久才想到,不過這方法不是萬能,還有些限制和使用時機:

  • 有需要支援 IE6-8 才需要,IE9 以上就有支援background-size
  • 不考慮 HiDPI 時其實不需要background-size,例如固定大小區塊的背景、用背景的 icon、有重複排列的背景等
  • background-size: cover;依然需要靠 polyfill 之類的來處理

想出來之後,有開了 IE8 來測試,結果一如預期,這樣應該是 IE6-7 也會正確吧。


Acclerated Mobile Pages

O2 DAC + AMP,

Acclerated Mobile Pages簡稱 AMP,是 Google 所推出,為了提升行動網路體驗的一個專案,我一開始對於這種(看似)偏離網路標準的方案其實不太有興趣,不過在瞭解其技術原理後,覺得相當有趣,而且其實沒有想像中的偏離標準,整個架構也比 Facebook 的 Instant Article 還要來的開放,雖然我覺得這個解決方案(只說是 Framework 或是 Library 都不夠完整),其實只算是個暫時的解法,但是 AMP 本身的實做方式,其實是架構在一堆網路標準的發展之上的,相當出人意料,可以說是Extensible Web 宣言以來,第一個重要的里程碑。

閱讀「Acclerated Mobile Pages」全文

Modern HTML Email Development

今天在 Modern Web 分享的主題

其實最主要是想介紹MJML這個工具,不過最後介紹的篇幅有些不夠,有些可惜,話說今天設備也有些狀況,一是投影機解析度和預期的不一樣,二是無線麥克風聲音會延遲,對於講者來說還蠻干擾的,最後時間還剩的比預期多,覺得愧疚啊。

最後附上這次介紹的一些資源的連結,方便取用:


Pure.css

最近做的修改還有一個就是把Bootstrap換掉,因為我覺得它實在是太大了,包進很多我沒用到的東西,即使是壓縮過的版本也要上百 KB,其實我只需要一部份的元件,像是圖片、引言、程式碼和 grid layout 等,但是這些部分的 CSS 所佔的比例其實超級少,所以我決定找一個新的 framework 把它換掉,結果我挑的是 Yahoo 已經沒有繼續更新的專案Pure.css

Pure.css 檔案大小比起 Bootstrap 實在小很多,最小化過的版本只有約 17KB,不過 responsive grid layout 的部分是獨立的,該模組的大小約是 9.57KB,和原來使用 Bootstrap 相比,總共大概是只有原來的四分之一(當然 gzip 後沒差這麼多),不過檔案小這麼多一定有所犧牲,Pure.css 提供的元件比起 Bootstrap 來少很多,而且提供的我幾乎都沒用到,最後我只用到 menu 和 grid layout 兩個部分而已,所以又從 Bootstrap 那邊把我要的元件手工複製出來使用,最後整理出來的 CSS 是 36.15KB,gzip 後是 7.3KB,brotli 壓縮過後是 6.34KB,傳輸前的資料量和原來相比大約是三分之一至四分之一,使用 gzip 壓縮傳輸的資料量則是三分之一左右(相較於原本的 18.97KB),整體來說成效不錯,其實 Framework 的部分還是佔了很大的比例,所以下一步就是把整個 CSS Framework 拿掉了,我推估實際上我需要的這些 CSS 大概 3KB 的資料量就夠了。

不過 Pure.css 是個已經沒在維護的專案了,Github 上已經超過一年沒有動靜,再加它其實沒 Bootstrap 那樣簡單用,有很多細節都還是要自己處理,就連它們首頁應該都是加工不少才出來的,所以要用這套 CSS Framework 的人還是要三思啊。


Device Pixel Ratio Header

之前文章有介紹過 DPR(Device Pixel Ratio),不過當時只能透過 JavaScript 在 client 端存取,如果 server 端要知道 client 端的 DPR 至少得要來回交換一次資訊,在那個時候就有想過應該之後會有新的標準來負責處理這個問題,而最近終於看到標準的草案了,叫做HTTP Client Hints,照字面的意思,這個 HTTP 擴充標準不是只有 DPR,而是提供 client 端的一些資訊,目前包括了:

  • DPR
  • width
  • viewport-width
  • downlink

其中DPR就是 device pixel ratio;viewport-width就和以前介紹過的 viewport width 一樣;width比較特別,是實際希望的 resource 寬度,而不是真的屬於 device 的資訊,例如圖片在網頁內是 160px,裝置的 DPR 是 2,width就是 160×2 = 320px;最後一個downlink則是以前一直很難取得的網路速度,以前最大的限制在於不管是 client 端還是 server 端都很難取得實際的網路速度,雖然可以用 JavaScript 下載檔案算時間來取得素質,但是這樣的作法其實有個兩難的問題,一方面問題是如果檔案大小,那取得的數字會很不準確,另一方面問題是如果檔案太大,那必然就是浪費時間和網路頻寬,更不用說很多地方沒有吃到飽方案可以用,這樣的作法應該不會太受歡迎,甚至如果不測速度,搞不好都已經把網頁讀好了。現在把這個資訊的提供者更往上一層,改成由瀏覽器提供,瀏覽器就可以跟作業系統作溝通,就比較有機會不用先測速就拿到可以參考的網路速度。這些 Header 傳輸的時候都不用附上單位,只有數字,其中 downlink 的單位是 Mbps,而規範所希望瀏覽器提供的 downlink 數字是參考Network Information API這份 API 裡面整理的,主要連線方法的理論速度上限,例如 GSM 就是 0.01、LTE 是 100﹑wifi 802.11g 是 54,本來是想說這個數字是看瀏覽器自己決定,不只根據連線的方式,還會看實際的傳輸數字或是使用者偏好,例如他可能使用 LTE 但是因為不是吃到飽,所以希望把速度報低一點之類的。

Downlink 其實會比預期的還要有用,除了可以根據網路速度決定回傳的圖片大小外,還可以根據 downlink 大小來決定要回傳的 CSS,實際上使用可能像是,網路慢的時候就給出比較輕量的 CSS 回去給 client 端,用不一樣的版面呈現給使用者,有點像是 Google 的Accelerated Mobile Pages Project所做的。

Client hint header 是個需要 opt-in 的擴充功能,就是預設是沒開的,那要怎麼開啟呢,就是在網頁 document 的 header 加上Accept-CH

Accept-CH: "DPR, Downlink"

或是加上 meta tag

<meta http-equiv="Accept-CH" content="DPR, Width, Viewport-Width">

其中 equiv 是 equivalent 意思,就是和 HTTP header 等效的意思,透過這個 header 跟瀏覽器說 server 端可以接收 client hint 資訊,然後支援的瀏覽器就會在後續,這個網頁所需要用到的其它資源,像是 js, css, 圖片等等的 request 都加上這些資訊,這個溝通過程其實就是使用已經很久的機制:HTTP content negotiation,用的 header 也是Accept開頭的,目前已經支援的還只有 Chrome,而且要 46 版以後,其他幾個瀏覽器大多都還在討論要不要支援,Firefox 是有一些進度

這樣的設計其實有點先進,不完全是褒意,因為這意味著要 server 端,負責提供圖片的服務器,收到 request 後去看 DPR 或是 width 之類的資訊再來挑選要回傳哪一張圖片回去,而通常提供圖片靜態檔案的服務都不太有邏輯處理,大部分就是簡單的 HTTP server,只看靜態檔案,有就傳檔案回去,沒有就回傳 404,相信之後是會有些 module 或是設定的方法讓 HTTP server 可以把這部分直接處理掉,不過一些規模比較大的網站,如果有用上 CDN 的,可能就沒法直接用上這功能,還要等看有沒有 CDN 廠商支援。

Client hint 目前看來會是 responsive image 未來的一個主要解法,和 media query 相比其實有兩個很大的差異,第一個是 media query 是馬上(且隨時)發生效果,和 HTTP request 是無關的,所以一開始下載下來的網頁就會發生效果,但是 client hint 要透過第一個 HTML document 本身的 request 進行溝通,意味著 HTML document 本身的 request 是不會收到 client hint。第二個就是 media query 是隨時發生作用的,即使在網頁完整讀取下來後,還是有可能因為使用者造成的 client 環境變化,而讓瀏覽器去多下載一份檔案,最常見的情形就是調整視窗大小,在手機上可能就是轉直轉橫造成 viewport 寬度變化,甚至在 Mac 如果外接普通螢幕,拖拉瀏覽器視窗到不同螢幕也會造成影響,如果這些變化剛好符合設定的 media query,DPR 從 1 變成 2,就會讓瀏覽器又去下載了一張 DPR 不同的圖片,這類狀況在網路頻寬有限的環境下實在不是好事,而用 client hint header 的話就完全不會有這種狀況了,就只會有一開始帶著 Client Hint 去抓下來的那個檔案。

不過 Client Hint 也不是完全沒有副作用,目前最大的問題就是這幾個新 header 都不列在simple headers裡面,所以在做 CORS request 的時候,本來不用 preflight request 的都會變成需要 preflight request 了,這個問題其實現在還在討論,因為其實有很多類似的新 header 都會造成一樣的問題,像是 CSP、Server-Sent Events 裡面的 Event-ID,主要是這麼多 Header 進去,就不 simple 了,而且照最近標準發展的狀況來看,這份 simple headers 的清單很難固定不變,有興趣的可以跟一下討論


srcset

Responsive Image 大概定案成srcset<picture>都有了,src-N 已經消失,雖然我還蠻喜歡,不過總之最近發現srcset和我當初介紹時已經差蠻多,中文資源有找到 Zhusee 有另外一篇介紹,不過其實我去看現在的 spec 的時候發現,又有些修改了!最早 srcset 後面是用類似 media query 的設計,後來改成對圖片的 metadata,spec 裡面稱為 descriptor,分別有 width descriptor 用w和 density descriptor 用x,而且限制 srcset 裡面只能用同一種 descriptor,例如全部用x或是全部用w,所以:

  1. 不能在一張圖片裡面同時有wx
  2. 全部都用w或是全部都用x
  3. 不可有相同的數值,例如兩個1x或是兩個760w
  4. Descriptor 可以算是該圖片的資訊
閱讀「srcset」全文

Resource Hints

Resource Hints是最近剛公開的一個 W3C Working Draft,目前看作者都是 Google 的人,Resource Hints 是依附在<link>標籤或是 header 下的,用來告訴瀏覽器有哪些資源可以先讀好的一個慣例語法,範例如下:

<link rel="preconnect" href="//example.com">
<link rel="preconnect" href="//cdn.example.com">
<link rel="preload" href="/assets/font.woff" as="font" loadpolicy="next">
<link rel="preload" href="/assets/logo.webp" as="image" loadpolicy="next inert">

可以發現有兩種 hint,分別是preconnectpreload,當然就照字面上的意思就可以理解了,preconnect就是先建立好連線,像是 HTTPS 連線就需要先做 handshake 交換好加解密用的 key,影響比較直接,preload當然就是先把特定的檔案拉下來,而且還可以指定要不要先做處理,像是 decode jpg 圖檔之類的動作可以推遲到真的需要圖片時才做。

目前看起來是不適合大量的檔案,應該還是要用來指派較關鍵的一些檔案,像是主要背景、大圖、字形之類的,而且因為可以根據設定 block onload event,拿來讀字形似乎可以避免 FOUT 的狀況,感覺是個看似簡單,但是功能其實還蠻多的新東西。


RGBA_ 控字慾

昨天去參加了 RGBA 很久沒辦的活動,這次主題是字體,剛好是我很有興趣的議題,所以從一開始就很想參與,結果報名時竟然忘記,而且還連續兩次都忘記了,想不到活動當天 hlb 沒辦法過去,我就順利的接手他的票了,然後覺得應該分享一下,所以就有了這篇文章。

RGBA_ 控字慾

一開始是由 Jason 的手寫西文毛筆字開始,我有拍些照片也錄了一段影片:

一直覺得寫這種字體用的筆很有趣,根本就是一片鐵片,要控制的好讓出墨平均應該需要不少練習吧,

接著就是 Even 介紹他挑選字形的方法,因為他基本上都是挑選黑體來用比較多,不過黑體也分很多種,所以先介紹了不同黑體的差異,基本上就是黑體字並不是所有都侷限現在框框內的,筆劃也不是都方方正正的,例如蒙納黑和 Arial Unicode MS 的底就不平,所以字體本身可能是有點歪斜的。

喇叭口

另外一種變化可以稱為喇叭口,也就是上圖那樣,在筆劃末端會放大,這種黑體比較適合小字使用,喇叭口可以讓視覺上感覺筆會比較粗,但是又不會影響整體的灰度,例如 Hiragino 這個 OSX 內建的日文字體。如果不要喇叭口的話可以選華康黑,華康黑又有一個特別的地方是他的一些筆劃是有裝飾的,我本來以為黑體就是不會有這些裝飾元素的,真是沒想到啊。最後講到信黑體,信黑體就是非常標準,文字侷限在方框內,灰度平均,沒有裝飾的黑體。然後 Even 還有用fontshop這個網站和Sketch示範一下不同字重的搭配。

RGBA_ 控字慾

接下來聽的是 but 介紹 OpenType 特殊功能和他做的一些實驗,OpenType 的規範有定義一些在西文排版上已經存在很久的功能,例如連字 Ligatures。

Ligatures

OpenType 有的功能時很很多,微軟有份文件有列出,據 but 說,OpenType 是微軟Adobe合作產出的,我後來搜尋一些相關資料時發現微軟有不少文件,可惜 IE 對於 OpenType feature 的支援卻落後不少,Firefox 和 Chrome 都已經支援一陣子了,雖然目前都還算是實驗中的功能, but 有用這些功能和他自己製作的字形弄了兩個實驗網站,兩個其實都是用連字辦到的,第一個是完全用文字把日本的火車時刻表做出來,這樣做除了畫面呈現上可以和紙本一樣,SEO 也會表現的比插入 icon 來的好,當然親和力也自動跟著提昇,第二個則是注音字形,可以邊打拼音邊顯示你輸入的注音符號,然後另外還做一個反向的,可以打注音拼音,兩個 demo 都蠻有趣的,而且可以在頁面上玩玩看連字。

另外因為有提到 emoji,因為 emoji 是彩色的,目前各家實做不一樣,蘋果是塞圖進字形、Firefox 我忘了,然後微軟是用多個圖層來處理多色的問題,所以我問了一下去年看過的 SVG in OpenType 的相關資訊,可惜 but 沒接觸過,後來我去搜尋看看到底這神秘的東西是誰推出來的,因為把 SVG 放進去字形裡面不是單純只是向量的彩色圖片,所有 SVG 可以做的事情都可以辦到,例如動畫之類的。總之發現是 Mozilla 開始這計畫的,在 W3C 有community,Adobe 的網站也有介紹過,應該不會是個孤兒格式才是,目前好像轉轉進到 MPEG 組織下維護了,Open Type 有個很神秘的地方是它現在是由 MPEG 組織來維護,屬於MPEG-4檔案格式的一部分,真是令不解的狀況啊~

最後比較可惜的是 Vertical Rhyme 沒聽到,不知道之後會不會放出投影片(but 的投影片也想要啊),主要想聽的主題就差那個而已,有一些特別去聽的就是之前已經在別的場合聽過了,整體來說這次收穫算是很多的,最後還是要感謝一下辦活動的 Even 和突然沒辦法去的布丁了,然後就看一下當天的盛況吧。

RGBA_ 控字慾

RGBA_ 控字慾

RGBA_ 控字慾


object-fit

在一些情境下,網站的圖片來源可能無法和版面很契合,例如新聞網站,新聞的照片可能有直的橫的,甚至有不同的寬高比,但是網站的版面配置不可能配合所有的可能性,如果想要有個封面故事,又要個滿版的照片,又或者是提供給 Facebook 的照片,它的縮圖都是正方形的,但是大部分的時候,文章的照片都不會是這種比例,最理想是有個 server 端的程式可以幫忙把圖片轉成想要的大小,例如 Facebook 其實是有個程式來作這件事,包括調整大小、重新壓縮、快取,大概連雲端分散式儲存的部份等等問題都一口氣處理掉了。

如果要純前端處理,其實目前最好用的作法是用CSS3 Backgroundbackground-size: cover;,這個樣式會讓標籤的背景圖調整成剛好可以填滿元素大小的程度,當然也考慮好寬高比了,這個方法最主要的缺點在於把圖片從 HTML 文件中抽掉了,在語意上不太好,像是搜尋引擎之類的,對於<img>和 CSS 背景的處理應該還是會有差異的,例如 Google 圖片搜尋我就沒印象有找到 CSS 的背景圖過。

要維持<img>標籤的存在,又要不管大小和寬高比都可以滿版,在現在是只能用 JavaScript 來輔助,實際上的邏輯也不會太複雜,其實就是比較一下版面的寬高比和圖片的寬高比,然後決定用外框的寬還是高當基準,接著維持圖片的寬高比縮放到計算的大小,定位到讓圖片置中,寫成 jQuery plugin 大概像是下面這樣:

$.fn.cover = (selector) ->
  $(@).each ->
    $outer = $(@)
    ow = $outer.width()
    oh = $outer.height()
    or = ow / oh
    $outer.find(selector).each ->
      $item = $(@)
      iw = $item.width()
      ih = $item.height()
      ir = iw / ih
      if ir < or
        w = ow
        h = ow / ir
        l = 0
        t = (oh - h) / 2
      else
        h = oh
        w = oh * ir
        t = 0
        l = (ow - w) / 2

      $item.css(width: w, height: h, top: t, left: l)

使用範例:

$('.cell').cover('img')

不過實際上還要考慮的問題不少,像是執行的時間點,上面這個範例可以運作的時間不只要圖片讀好,有正確的寬高之外,.cell或是說$outer也要在頁面上顯示,有 render 過,才能夠取得它的寬高,整個函式才能夠正確的運作,結果就是訪客其實會看到一瞬間圖片調整好大小位置前的樣子。

用 JavaScript 加上<img>這個方案的缺點除了上面說的之外,還有一個是會需要多一層的標籤,不過目前還是很多人會使用這個方案,也有不少 Library 在處理這個問題,像是fit.js

這個問題,其實在未來就不存在了,CSS3 Image 裡面有個新的樣式定義就是為了處理這個問題,叫做object-fit,可以想像成讓<img>標籤可以用 background-image 的方式來操控裡面的圖面了,而對應background-size的,就是object-fit了,未來可以這樣寫 HTML:

<img src="http://blah.com/blah.png" class="cover" width="300" height="300" />

配上 CSS:

img.cover {
  object-fit: cover;
}

就可以同時達到不用多一層標籤,又有實際的<img>,加上不用 JavaScript,不用考慮 resize 圖片的時機,非常的完美,不過這個新標準目前實作的瀏覽器不多,只有 Chrome 和換成 WebKit 前的 Opera,Opera 的 blog 也有發表過一篇文章介紹這個新屬性:CSS3 Object Fit Object Position,裡面也有不少範例可以用 Chrome 開來試試看。

要說有什麼缺點的話,其實還是有的,就是這個 object-fit 不是排版用的,只能用在圖片上,不像 JavaScript library 基本上是什麼元素都可以調整,沒有受限。


此類別所有文章