最近项目中有表单提交的地方需要用户填写邮箱,PM(产品经理)和运营都强烈要求在用户填写邮箱的时候出现suggest列表,简化用户输入的填写流程。我考虑了下,这个应该也是经常会用到的功能,细心的朋友可能会发现好多登录或者其他有表单提交的地方,比如搜狐白社会登录,我的搜狐登录等等都有这个功能,所以为了方便以后使用,索性写一个jQuery插件出来好了。
这里不对具体代码做详解,只讲述实现思路和流程。首先有一份默认配置defaults
// 默认参数配置
defaults = {
sugClass: 'ema-sug',
domains: [
'sohu.com',
'163.com',
'126.com',
'139.com',
'sina.com',
'qq.com',
'gmail.com'
]
}
sugClass是用户为suggest添加样式和作为插件内容js钩子使用的,domains是suggest列表内容,如果用户使用插件时候没有传递参数就会使用默认配置
插件内部有一个EmailSug构造函数,每次调用插件时候都会新创建一个EmailSug的实例,每个实例有自己的属性和配置
function EmailSug(elem, options) {
this.$field = $(elem);
this.options = $.extend(true, {}, defaults, options);
this._defaults = defaults;
this._domains = this.options.domains;
// 当前选中元素下标
this.selectedIndex = 0; this.init();
}
当创建实例时候会执行初始化函数,初始化函数调用了事件绑定方法
init: function() {
this.addEvent();
}
事件绑定方法对input元素进行了keyup blur事件的绑定,分别在用户输入内容时候进行suggest更新和blur时候关闭suggest提示
addEvent: function() {
var
// 当前上下文
that = this, //
value; this.$field.on('keyup.ema', function(e) {
value = $.trim(this.value); if (value) {
that.create(this, value); that.doSelect(e.keyCode);
} else {
that.hide();
}
}).on('blur.ema', function() {
setTimeout(function() {
that.hide();
}, 200);
});
}
blur事件处理必须要使用setTimeout处理下,因为在用户通过鼠标点击suggest的某一个元素时候会触发input的blur事件,如果不做setTimeout处理会点击不到要选择的元素
create: function(elem, value) {
var
//
that = this, arr, len, //
fragment, //
ul = [], //
offset, left, top, width, height, style, // 左右边框
borderWidth = 2; elem = $(elem);
offset = elem.offset(); width = elem.outerWidth(true) - borderWidth;
height = elem.outerHeight(true);
left = offset.left;
top = offset.top + height;
style = 'left: ' + left + 'px; top: ' + top + 'px; width: ' + width + 'px; border: 1px solid #e2e2e2; border-top: 0; display: none'; //
fragment = $('<div class="' + this.options.sugClass + '-wrapper" style="' + style + '" />');
ul.push('<ul class="' + this.options.sugClass + '-list">'); arr = this.filter(value, this._domains);
len = arr.length;
$.each(arr, function(i, domain) {
var
//
_class = that.options.sugClass + '-item'; if (that.selectedIndex > len - 1) {
if (i === 0) {
_class += ' active'; that.selectedIndex = 0;
}
} else {
if (i === that.selectedIndex) {
_class += ' active';
}
} ul.push('<li class="' + _class + '" data-index="' + i + '">' + value.replace(/@.*/, '') + '@' + domain + '</li>');
}); ul.push('</ul>');
ul = ul.join(''); if (this.$suggest) {
this.$suggest.empty();
this.$suggest.append(ul);
} else {
fragment.append(ul); // 显示到页面
$('body').append(fragment);
this.$suggest = fragment; ///
this.$suggest.on('mouseenter click', '.' + this.options.sugClass + '-item', function(e) {
var lis, li; li = $(this);
lis = li.parent().children(); if (e.type === 'mouseenter') {
li.addClass('active').siblings().removeClass('active'); that.selectedIndex = $.inArray(this, lis);
} else {
// 当前选中
that.$field.val(lis.eq(that.selectedIndex).text()); // 隐藏email sug
that.hide();
}
});
} //
this.show();
}
create方法调用了filter方法根据用户输入的内容对配置参数domains进行过滤筛选,返回跟用户输入匹配的数组,然后创建suggest列表并选中某一个元素,根据input元素的位置对suggest进行定位,最后渲染到页面并显示。每个实例只会创建一个suggest元素,每次根据用户输入的内容更新列表
//
filter: function(value, arr) {
var
//
start, suffix, r = []; start = value.indexOf('@');
if (start > -1) {
suffix = value.substring(start + 1); $.each(arr, function(i, str) {
if (str.indexOf(suffix) > -1) {
r.push(str);
}
});
} else {
r = arr;
} return r;
}
filter方法,实现逻辑很好理解
doSelect: function(keyCode) {
var
//
elems = $('.' + this.options.sugClass + '-item', this.$suggest), //
min = 0, //
max = elems.length - 1; switch (keyCode) {
case 13:
// 回车选中当前已选项
$('li.active', this.$suggest).trigger('click'); // 下标重置
this.selectedIndex = 0; break;
// 向上
case 38:
this.selectedIndex --; if (this.selectedIndex < min) {
this.selectedIndex = max;
} elems.removeClass('active').eq(this.selectedIndex).addClass('active');
break;
// 向下
case 40:
this.selectedIndex ++; if (this.selectedIndex > max) {
this.selectedIndex = min;
} elems.removeClass('active').eq(this.selectedIndex).addClass('active');
break;
default:
break;
}
}
doSelect方法用户处理用户的键盘操作,Up Down Enter Esc这些按键处理
show: function() {
if (this.$suggest) {
this.$suggest.show();
}
} hide: function() {
if (this.$suggest) {
this.$suggest.hide();
}
}
show和hide方法就是简单的最suggest进行显示和隐藏操作
实现很简单吧,源码不超过300行。可能实现有不合理或者不完善的地方欢迎指正~
最后附上demo地址和源码
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>jQuery插件email suggest - demo</title>
<link href="https://files.cnblogs.com/files/typeof/base.css" rel=stylesheet type=text/css >
<style type="text/css">
.page {
padding: 20px 10px;
}
.page .email {
width: 350px;
height: 30px;
line-height: 30px;
border: 1px solid #e2e2e2;
padding-left: 10px;
font-size: 14px;
}
.ema-sug-wrapper {
position: absolute;
background: #fff;
text-align: left;
}
.ema-sug-wrapper .ema-sug-list .ema-sug-item {
font-size: 14px;
height: 25px;
line-height: 25px;
padding-left: 10px;
color: #333;
}
.ema-sug-wrapper .ema-sug-list .ema-sug-item.active {
background: #ff4800;
cursor: pointer;
color: #fff;
}
</style>
<script src="https://apps.bdimg.com/libs/jquery/1.10.2/jquery.min.js"> </script>
<script src="https://files.cnblogs.com/files/typeof/email-sug.js"> </script>
</head>
<body>
<div class="page">
<input type="text" class="email" />
</div>
<script>
$('.email').emailSug({
sugClass: 'ema-sug',
domains: [
'sohu.com',
'sogou.com',
'163.com',
'126.com',
'139.com',
'sina.com',
'qq.com',
'gmail.com'
]
});
</script>
</body>
</html>