#tag 已經被廣泛的應用在我們的生活周遭, 主要作用便是方便我們為特定物件做 grouping, 很多 social based 的服務皆已大量採納這樣的實作, 比方說 facebook, instagram 以及 tumblr 皆有支援
雖然說核心思想一致, 不過實際上各家的實作還是存在著很大的差異, 前一陣子筆者仿照 facebook 開發了一個相關的 web components – <hash-tag>, 為了讓 PM 能有更多的選擇, 筆者又開發了一款 web components – <hash-tag-t>, 這款是依照自家的服務 tumblr 所開發, 雖然說少了 facebook 自由奔放的感覺, 不過多了便利的 autocomplete 以及 i13n tracking, 更方便我們做一些約制以及成效追蹤喔! 接下來就讓我來介紹 <hash-tag-t> 給大家認識
歡迎點擊下方連結進行觀看 : hashtagT: http://mei.homin.com.tw/hashtagTPro…
由於架構在 Web Components 上, 所以「hashtagT」俱備了可攜、易用的特性, 搭配客製化 tag – <hash-tag-t> (custom element), 除了語意化結構獲得了補強, 並且發揮了 custom element 的「熱拔插」特性, 只要對 DOM 進行 element append / remove 的動作, 便可以快速對 element 進行 init / detach 的行為, 對 developer 的 effort 降到最低, 達到快速安裝使用. 最有趣的還可以直接在 element 上 bundle method, 讓 developer 可以輕輕鬆鬆便享受到 tag 所帶來的便利性, 將心力用於其他細節處理上, 讓整體體驗達到另一層次
facebook VS. tumblr
雖然 facebook 和 tumblr 都有 support #tag, 不過在 UI 操作上可以說是截然不同的體驗, 再繼續開始介紹本 module 前, 先簡單介紹兩者之間的差異
facebook 的版本對筆者來說, 是自由的, 是奔放的, 它讓你可以隨時隨地自己決定是否要使用 hashtag, 只要是 「#」開頭, 空格結尾的字串, 它就會把符合該 pattern 的字串做裝飾, 方便 user 知道開字串等等會被當作 #tag 使用, user 擁有最大的主導權, 隨時隨地可以進行編輯, 調整內容甚至是刪除的動作, 系統完全不會有任何的干擾, 讓 user 的操作如行雲流水般, 不會被系統阻擋或者出現一些不符合 user 操作預期外的行為
筆者非常欣賞這樣的做法, 因為它沒有其他特殊的操作要熟悉 (硬要說的話就是 # 開頭, space 結尾的 pattern), 所以在鍵入內容的當下, 隨時都可以決定要不要塞入 hashtag, 也可以任意的決定它要出現在文章的什麼地方, 更不會有些怪行為出現, 有些服務當 user keyin 一些不符合系統預期的文字時, 會直接把當前的行為 block 起來, 這些行為有可能是直接阻擋鍵盤的操作, 或者是擅自 trim 方才所輸入的內容, 不管是哪一種, 均會讓接觸到的 user 感到 shock, 不知所措….
- tumblr
tumblr 基本上是把 #tag 獨立成一個新的欄位, 完完全全跟 user 的文章作脫鉤, 光是這點就跟 facebook 有很大的差異, 另外, 如上圖所示, 它其實多了「 autocomplete 」的 feature, 會根據 user 當前 keyin 的內容, 推薦目前最熱門的 tag 出來, 方便 user 做一個快速選取的動作, 對於熟練度不是很高的 user 來說, 這真的很貼心
每個新增出來的 #tag, 都會自動的移到左邊區塊, 滑鼠遊標仍會停留在 input field 中, 方便 user 繼續做 tag 新增的動作
不過, tumblr 並不提供 #tag 修改的功能, user 僅能透過點選以及 press 鍵盤的 delete 或者是 backspace 鍵來做刪除喔!
以上便是兩大 social site 針對 #tag 實作上的差異處, 接下來要跟大家介紹的 hashtagT 便是以 tumblr 為藍本進行開發的 module
hashtagT 的簡易操作介紹
hashtagT 的「T」指的便是 tumblr, 所以其呈現與操作大致上與 tumblr 一致, 我們先看看區塊分布
大致上會有兩個主要的區塊, 分別為
- 所有已建好的 hashtag 呈現區 預覽區)
- 輸入區
如上圖所示, 我們先在 input field keyin 所需要的 hashtag 後, 按下「Enter / Return 」, 便會把方才 keyin 的 hashtag 往最左邊區塊擺放, 所以 user 可以知道目前已經建立了多少以及哪些 hashtag
如果今天試圖建立一個已經存在, 或者不符合我們所設定的 format 時, 系統會有善意貼心的提示效果, 方便 user 知道當前的 keyin 需要做調整, 上例便是試圖 keyin 一個已存在的 hashtag – 「mei 」, 這時後 input filed 便會使用醒目的文字顏色變化, 以及搖搖頭的動態效果來告知 user 需要調整
只要 input filed 有內容變化時, 系統便會自動呈現可能感興趣的 hashtag 列表供 user 作快速選取, 不僅方便 user 快速完成 hashtag 建立, 更讓 user 知道下哪些 hashtag 可能會讓自己的文章更容易受到注目
autocomplete 的操作, 除了支援直接點擊外, 亦支援鍵盤的操作
- ↑: 將當前作用區往上選取 (無限循環)
- ↓ : 將當前作用區往上選取 (無限循環)
- Enter / Return: 使用當前作用區的的文字作為 hashtag 輸入的內容
- Esc: 關閉 autocomplete
承續上面的例子, 當我們直接在 input filed 完成 keyin 且直接按下 Enter / Return, 系統便會以目前 input field 的內容建立一組新的 hashtag 並往左邊的預覽區塊擺放, 此刻的游標仍在 input field 中, 方便 user 作下一個 hashtag 的操作
本 module 不提供修改功能, 所以如果 user 對於 hashtag 不滿意的話, 請直接進行刪除, 刪除的方法很簡單, 共有兩種方法, 如下所示:
- 直接點擊欲刪除的 hashtag 後(系統會有明顯的樣式變化), 並按下鍵盤 Delete || Backspace 後, 便可以進行刪除
- 直接 focus 在 input field 中, 並且不斷按下鍵盤的 Backspace, 系統便會將預覽區最右邊的 hashtag 直接進行刪除
如何套用 hashtagT 到 web page 中 ?
hashtagT 是一個獨立的 web component, 所有的行為和樣式都完整的封裝起來, developer 只要將特定的 HTML tag – <hash-tag-t> 放入頁面上任何想要擺放的位置即可, 不需要額外作任何 JavaScript init 的動作
HTML
< hash-tag-t > < h3 >hashtagT< /h3 > < input name="hashtag" value="#mei #lauMu" placeholder="#tag" >< /hash-tag-t >
另外, JavaScript 也要將之 require 進來
< script async src="script/pack-hashtag-t.js" >< /script >
由於 hashtagT is designed by native JavaScript, 所以不管當前 website 是使用什麼樣的 framework, 皆可以正常運作, 不會受到任何影響
hashtagT 再進行 init 的時候, 會將子元素第一個 input 的當前設定來做初始化設定
- name: input field name, 當 user 完成 keyin 的動作後, 會把有 mapping 到的 tag 以 array 的方式產出, ex: hashtag[]
- value: 設定初始化內容
- placeholder: 當 hashtagT input field value 為空時, 要顯示的文字
例外, 也可以透過 JSON String 的方式來帶入相關初始化的 data, 透過 config 的設置, 可以讓 developer 可以讓 <hash-tag-t> 有更多元且客製化處理喔!
以下為相關的 config 設置
{ "io": { "uri": "hashtagTFunc.php", "params": { "qAmt": "5", "action": "htagSuggest", "property": "meistudio" } }, "pattern": [ "^[u4e00-u9fa5a-zA-Z0-9]{1,20}$", "^(d+[u4e00-u9fa5a-zA-Z]+)|([u4e00-u9fa5a-zA-Z]+d*)" ], "maxAmount": "20", "duration": "180s", "withCredentials": true, "fieldName": "hashtag", "fieldValue": "#mei #lauMu", "placeholder": "#tag"}
再將上述 data 以 JSON String 的形式帶入 data-conf 這個 attribute 即可, 如下所示
< hash-tag-t data-conf='JSON String'>< /hash-tag-t >
我們來看一下 config 擁有什麼參數可供 developer 調整
- io: 此為設置 autocomplete 的 web service, 如果 developer 需要夾帶其它額外的參數的話, 可以在 io.params 裡面設置
- pattern: 如果有設置的話, 那麼在 user 建立 hashtag 的當下, 便會以此為依據進行檢查, 它為 array 型態, 所以 developer 可以任意添加想檢查的 pattern 進去
- maxAmount: 用來限制 user 可以建立的 hashtag 數量, default 為 Infinity
- duration: 為了避免對 autocomplete 的 server 狂抽猛送, 所以本 module 有個智能 cache 機制, 所有 fetch 過的 data 均會被 client cache 起來, duration 便是讓 developer 設置 client cache 的存活時間, unit 為 ms, 可接收的格式有: “180s” || “180000ms” || “18000”
- withCredentials: 由於本 module fetch autocomplete data 的方法為 XmlHttpRequest2, 所以允許 developer 自行設定是否允許當前 client cookie 進行雙向溝通, default 為 false
- fieldName: 設置 input field name, 當 user 完成 keyin 的動作後, 會把有 mapping 到的 tag 以 array 的方式產出, ex: hashtag[]. (如果 <hash-tag-t> 子元素第一個 input 存在的話, 則會以該元素設定為主要依據)
- fieldValue: 設定初始化內容. (如果 <hash-tag-t> 子元素第一個 input 存在的話, 則會以該元素設定為主要依據)
- placeholder: 當 hashtagT input field value 為空時, 要顯示的文字. (如果 <hash-tag-t> 子元素第一個 input 存在的話, 則會以該元素設定為主要依據)
建好的 hashtag 如何傳遞給 backend ?
資料結構的呈現會是如何呢 ? 基本上 hashtagT 會將整理好的資料用 array 的方式做呈現, 不過由於 form action 無法將 shadowDOM 裡面的 input 刮出來做資料傳遞, 因此這些資料的結構必須置放在 shadowDOM 之外, 系統會將產生一個 .vanquish 的元件來做存放
以上, 便是 hashtagT 的基本設置, 輕輕鬆鬆幾個簡單的步驟, 就可以幫頁面上 form 新增相關的元件, 另外 <hash-tag-t> 亦提供了一些 method 來方便 developer 使用
hashtagT 所提供的各種 method
- set: 用來設定 hashtag 的內容, 如果需要動態調整其內容的話, 便可以透過這個 method 來做 … (git)
$('hash-tag-t').set('#mei #lauMu');
- get: 用來取得當前已建立所有 hashtag, 資料會以 array 的型態進行回傳 … (git)
$('hash-tag-t').get(); // return ['mei', 'lauMu']
- addTag : 透過 JavaScript 動態來新增 hashtag, 傳進來的 tag string 會以相同標準來審視是否可以進行新增 … (git)
$('hash-tag-t').addTag('yes');
- removeTag: 透過 JavaScript 動態來刪除 hashtag, 若傳進來的 tag string 並不存在於現有的 hashtag 中則不做任何處理 … (git)
$('hash-tag-t').removeTag('yes');
- addCallback: 動態綁定 callBack function, 當 hashtagT 有出現 addTag || removeTag || error 狀態的時候, 便會 call 我們所綁定的 callBack function (同樣的 callBack function 不會進行重複綁定) … (git)
function callBack(action, label) { console.log(arguments);} $('hash-tag-t').addCallBack(callBack);
- removeCallback: 動態移除 callBack function (callBack function 非在綁定 queue 中時, 則不會作任何處理) … (git)
$('hash-tag-t').removeCallBack(callBack);
透過以上這些 method 的加入, 相信 developer 可以更加有效的掌控當前的 hashtagT
hashtagT attribute 操作的點點滴滴
除了 <hash-tag-t> 本身提供的 method 外, 其實也可以透過 <hash-tag-t> 的 attribute 變化來得到或者操作一些當前的狀態喔!
- hash-tag-t[data-set]: 其作用就跟 hash-tag-t.set 一樣, 用來設置當前 hashtag 的內容
$('hash-tag-t').setAttribute('data-set', '#mei #lauMu');
- hash-tag-t[disabled]: 透過 disabled 這個 attribute 的設置, 可以動態的開放或解除 “輸入區”, 如果 input[disabled] 一樣
$('hash-tag-t').setAttribute('disabled', 'disabled');
- hash-tag-t.value: 其作用就跟 hash-tag-t.get 一樣, 用來取得當前已建立所有 hashtag, 資料會以 array 的型態進行回傳
var value;value = $('hash-tag-t').value;console.log(value); // return ['mei', 'lauMu']
如何 tracking user 使用成效 ?
相信每位有心且專業 PM 都會想要知道所開發的 feature 是否被 user 廣泛接受, 就連 developer 也不例外, 所以 hashtagT 亦提供 i13n tracking 喔! 其主要的 tracking tool 為筆者前一陣子所開發的 Google Analytics 補強計畫 – gaExt.js, 只要出現以下狀態均會發送 beacon 出來喔!
- addTag: 只要有 hashtag 被新增, 便會發送 action 為 addTag 的 beacon, label 的內容則為所建立的 hashtag
- removeTag: 只要有 hashtag 被刪除, 便會發送 action 為 removeTag 的 beacon, label 的內容則為所刪除的 hashtag
- dataFetch: 當 fetch autocomplete data 發生時, 會發送 action 為 dataFetch 的 beacon
- suggestView: autocomplete 出現時, 會發送 action 為 suggestView 的 beacon, 方便我們 counting 其展開的次數, 以利我們計算 CTR
- suggestClick: autocomplete 發生點擊時, 會發送 action 為 suggestClick 的 beacon, label 的內容則為剛剛所點擊的列表, 以利我們計算 autocomplete 的 CTR
hashtagT 特點
- pure native JavaScript, 不需要憑依在任何 framework 上
- 使用容易, 透過簡單 HTML tag的設置, 便可以快速擁有其 feature
- 讓 hashtag 的建立可以 base on 我們所設置的一些規則之上
- 智能 client cache 的導入, 所有被 featch 過的資料都會被存放起來, 不會對 web service 狂抽猛送, 不僅讓當前頁面所有有用到同樣 web service 的 <hash-tag-t> 共享外, 就連不同 tab 下同樣 web service 的 <hash-tag-t> 都能及時受益
- 豐富的 method 以及 attribute 操作讓 developer 可以更及時有效的掌握 <hash-tag-t> 的狀態
- i13n 的導入, 充分有效的掌握 user 使用情況
以上, 便是 hashtagT 的相關使用與分享, 有興趣的朋友們請不用客氣, 可以直接使用, 也請不吝一同討論與指教喔!
測試環境:
- OS: Windows 7
- Browser: Chrome Version 50.0.2661.102 m (64-bit) , Firefox 46.0.1, Internet Explorer Version 11.0.9600.18282