autocomplete-2
接續上篇,這次會從需求和功能設計推進。
前情提要
- 基本的架構完全可以從現在很多人關注、或者認為好用的 pluins 下手。
我們已知的基本 autocomplete layout 如下:
- 有候選的情況
INPUT |
---|
HEADER |
option(clickable) |
option(clickable) |
option(disabled) |
option(clickable) |
option(clickable) |
FOOTER |
- 未產生候選(錯誤或等待)
INPUT |
---|
HEADER |
some message |
FOOTER |
所以、可拆成
- input
- option
之後就會圍繞著這兩個 component 討論。
Option
從相對單純的 Option
開始。
根據我們的需求,option 其實只需要單純的… 可以自訂template + click event
好像就可以結束了,也許會有人說『那 message 呢?』 『我的 key 還要 highlight』『尼上一篇說的 tabs 咧?』『還有 header 、 footer』,尼騙人、哪有那摸單純!
自訂 template
好的、當我們放鬆點,不要這摸認真的把因為樣貌的關係而造成我們有特殊觀感的元件視為特別的 component 的話。那、不論是 header
、 footer
、 message
或者 tabs
甚至其他超展開的元件,都是放在 option
區域啊~ 這樣的話、為什摸不能視他們為一種 option 呢?只是差在樣貌不同、跟 click event 有所差異。(當然、想特化也是沒有問題啦!但我認為沒有特化也是沒問題的。前提當然是要提供自訂的 template)
Click event
click event 為什摸抽出來呢?這是因為、的確會有這個 option 能不能 按下
的需求,撇開我們是不是將 header
等等特殊元件視為 Option
,一個正常的 option 的確有可能因其他的原因不能按(比如、這 option 即將失效?)
但是、無論能不能按下,或者按下之後要幹麻… 我認為都不是這 option 可以決定的,頂多、是透過定義的時候,多訂個 onClick
的 option function 然後,綁定在 click event 上,讓他知道該做什摸事。
Highlight
說了這摸多、都沒有提到 highlight
, 也許會很奇怪 highlight 應該很難吧?他牽扯那摸廣、還需要 input ,哪有這摸單純?
但… 他其實真的很單純。
基本上、我是把他歸類在 自訂 template
那部分,為什摸呢?顯然、 highlight 的 class-name,或者這樣的 component 的 layout ,絕對不可能一個打全部,一定會有各式各樣的超展開,所以、我不認為他是需要額外要抽出來在 Option
的其他區塊解決的。
可是!為了達到這目地,我們必須在規劃 option 的時候,把 input 的 value 帶進來,然後再 customer template 裡取得 input 的 value,這樣、就可以解決這題啦~ 而且、 Option
可以很單純,基本上只處理 template 和 event。
Option 結論
開發這 component 需要注意以下幾點:
- customer & default template
- Get input value
- click event
- clickable or not
Input
比較難的其實是這個,也許、大家又開始困惑啦~
『Input?他除了 onChange 還有什摸特別的?』
『他又沒有 customer template』
『不就是 onchange + 要讓 option 顯示嗎?』
是的、他的確除了 onChange
以外,好像沒有什摸特別的。但是根據我們常見的需求,可能會有 tagsinput
, 對於一個輸入法是需要切換的語言,常見的幾個 autocomplete plugins 其實操作起來不順暢。再加上、 mobile 和 desktop 的操作習慣明顯不同。還有 static 或者透過 request 取得 options 的銜接不同。其實… Input
的細節會比 Option
要來得多。
Static or Dyanmic Options
他們的差異,其實就是 options 是不是已經定義好的,就只有這些 options 。總共 10 個 options 的話,那輸入超過這 10 種可能性就也沒有了。這是一般 plugin 都會具備的功能,也是 demo 一定都會做的。所以、這部分就不多提了。
把主力放在 dyanmic。
一般操作,其實廣泛地運用上,還是會希望輸入各種奇怪的東西後, option 也隨著改變,而顯然… 不管是哪種 devise 都不可能放上一個資料庫… 吧~||| 所以、理想狀況就是透過打 api 取得候選字組。這時候問題就來了….
1 秒鐘如果輸入 10 個字,打 10 次 api?
那… 100 個人再用, 1 秒鐘就… 1000 次?
那… 如果是很不錯的服務,天阿!那不就要被打爆了?
扣掉會不會被打爆的問題、另外衍生的問題還有…
發了 10 次 request,問題是、回來的時間不定,尼知道尼要的是哪個嗎?
就算知道尼想辦法知道尼要的是哪個、10 個裡、我只需要 1 個,然後 9 個都是要丟掉的、尼知道… js 在建立 request,發出之後需要消耗的時間和資源嗎~?而這不只造成了 sever 的負擔,同時也會造成 rerender 的時候的不順暢。
這多可惜啊~ 也許、我們不能很完美的只發出那個需要的 request,但、我們絕對可以想辦法減少這樣的事情發生。所以、我們有兩個方案可以選擇 throttle
或者 debounce
。
Set throttle or debounce
有點陌生的話,可以參考這篇 ,順手貼過來的、網路上有更多的參考資料。
這兩種方案、也很難說哪個好、哪個不好啦~ 我是認為順暢的話就都很好。至於最困難(?)的時間該怎摸設定,這部分我其實是經驗判斷啦~ T UT
根據分析(我忘了是在哪看到的),一般人感受到的 慢
、 卡卡的
或者 不順暢
,大約是 300ms
。所以、那加上 requeset 需要的時間,我認為 200~250ms
甚至不想改變用 300ms
都還蠻合理的、要上調到 350ms
其實說不定也不是不行歐。如果很厲害可以知道目前的網速,甚至要再更精確地調整這時間我覺得都很好。
Tagsinput
如同上篇所說,我不認為一個 input 疊上一層 div 做某些特殊化的 render ,然後馬上輸入什摸、就顯示什摸是個正常的 input。也許大家覺得很美觀(?),但我無法接受。有幾個原因:
- 一個 input(或 textarea) 如果考慮到開發出的 component 能不能 reuse 等等。還會有以下的情況。
- 我要固定大小。
- 我要可以變高、變矮、變長、變短。
- 我要輸入的時候就可以知道各種長相(比如、不只 tags,如果是 username?)
- desktop 和 mobile 的操作行為不同。(這下下面會認真提)
以上的原因、當認真思考的時候,會發現、 input + div
這種組合根本就只想要翻白眼而已啊!!!!!!! 不管對於哪方面的工程師(開發實際功能與樣式)來說,這都是災難。
因此、在觀察過很多人的框框後,我們採用了 pinterest 的做法。
也就是在 multiple input 的情況,focus 是 input,blur 的時候會是 tags。
輸入法問題
輸入法、這大概是開發時候,最讓人頭痛的事情了~,絕大多數的 plugin 並不會想到這件事,因為幾乎都是英文嘛~ 也不容易找到相關的資料。後來、解決的方法,我得說、這應該是運氣好,發現的吧!
關鍵在 selectionStart
原本以為 selectionStart
不過就是只能找到開始的位置,或者在 slect 的狀態才有用,沒想到、意外發現… 在切換輸入法輸入的時候、 selectionStart
可以知道在切換之後輸入到一半的 value,真正是意外之喜啊~
所以、搭配 selectionStart
、 selectionEnd
還有 delimiter
就可以解決 輸入法 + tagsinput 的問題啦~
Desktop vs Mobile
這兩者在輸入的行為上,有非常大的差異…
Mobile | Desktop | |
---|---|---|
修改情況 | 幾乎不修改 | 可能移動從中插入或刪除 |
option操作 | 點擊後直接到頁面 | 點擊後更新 input |
上下 | 沒有 | focus option |
mouseenter(leave) | 沒有 | focus option |
左右 | 沒有 | 在 input 裡可以移動 |
enter | 沒有 | 可選擇 option 或 submit |
顯然、 mobile 的操作可能相對的單純,在輸入的情況下、很少移動位置,與其移動位置,不然刪掉重來(?) 而這也造就了、他似乎不需要像 desktop 的這種 autocomplete ,他真正需要的應該是串出正確的 url 、並且 direct 。
desktop 就複雜了,扣掉很多的 event , 行為也瑣碎的多了,平常操作,就常常會移動位置更新輸入的資料, autocomplete 也的確比較像我們認知的行為,而且、從上下移動到 click 都是需要的。如果這個 input 是包在 form 裡面、更需要考慮 enter 是否可觸發 submit 事件,另外還需要考慮輸入法正在輸入的時候按下 enter
的事件是否會造成影響。(整個案情就不單純啊!!!!)
Input 結論
這邊分兩部分結論
- Mobile
- event
- focus:非必要,但可以透過這 event 更改 focus 後要調整哪些顯示。
- blur:非必要,同上。
- change:必要,並且需要串接
option
,組出正確的 link。 render option 需要注意highlight
。
- event
- Desktop
- event
- focus:非必要,但在 multiple (也就是 tagsinput) 的情況下,可以透過它來切換樣式。
- blur:非必要,同上。
- change:必要, render option 需要注意
highlight
。 - keydown:
- 上下:focus option。
- enter:change input or submit,需要特別注意 multiple 情況下,更新字詞的位置是否正確。
- caret:用於處理 multiple 與輸入法切換輸入的情況。
- multiple 的情況,blur 後,需要特別處理 delimiter 重複的情形。
- event
總結
運氣蠻好的是,這次是用 React 開發的,對於 redering 和 event 更好掌握。
而當 Input 和 Option 這兩個 component 分開開發後,會發現雖然麻煩,但相對的單純、而且很多地方變得很有彈性,即使是 mobile 和 desktop 這兩個完全迥異的行為,其實、都可以共用這兩個 component,差別只在 Input 和 Option 結合起來的時候,在另外處理,但這樣的處理方式、我認為乾淨多了、而且更有彈性。
最有趣、也最不討喜的地方在… input 的 caret,當時死馬當活馬醫、沒想到就突破了。
最麻煩的地方其實是,兩個 component 分開的時候,其實蠻好開發的,比較難的地方是為了銜接。為了銜接好 request 和 onChange 之後該做的事情,的確下了番工夫。
有點痛苦的是,在這次處理,mobile 在 focus 後必須彈出 tabs 並且可以操作。因為會觸發 blur,options 會收起來,又費了一番工夫。
最讓我不耐煩的是,細節非常的多… 很多、很多、很多、很~~~~多。
總而言之,其實看起來這小小的好像不難(?),貌似也非常多人實作,但依據現實的情況,其實是有很多細節需要經過處理的。也不敢說,東西就已經很好了(是覺得應該有蠻多洞的…|||),不過、對現在的我來說,蠻夠用的,以我現在知道的各種組合來說,大部份都是沒有問題的。
至於更複雜的話、我會認為,過於複雜的操作或顯示是不現實的,像我太複雜我就會瞳孔放大啊!眼神、精神完全不能集中。
以上、autocomplete 完結。