前情提要
如果有印象的話,之前在某篇介紹Button時,曾經簡單的講解過form submit的概念。但人參總是那麼複雜(User總是那麼難搞),如果是唯讀欄位卻又想送回後台呢…?
問題情境
某天被隔壁的小姐姐呼喊了,某個select欄位的值在後台不知道為什麼都接不到。看了之後發現,那是一個唯讀的select(下拉選項)。
在解決這個問題前,我們先來聊聊資安這件事。
只是想唯讀,有需要講資安那麼嚴重嗎?
大家都知道(?)網頁的前端並不是安全的。即使設定為唯讀,都還是有被修改的可能,據我淺薄的前端觀念,使用label會比較安全。
但有時人在屋簷下,不得不低頭,如果美編就是弄了個唯讀的select,甚至更多的唯讀文字方塊給你,主管覺得你神經敏感、大驚小怪…那說不得也是得照著做。(我不知道小姐姐有沒有那麼悲慘,總之看到的時候她已經在做了。) 可能真的有些情境會用到select唯讀吧,有人要分享嗎?XD
那先講講文字方塊(textbox)的唯讀吧
如果在Google上搜尋「HTML 文字方塊 唯讀」,會先看到的結果應該都是 readonly ,從字面上解讀確實如此,也的確可以解決文字方塊Textbox唯讀的需求。
只是如果再繼續深入看下去,應該會發現有一個類似的關鍵字 : disabled。
以我自己而言,readonly 和 disables 可區分成三個層面的差異。
- 1.視覺呈現
在沒有額外css的狀況下,readonly和一般的文字方塊幾乎一樣。只是readonly的文字方塊即使點到滑鼠爆掉裡面的文字也不會改變XD。
而disabled略為反灰,比較直覺性的讓User知道無法更改。(但這是個有css的時代,蛋糕上面不但要有鮮奶油還要畫花畫朵)
- 2.操作差異
如果網頁上的資訊需要另外擷取處理,熟練的User在操作時會習慣點按Tab做切換的動作,避免在鍵盤與滑鼠間交替而耗費時間。
readonly的欄位會提供Tab停留,而如果是disabled的欄位,則會被直接跳過。(逼User拿起滑鼠嗎? 真不友善XD)
- 3.前端事件
系統越大越複雜,有時難免又要體貼使用者( 各種防呆)。
前端事件多如繁星,沒有前端一起分擔的全端工程師只能哭哭。如果剛好textbox有作事件處理,不管是click或是blur,那麼readonly也會一起被影響,但事實上或許不需要(例如說跳出欄位的檢查,但readonly的值不會變動,根本不需要檢查)。
相對來說disabled根本在頁面上完全裝死,就不需要擔心~
- 4.後台處理
理論上來說,既然欄位唯讀了,那麼這項資料後台一定取得到,不需要傳回後台處理。
但不幸的是,有些狀況下,你還是會希望在後台接收到唯讀欄位的資訊,而如果更不幸的,你是個全端工程師,那麼這有時可能會讓你好一陣打架並且困惑不已 - 使用disabled的欄位,其數值不會被傳回後台。
所以在文字方塊的狀況下,唯讀要使用readonly或是disabled,還是取決於後台是否需要接收其值 (對UI & UX 可能又有另外的考量,改天找友人問問看再分享~) 。
來說說readonly吧~
Readonly屬性是一個布林屬性,可以防止使用者對值進行修改,規定輸入欄位是唯讀的。
欄位被設置唯讀時無法修改,但使用者仍可以tab切換到該欄位選取或拷貝文字,亦可被提交。
那麼disabled呢?
Disabled屬性是一個布林屬性,規定應該禁用的input元素。被禁用的元素無法使用或點選(tab也進不去),且不會被提交。
除了文字方塊外,disabled還可以設置在許多其他不同的元素上,包含 : button、fieldset、input、optgroup、option、select、textarea。
另外有看到「disabled屬性不適用於<input type=”hidden”>」。但實際測試後,hidden可設置disabled,其結果一樣該值不會送回後台。(只是這樣的作法有點匪夷所思,又藏在前端又不送回後端XD)
回到正題
如果大家還記得,最一開始的問題是「唯讀的select,且需要傳值回後台」。
對兩個屬性有點基本認知後,從上面的資訊其實可以看出答案了 : 「select沒有readonly,可設置disabled但就無法送回後台」…欸嘿嘿,沒有概念的話真的會好陣子打架,誰知道加了個disabled就此前台與後台海闊天空呢?(小姐姐檢查了好久的ajax呢)
此情境可以採用的作法有四種,如果有其他作法也歡迎大家分享~
- 1.設置disabled開關。在每次頁面提交(submit)前,移除disabled屬性。(看起來是大家比較偏好的作法)$('input[name="s3"]').prop('disabled',false);
- 2.隱形的文字方塊。每次select change時,將其值置入隱藏的文字方塊。(也不少人使用!但稍微麻煩點,還要考慮到name)
- 3.關閉其他選項。這個是從Stack Overflow看到的方法,以前沒有用過,但真的滿神的。透過把其他未選擇的選項disabled,同時達成唯讀與傳值的效果。
但部分瀏覽器早期似乎不支援此做法(option無法為disabled),經測試目前win 10的IE、Edge及Chrome(版本 88.0.4324.150)皆可使用。$('#s1 option').not(':selected').prop('disabled',true) - 4.設定class。其實這沒有跳脫第1、2點,只是非處理單一頁面,強調統一處理。之前處理的系統有遇過類似狀況,所以將「在前台因為特定原因,無法將資料送至後台的欄位」設定共同的class,不管是產生隱藏的文字方塊塞值,或是在提交前取消disabled。好處是不用在各個頁面處理,寫好後只要記得設定class即可。那為什麼要額外設定class,不能直接移除所有disabled就好嗎?畢竟有些欄位會真的是 forever disabled,為了避免需求異動造成其他問題,且較好管理,就另外用class設定了~
disabled 或 disabled=”disabled”?
數個經手的網站,這兩種寫法都有看過。前者簡潔、後者清楚,且都可運作。
那實際上應該要採用哪種比較好?
在HTML寫disabled,但XHTML要寫disabled = “disabled”
就看專案的類型了
readonly、disabled與reset
在User輸入完資料後,不論將該欄位readonly或disabled,看起來似乎就保全了資料,萬無一失了。
但如果在表單中有reset的狀況下,雖然可保有欄位唯讀的效果,但User輸入的資料,還是會被還原到原始的狀態(空值或預設文字),需要注意。
結語
本篇大多是概念性的東西,所以貼Code部分不多,就不逐行說明示範。
如果有問題,歡迎留言告知,到時再詳細補上~
其實對我自己來說,用 readonly 或 disalbed 並沒有很明顯的差異。因為就是得照頁面設計走,select如果真的傳不回去等等的問題,因應辦法有很多,只是"很麻煩"還是"有點麻煩"的差別
不過概念還是推薦要弄懂,才不會發生卡了很久,但只是為了一個屬性的差異~
( 這次嘗試改了編排的方式,有看起來比較好嗎? )
參考資料
HTML <input> readonly Attribute
[JavaScript]設定Select、Input標籤,只可讀不可修改且可傳值
how to set select element as readonly ('disabled' doesnt pass select value on server)