在做这个功能时候,我参考的是网易注册(http://reg.163.com/reg/reg.jsp?product=urs)中的邮箱栏目所实现的功能与效果。这个功能的目的是使用户在填写邮箱栏目的过程中,在带有邮箱后缀名的菜单的提示下,可以不需要完整输入自己的注册邮箱,达到方便、快捷、友好的效果。在做这个功能的时候,需要充分站在使用者的角度,看怎样设计能够最符合通常情况下人们的使用习惯,又怎样设计反而会让用户感到不便。先来看看功能实现的演示效果图:
①初始状态,邮箱栏目未获得焦点时
以及鼠标点击后获得焦点时
②以空白字符( space , tab ) 以及@开头时,不出现提示菜单
③输入有效字符时,提示下拉菜单出现,用户输入的内容自动添加进菜单,并且第一条提示栏目获得高亮
④输入的字符第一次出现"@"时,输入的"@"并不加入提示菜单
⑤"@"之后出现有效字符时,就会同提示菜单中的邮件后缀从后缀首位起进行比较,把和用户输入的邮箱后缀不同的提示从菜单中去掉。同时过滤后的第一条提示获得高亮。在完全匹配之后,下拉菜单隐藏。
⑥用户删除邮件输入框中的字符时,呈现的效果顺序是⑤图3--->⑤图2---> ⑤图1---> ④图
⑦按下键盘方向键向上或向下使菜单相应提示高亮
⑧鼠标划过菜单提示时,原先的高亮消失,鼠标划过的栏目获得高亮;鼠标移开,保持之前鼠标划过的栏目高亮,直到用户继续输入字符,栏目失去高亮,同时第一条可见的提示高亮
⑨点击高亮提示或者高亮处回车,自动填充邮箱元素,选择完成,菜单收起
⑩在下拉菜单展开时,鼠标点击页面任何元素,菜单收起
注:此功能不包括正则验证邮箱格式。
本功能没有用到什么复杂的知识点,只有简单的JS字符串的匹配 ( match )、位置检索 ( indexOf ) 、截取 ( substr ) 以及jQuery选择器的应用等。更主要的是对功能的逻辑分析要思路清晰,以及思考如何设计能真正使用户操作简便。这个功能的事件主要包括:
代码分析:
HTML和CSS代码:
HTML
1
2
3
4
|
< div class = "ipt" >
< input type = "text" name = "uemail" id = "uemail" placeholder = "请输入常用邮箱" />
< ul class = "autoul" ></ ul >
</ div >
|
CSS
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
|
.ipt{ width : 218px ;} /* 输入框外围div */
input{ margin : 0 ; padding-left : 15px ; padding-right : 15px ; width : 184px ;} /* 输入框 */
ul.autoul{ /* 下拉菜单 */
width : 216px ;
margin : 0px ;
margin-top : -5px ;
padding : 0px ;
color : #666 ;
border : 1px solid #666 ;
border-top : 0 ;
overflow : hidden ;
}
ul.autoul li.autoli{ /* 下拉菜单li */
height : 30px ;
display : block ;
list-style-type : none ;
text-align : left ;
cursor : pointer ;
font-size : 14px ;
line-height : 30px ;
padding-left : 15px ;
padding-right : 15px ;
overflow : hidden ;
/*当对象内文本溢出时显示省略标记*/
text- overflow :ellipsis;
}
.lihover{ /* 下拉菜单li高亮样式 */
background-color : #eee ;
}
span{ padding-right : 2px ;} /* li下文字 */
.showli{ display : block ;} /* 经过过滤的li的样式 */
|
在DOM加载完毕后,初始化邮件列表并隐藏下拉菜单,JS代码片段:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
|
$( function (){
//初始化邮箱列表
var mail = new Array( "sina.com" , "126.com" , "163.com" , "gmail.com" , "qq.com" , "vip.qq.com" , "hotmail.com" , "sohu.com" , "139.com" , "vip.sina.com" , "21cn.cn" , "189.cn" , "sina.cn" );
//把邮箱列表加入下拉
for ( var i=0;i<mail.length;i++){
var $liElement = $( "<li class=\"autoli\"><span class=\"ex\"></span><span class=\"at\">@</span><span class=\"step\">" +mail[i]+ "</span></li>" );
$liElement.appendTo( "ul.autoul" );
}
//下拉菜单初始隐藏
$( ".autoul" ).hide();
//其他事件..........
});
|
输入框$("#uemail")的keyup和keydown事件是该功能的主要事件,输入字符时之所以用keyup事件而不用keydown事件是因为keyup事件在用户将键盘按键抬起时触发,是整个按键动作的最后阶段,所以在抬起按键时能看到相应的动作即实时看到输入的字符;而使用keydown事件时,只有在下一次按下键盘按键时才能看到上一次按下按键触发的动作,所以keydown事件在通过键盘获得页面控制方面用的比较多,因此在使用键盘方向键↑↓选择菜单提示时使用keydown事件。
1
2
3
4
5
|
//在邮箱输入框输入字符
$( "#uemail" ).keyup( function (){
//........
})
|
邮件输入框$("#uemail")的keyup事件:
①有输入的按键,使输入框有长度( 因为删除事件也是判断匹配以及长度,所以只需要添加删除事件特有的动作,而不必在此处排除删除事件 )
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
|
if (event.keyCode!=38 && event.keyCode!=40 && event.keyCode!=13){
//菜单展现,需要排除空格开头和"@"开头
if ( $.trim($( this ).val())!= "" && $.trim( this .value).match(/^@/)== null ) {
$( ".autoul" ).show();
//同时去掉原先的高亮,把第一条提示高亮
if ($( ".autoul li.lihover" ).hasClass( "lihover" )) {
$( ".autoul li.lihover" ).removeClass( "lihover" );
}
$( ".autoul li:visible:eq(0)" ).addClass( "lihover" );
} else { //如果为空或者"@"开头
$( ".autoul" ).hide();
$( ".autoul li:eq(0)" ).removeClass( "lihover" );
}
//把输入的字符填充进提示,有两种情况:1.出现"@"之前,把"@"之前的字符进行填充;2.出现第一次"@"时以及"@"之后还有字符时,不填充
//出现@之前
if ($.trim( this .value).match(/[^@]@/)== null ){ //输入了不含"@"的字符或者"@"开头
if ($.trim( this .value).match(/^@/)== null ){
//不以"@"开头
$( this ).next().children( "li" ).children( ".ex" ).text($( this ).val());
}
} else {
//输入字符后,第一次出现了不在首位的"@"
//当首次出现@之后,有2种情况:1.继续输入;2.没有继续输入
//当继续输入时
var str = this .value; //输入的所有字符
var strs = new Array();
strs = str.split( "@" ); //输入的所有字符以"@"分隔
$( ".ex" ).text(strs[0]); //"@"之前输入的内容
var len = strs[0].length; //"@"之前输入内容的长度
if ( this .value.length>len+1){
//截取出@之后的字符串,@之前字符串的长度加@的长度,从第(len+1)位开始截取
var strright = str.substr(len+1);
//正则屏蔽匹配反斜杠"\"
if (strright.match(/[\\]/)!= null ){
strright.replace(/[\\]/," ");
return false;
}
//遍历li
$(" ul.autoul li ").each(function(){
//遍历span
//$(this) li
$(this).children(" span.step ").each(function(){
//@之后的字符串与邮件后缀进行比较
//当输入的字符和下拉中邮件后缀匹配并且出现在第一位出现
//$(this) span.step
if($(" ul.autoul li ").children(" span.step ").text().match(strright)!=null && $(this).text().indexOf(strright)==0){
//class showli是输入框@后的字符和邮件列表对比匹配后给匹配的邮件li加上的属性
$(this).parent().addClass(" showli ");
//如果输入的字符和提示菜单完全匹配,则去掉高亮和showli,同时提示隐藏
if(strright.length>=$(this).text().length){
$(this).parent().removeClass(" showli ").removeClass(" lihover ").hide();
}
}else{
$(this).parent().removeClass(" showli ");
}
if($(this).parent().hasClass(" showli ")){
$(this).parent().show();
$(this).parent(" li ").parent(" ul ").children(" li.showli:eq(0) ").addClass(" lihover ");
}else{
$(this).parent().hide();
$(this).parent().removeClass(" lihover ");
}
});
});
}else{
//" @ "后没有继续输入时
$(" .autoul ").children().show();
$(" ul.autoul li ").removeClass(" showli ");
$(" ul.autoul li.lihover ").removeClass(" lihover ");
$(" ul.autoul li:eq(0) ").addClass(" lihover");
}
}
} //有效输入按键事件结束
|
②按键为backspace(8)或是delete(46)
1
2
3
4
5
|
if(event.keyCode == 8 || event.keyCode == 46){
$(this).next().children().removeClass("lihover");
$(this).next().children("li:visible:eq(0)").addClass("lihover");
}//删除事件结束
|
③按键为方向键↑(38)
1
2
3
4
|
if (event.keyCode == 38){
//使光标始终在输入框文字右边
$( this ).val($( this ).val());
} //方向键↑结束
|
④按键为回车键(13)
1
2
3
4
5
6
7
8
9
10
11
|
if (event.keyCode == 13){
if ($( "ul.autoul li" ).is( ".lihover" )) {
$( "#uemail" ).val($( "ul.autoul li.lihover" ).children( ".ex" ).text() + "@" + $( "ul.autoul li.lihover" ).children( ".step" ).text());
}
$( ".autoul" ).children().hide();
$( ".autoul" ).children().removeClass( "lihover" );
$( "#uemail" ).focus(); //回车后输入栏获得焦点
}
|
至此keyup事件结束。
#("#uemail")的keydown事件
1
2
3
4
5
6
7
8
9
10
11
|
$( "#uemail" ).keydown( function (){
if (event.keyCode == 40){
//按键为↓时....
}
if (event.keyCode == 38){
//按键为↑时....
}
})
|
⑤按键为方向键 ↓ (40)
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
|
if (event.keyCode == 40){
//当键盘按下↓时,如果已经有li处于被选中的状态,则去掉状态,并把样式赋给下一条(可见的)li
if ($( "ul.autoul li" ).is( ".lihover" )) {
//如果还存在下一条(可见的)li的话
if ($( "ul.autoul li.lihover" ).nextAll().is( "li:visible" )) {
if ($( "ul.autoul li.lihover" ).nextAll().hasClass( "showli" )) {
$( "ul.autoul li.lihover" ).removeClass( "lihover" )
.nextAll( ".showli:eq(0)" ).addClass( "lihover" );
} else {
$( "ul.autoul li.lihover" ).removeClass( "lihover" ).removeClass( "showli" )
.next( "li:visible" ).addClass( "lihover" );
$( "ul.autoul" ).children().show();
}
} else {
$( "ul.autoul li.lihover" ).removeClass( "lihover" );
$( "ul.autoul li:visible:eq(0)" ).addClass( "lihover" );
}
}
}
|
⑥按键为方向键 ↑ (38)
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
|
if (event.keyCode == 38){
//当键盘按下↓时,如果已经有li处于被选中的状态,则去掉状态,并把样式赋给下一条(可见的)li
if ($( "ul.autoul li" ).is( ".lihover" )){
//如果还存在上一条(可见的)li的话
if ($( "ul.autoul li.lihover" ).prevAll().is( "li:visible" )){
if ($( "ul.autoul li.lihover" ).prevAll().hasClass( "showli" )){
$( "ul.autoul li.lihover" ).removeClass( "lihover" )
.prevAll( ".showli:eq(0)" ).addClass( "lihover" );
} else {
$( "ul.autoul li.lihover" ).removeClass( "lihover" ).removeClass( "showli" )
.prev( "li:visible" ).addClass( "lihover" );
$( "ul.autoul" ).children().show();
}
} else {
$( "ul.autoul li.lihover" ).removeClass( "lihover" );
$( "ul.autoul li:visible:eq(" +($( "ul.autoul li:visible" ).length-1)+ ")" ).addClass( "lihover" );
}
} else {
//当键盘按下↓时,如果之前没有一条li被选中的话,则第一条(可见的)li被选中
$( "ul.autoul li:visible:eq(" +($( "ul.autoul li:visible" ).length-1)+ ")" ).addClass( "lihover" );
}
}
|
至此keydown事件结束。
⑦当鼠标点击下拉菜单的具体一条内容时
1
2
3
4
5
6
|
$( ".autoli" ).click( function (){
$( "#uemail" ).val($( this ).children( ".ex" ).text()+$( this ).children( ".at" ).text()+$( this ).children( ".step" ).text());
$( ".autoul" ).hide();
});
//鼠标点击下拉菜单具体内容事件结束
|
⑧当鼠标点击document时,下拉隐藏
1
2
3
4
5
|
$( "body" ).click( function (){
$( ".autoul" ).hide();
});
//鼠标点击document事件结束
|
⑨鼠标划过li时
1
2
3
4
5
6
7
8
|
$( "ul.autoul li" ).hover( function (){
if ($( "ul.autoul li" ).hasClass( "lihover" )){
$( "ul.autoul li" ).removeClass( "lihover" );
}
$( this ).addClass( "lihover" );
});
|
至此,功能完成。