在實際開發開發中我們會用到各種瀏覽器、HTML、JS等提供的原生的組件/介面,但是這樣並不一定滿足我們的要求,所以我們需要自己寫一些我們需要的組件。 平常我們會經常用`select` 標簽做下拉選項,不過這個只能選擇不能手動輸入,當然網上也有很強大的select2插件,如果只要輸入和選擇兩個功能的話 ...
版權聲明:本文為博主原創文章,如要轉載或者其他合作請郵件告知我將會在24小時內回覆,郵箱:lengroubao@163.com
在實際開發開發中我們會用到各種瀏覽器、HTML、JS等提供的原生的組件/介面,但是這樣並不一定滿足我們的要求,所以我們需要自己寫一些我們需要的組件。 平常我們會經常用`select` 標簽做下拉選項,不過這個只能選擇不能手動輸入,當然網上也有很強大的select2插件,如果只要輸入和選擇兩個功能的話那麼強大的功能並不是我們需要的。這篇文章我們就來寫一個簡單實用的select
需求
需求很簡單我們要做的是兩個功能 輸入和下拉選擇,為了交互體驗更好,我們需要做動畫和事件。保證交互的流暢。而且我們還需要做一些簡單的驗證來檢測非法輸入。
交互
首先,我們要先明白要寫那些事件,通過之前的gif 我們可以看到 一共有三個事件,獲取焦點,焦點事情,點擊事件。 除了事件,還需要做一些過度,如果對相容性沒有嚴格要求的話(IE低版本)過度用CSS就好了。如果對相容性有嚴格要求那麼就用JS來寫。這裡的是用CSS3的 `transition` 來做一個0.3秒的過渡
驗證
除了交互還要驗證是否輸入了非法字元,這裡最合適的是用正則表達式來做,我們這個例子是驗證正整數 `/^[1-9]\d*$/` 如果是要驗證郵箱`/^([a-zA-Z0-9_-])+@([a-zA-Z0-9_-])+(.[a-zA-Z0-9_-])+/` 驗證只能輸入數字和英文的 `/^[0-9a-zA_Z]+$/` 如果不會正則表達式可以網上百度,基本上你需要的表達式都能查到。
具體實現
知道要做什麼了 你們就直接上代碼吧,這裡JS是重點,所有我先貼html結構和JS,CSS最後放。HTML結構
<div class="canInpSelection"> <input type="text" class="canInp" name=""> <ul class="select" data-height=""> <li data-value='1'>1</li> <li data-value='2'>2</li> <li data-value='3'>3</li> <li data-value='4'>4</li> </ul> </div>
這裡的li列表可以寫死可以動態渲染。 data-value是用來存數據的,比如數據的ID或者其他的什麼。 data-height是用來存ul的高度的,主要是過度動畫需要
JS 註意 這裡依賴JQ的選擇器和data()方法 可以直接用百度的CDN
<script type="text/javascript" src="https://ss0.bdstatic.com/5aV1bjqh_Q23odCf/static/superman/js/lib/jquery-1.10.2_d88366fd.js"></script>
獲取焦點事件(focus)
這個事件的邏輯是 獲取焦點->如果用戶還沒輸入那麼把輸入框清空方便用戶輸入 ->根據列表數計算UL高度存入data-height屬性里->然後設置高度,CSS會自動出現過度效果
$('body').on('focus ','.canInpSelection',function () { var ul =$(this).find('.select') if($(this).find('input').val() == 0){ $(this).find('input').val('') } if(ul.data('height') == ''){ var h= ul.find('li').length *25 ul.data('height',h) } var ch = ul.css('height') var dh = ul.data('height') if(ch == '0px'){ ul.css('height',dh) } })
失去焦點事件(blur)
這裡的邏輯很簡單 如果用戶沒有輸入就設置預設值0,否則就判斷,然後`非同步`執行關閉下拉事件。
$('body').on('blur ','.canInpSelection',function () { var ul =$(this).find('.select') var val = $(this).find('input').val() if(val== ''){ $(this).find('input').val('0') }else{ var ex = /^[1-9]\d*$/; if(!ex.test(val)){ $(this).find('input').val('0') // tips.error('請輸入正整數') alert('請輸入正整數') } } setTimeout(function () { ul.css('height',0) },100) })
點擊事件(click)
點擊事件就是賦值加關閉
$('body').on('click ','.select li',function () { var value = $(this).data('value') $(this).parent().prev().val(value) $(this).parent().css('height','0') })
解BUG-非同步編程
在事情焦點事件里最後的關閉一定要用非同步來做(setTimeout),否則會出BUG,因為JS是單線程的,必須先執行完blur才能執行click,但是執行blur的時候會關閉UL,導致click無法觸發(因為我們這裡有300毫秒的過度所以會有一部分能觸發click一部分不能)。為了保證交互 那段關閉UL的代碼也必須存在,所有隻能用非同步來做。 等個100毫秒,在關閉,在這100毫秒里足夠JS處理完click的事件了。 有興趣的同學可以試試不用非同步會發生什麼,或者將100毫秒縮短到1毫秒? 如果對非同步感興趣的同學可以百度“js 非同步編程”。以後我也會寫一篇關於非同步的博文。
組件化
對於組件化我覺得要根據實際工作環境來說: 如果用框架的可以用框架的方式來,比如NG的directive或者vue的directive之類的。 如果不用框架用了underscore之類的工具庫,也可以用_.template來寫通用的。 如果都不用,也可以用面向對象的方式抽出來。 這個組件化,根據實際開發環境來,仁者見仁智者見智。 如果 要寫成組件的話,正則表達式最好作為參數傳進去。我這裡只提供思路,並不難。
CSS
其實CSS沒啥 可以根據自己公司的風格隨便改。這裡最主要的就是下麵這段CSS
-webkit-transition: height ease-out .3s; transition: height ease-out .3s;
transition height 就是根據height的編程做過度 用時300毫秒。 值得註意的是 transition 所根據的屬性必須是具體值,比如auto就不可以。而且這個元素也必須存在,不然也不會有效果。 下麵是完整CSS
div,ul,li,input{ box-sizing: border-box; } ul{ list-style-type: none; } .canInpSelection{ position: relative; width: 200px; } .canInp{ width: 100%; height: 34px; padding: 6px 12px; font-size: 14px; line-height: 1.42857143; color: #555; background-color: #fff; background-image: none; border-color: #ddd; border: 1px solid #ccc; border-radius: 4px; -webkit-box-shadow: inset 0 1px 1px rgba(0,0,0,.075); box-shadow: inset 0 1px 1px rgba(0,0,0,.075); -webkit-transition: border-color ease-in-out .15s,-webkit-box-shadow ease-in-out .15s; -o-transition: border-color ease-in-out .15s,box-shadow ease-in-out .15s; transition: border-color ease-in-out; box-shadow: none; -webkit-box-shadow: none; -webkit-tap-highlight-color: rgba(0,0,0,0); } .select{ position: absolute; top: 33px; left: 0px; right: 0px; background-color: #fff; background-image: none; z-index: 10; text-align: left; height:0; overflow: hidden; -webkit-transition: height ease-out .3s; transition: height ease-out .3s; border-radius: 0 0 5px 5px; padding: 0; margin: 0; } .select li{ height: 25px; line-height: 25px; cursor: pointer; border: 1px solid #ccc; border-top: 0; border-bottom: 0; width: 100%; padding-left: 10px; } .select li:last-child{ border-bottom:1px solid #ccc; } .select li:first-child{ border-top:1px solid #ccc; }
總結
縱觀整個組件,思路理清楚了其實並不難,我覺得最有意思的就是用`setTimeout` 來做非同步處理了。