邮箱输入辅助插件 EmailPop

在讲解开始之前,你可能希望先查看:demo download

当用户使用邮箱登录你网站时,你可能希望免去用户一些输入,以及防止用户写错邮箱地址,类似于新浪微博登录时的提示:

emailpop

首先需要定义它的html结构,这决定了它的显示方式及运行原理,我使用了一个无序列表“UL”:

1
2
3
4
5
6
<ul id="emailpop" class="autopop">
	<li class="notpop">请选择邮箱类型</li>
	<li class="pop">yu123</li>
	<li>yu123@gmail.com</li>
	<li>yu123@sina.com</li>
</ul>

基本结构固定好以后,由于选择表现与逻辑分离,需要给它定义样式:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
.autopop{/*承载数据的容器,即UL*/
	position: absolute;/*需要它显示在文本框的正下方,采用绝对定位*/
	background: #fff;
	border: 1px #ddd solid;
	box-shadow: 0px 1px 4px #ccc inset;
	border-radius: 3px;color: #999;
	z-index: 19;/*层级设定高一些,避免被不必要的元素遮盖*/
	display: none;/*一开始是不显示的*/
}
.autopop li{/*邮箱列表的基本状态*/
	font-size:12px;
	height: 24px;
	line-height: 24px;
	padding: 0 6px;
	cursor: pointer;/*需要模拟可以点击的鼠标手型样式*/
	overflow: hidden;
}
.autopop li.notpop{/*默认提示语句一行是无法选取的*/
	cursor: default;
}
.autopop li.pop{/*选中状态*/
	background: #dee;
	color:#333;
}

需要说明的是,我只定义了基本样式,假如你拿去使用,可能样式与图片显示有差异,视你的reset样式及文档声明类型有关,请自行修改所需要的视觉表现。如有问题,概不负责。

结构和样式定义完了,提示也有了个基本的模样,接下来我们就需要在使用时把它插入到“body”里:

1
$("body").append('<ul id="emailpop" class="autopop"></ul>');

然后给它绑定鼠标悬浮以及点击事件:

1
2
3
4
5
6
7
$pop = $("#emailpop").on("mouseover", "li:not(:first)", function() {//鼠标滑过的时候会有高亮,也就是刚刚定义的“.pop”样式
	$pop.find("li.pop").removeClass("pop");
	$(this).addClass("pop");
}).on("mousedown", "li:not(:first)", function() {//点击以后提示隐藏,并把值赋给文本框
	$pop.hide();
	$bind.val($(this).text());
})

“$bind” 是当前正在输入的文本框,你可能要问:这样使用不会出现未定义吗?后面讲 :a7

元素创建了,事件绑定了,数据哪里来?考虑到邮箱后缀一般都比较稳定,没有哪个邮箱提供商会没事就改下后缀名的,我就定义死了。

1
2
var list = ["gmail.com", "sina.com", "163.com", "qq.com", "126.com", "vip.sina.com", "sina.cn", "hotmail.com", "sohu.com", "yahoo.cn", "139.com", "wo.com.cn", "189.cn", "21cn.com"],
$bind, delay, rsDelay, l//这几个参数后面有用

如果你想改成自定义或者根据参数来控制数据,请自行修改,很简单的啦,改改后缀就行了。

当用户输入邮箱的时候,假如他改变了浏览器大小,这个提示信息是不会自己跟着文本框跑的,为避免这种情况发生,我们要给浏览器窗口绑定“resize”事件:

1
2
3
4
5
6
7
8
9
10
11
resize = function() {
	if (rsDelay) clearTimeout(rsDelay);//延迟方法执行,很多时候要用到这种延迟以降低浏览器负担
	rsDelay = setTimeout(function() {
		var offset = $bind.offset();//每次浏览器改变大小时计算下正在输入文本框的位置,然后调整下赋值的位置
		$pop.css({
			left: offset.left,
			top: offset.top + $bind.outerHeight() + 2,
			width: $bind.outerWidth()//使得提示信息的宽度与文本框宽度一致,好看点
		});
	}, 99)
};

完事具备,只欠东风,最后就只剩下如何根据用户输入来显示提示信息了,想一下用户要在文本框输入的时候触发的第一个事件是什么?“focus”,当然是先选中它啦:

1
2
3
4
5
focus: function() {
	$bind = $t.trigger("keydown");//绑定$bind为当前正在输入的文本框,这样上面的$bind就不会在使用时出现未定义了,为什么要触发keydown事件?
	resize();//调整下提示信息的位置
	$(window).on("resize", resize);//把resize事件绑定到浏览器窗口上
}

接下来想一下用户在文本框输入的时候触发的第一个事件是什么?这跟上一个题目可不一样哦。“keydown”,键盘按下事件啊,你打字肯定是先down后up啊。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
keydown: function() {
	var val = $t.val();
	if ($.trim(val).length) {
		var i = 0,
		s = val.indexOf("@"),//获取到用户输入内容里“@”的位置
		u = val,
		r = "",
		html = '<li class="notpop">请选择邮箱类型</li><li class="pop">' + val + '</li>';//第一条肯定是提示,第二条是用户自己的输入
		if (s >= 0) {
			u = val.substr(0, s);
			r = val.substr(s + 1)
		}//把输入内容根据“@”分成两部分
		l = list.length;
		for (; i < l; i++) {//循环下一开始定义的邮箱数组
			if (r.length > 0) {//如果用户输入了邮箱后缀一部分
				if (list[i].indexOf(r) > -1 && r != list[i])//判断数组内是否有用户输入的邮箱后缀且不是全等于,思考下为什么不是全等于?
					html += "<li>" + u + "@" + list[i] + "</li>";
			} else html += "<li>" + u + "@" + list[i] + "</li>"
		}
		l = $pop.html(html).show().find("li").length;//把数据填充进去显示出来并获取到数据长度
	} else $pop.hide();//比如说我把文本框的内容全删除了,提示当然就应该隐藏起来
}

用户填完了,焦点离开输入框,这时候就应该把提示隐藏起来:

1
2
3
4
blur: function() {
	$pop.hide();//隐藏
	$(window).off("resize", resize);//移除浏览器窗口绑定的resize事件,养成好习惯,用不到的事件及时移除,减少浏览器负担
}

大功告成,快试试效果如何,等等,好像少了点什么?假如我想在输入框里用上下来选择邮箱,用回车来补全怎么办?判断用户按的哪个键来执行不同的方法就行啦:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
keydown: function(e) {
	switch (e.which) {
		case 9://TAB键,隐藏
			$pop.hide();
			break;
		case 32://空格键,没反应,放心,不会有邮箱名有空格的
			return false;
			break;
		case 13://回车,补全邮箱后缀
			$t.val($pop.hide().find(".pop").text());
			break;
		case 38://向上键
			var $p = $pop.find(".pop").removeClass("pop");//先找到当前选择的一列去掉高亮
			if ($p.index() > 1)//判断它的位置,记住有一行是提示信息是无法选择的
				$p.prev().addClass("pop");//给它前一项添加高亮
			else//如果是第二列即用户输入那列
				$pop.find("li:last").addClass("pop");//就给最后一项添加高亮
			return false;
		case 40://向下键,跟向上是反着的
			var $p = $pop.find(".pop").removeClass("pop");
			if ($p.index() < l - 1)
				$p.next().addClass("pop");
			else
				$pop.find("li:eq(1)").addClass("pop");
			return false;
		default:
			//这里是刚才写的方法
	}
}

在这一步你当然也可以禁止用户输入非法字符,只是多添加几个case而已,不过谁知道谁的非法字符都有哪些呢?想加自己加吧。

可能要问一开始定义的“delay”是干嘛用的?当用户按住一个键不放的时候,提示也会根据输入内容自动改变,为了减少浏览器消耗,所以要使用延迟把执行推后,那为什么不直接把事件定义在keyup上?当回车键keyup的时候,表单就已经开始提交了,这时候你再修改文本框的内容,表单是无法获取的它已经提交了还获取个毛啊,所以就只好绑定在keydown事件上了,不过你也可以把修改内容事件绑定在keyup,当keydown发生时,判断是否回车键,不过这样就需要多绑定一个事件,能省点事就省点嘛,不过假如编写搜索自动补全插件的话就需要用上了,这个回头再说吧。

最后的最后,把代码整合起来。如果你已经根据上面的内容自己写了一个插件出来,下面就可看可不看了。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
(function() {
	var $bind, $pop, delay, rsDelay, l,
	list = ["gmail.com", "sina.com", "163.com", "qq.com", "126.com", "vip.sina.com", "sina.cn", "hotmail.com", "sohu.com", "yahoo.cn", "139.com", "wo.com.cn", "189.cn", "21cn.com"],
		resize = function() {
			if (rsDelay) clearTimeout(rsDelay);
			rsDelay = setTimeout(function() {
				var offset = $bind.offset();
				$pop.css({
					left: offset.left,
					top: offset.top + $bind.outerHeight() + 2,
					width: $bind.outerWidth()
				});
			}, 99)
		};
	$(function() {
		$("body").append('<ul id="emailpop" class="autopop"></ul>');
		$pop = $("#emailpop").on("mouseover", "li:not(:first)", function() {
			$pop.find("li.pop").removeClass("pop");
			$(this).addClass("pop");
		}).on("mousedown", "li:not(:first)", function() {
			$pop.hide();
			$bind.val($(this).text());
		}),
	});
	$.fn.emailpop = function() {
		return $(this).attr("autocomplete", "off").each(function() {
			var $t = $(this).on({
				focus: function() {
					$bind = $t.trigger("keydown");
					console.log(1)
					resize();
					$(window).on("resize", resize);
				},
				keydown: function(e) {
					switch (e.which) {
						case 9:
							$pop.hide();
							break;
						case 32:
							return false;
							break;
						case 13:
							$t.val($pop.hide().find(".pop").text());
							break;
						case 38:
							var $p = $pop.find(".pop").removeClass("pop");
							if ($p.index() > 1) $p.prev().addClass("pop");
							else $pop.find("li").last().addClass("pop");
							return false;
						case 40:
							var $p = $pop.find(".pop").removeClass("pop");
							if ($p.index() < l - 1) $p.next().addClass("pop");
							else $pop.find("li").eq(1).addClass("pop");
							return false;
						default:
							if (delay) clearTimeout(delay);
							delay = setTimeout(function() {
								var val = $t.val();
								if ($.trim(val).length) {
									var i = 0,
										s = val.indexOf("@"),
										u = val,
										r = "",
										html = '<li class="notpop">请选择邮箱类型</li><li class="pop">' + val + '</li>';
									if (s >= 0) {
										u = val.substr(0, s);
										r = val.substr(s + 1)
									}
									l = list.length;
									for (; i < l; i++) {
										if (r.length > 0) {
											if (list[i].indexOf(r) > -1 && r != list[i]) html += "<li>" + u + "@" + list[i] + "</li>";
										} else html += "<li>" + u + "@" + list[i] + "</li>"
									}
									l = $pop.html(html).show().find("li").length;
								} else $pop.hide();
							}, 99)
					}
				},
				blur: function() {
					$pop.hide();
					$(window).off("resize", resize);
				}
			})
		})
	}
})()

使用的话:

1
2
$("#email").emailpop();//单个文本框使用
$("#email input").emailpop();//多个

我尝试着不太讲语法,只讲下我编写插件时的思路,因为我觉得,语法说重要也不重要,毕竟你测试的时候语法错误肯定报错,但是思路错误或者逻辑错误,有时候即使你运行正常还是会出很多BUG,或者效率不达标。我把自己的代码分享出来,在这个过程里,本身就是对我自己编程的一个回顾,即使刚才,我又发现了两处错误(不能算错误,只是改一下效率会好一些,代码能更简单一些),希望你看完后能给你一点启发,或者发现我的错误,如有问题,请留言不保证一定回复

题外话:近期一直在使用seajs,感觉很不错,延时,按需加载,模块化编程,修改方便,文件也很小,不错不错。我在GitHub上分享了工作中编写的一些插件,不过都是基于seajs的,我会慢慢把他们剥离出来一一讲解,敬请期待我很懒所以别期待太多

4 条评论在 “邮箱输入辅助插件 EmailPop

  1. Pingback引用通告: 搜索辅助输入插件 AutoCS - 吁... |禹向辉的长吁短叹

发表回复

您的电子邮箱地址不会被公开。 必填项已用*标注

此站点使用Akismet来减少垃圾评论。了解我们如何处理您的评论数据