版权声明:本文为博主原创文章,如要转载或者其他合作请邮件告知我将会在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` 来做异步处理了。