在做這個功能時候,我參考的是網易注冊(http://reg.163.com/reg/reg.jsp?product=urs)中的郵箱欄目所實現的功能與效果。這個功能的目的是使用戶在填寫郵箱欄目的過程中,在帶有郵箱後綴名的菜單的提示下,可以不需要完整輸入自己的注冊郵箱,達到方便、快捷、友好的效果。在做這個功能的時候,需要充分站在使用者的角度,看怎樣設計能夠最符合通常情況下人們的使用習慣,又怎樣設計反而會讓用戶感到不便。先來看看功能實現的演示效果圖:
在線演示:http://jsfiddle.net/vudr00xc/embedded/result/
①初始狀態,郵箱欄目未獲得焦點時
以及鼠標點擊後獲得焦點時
②以空白字符( space , tab ) 以及@開頭時,不出現提示菜單
③輸入有效字符時,提示下拉菜單出現,用戶輸入的內容自動添加進菜單,並且第一條提示欄目獲得高亮
④輸入的字符第一次出現"@"時,輸入的"@"並不加入提示菜單
⑤"@"之後出現有效字符時,就會同提示菜單中的郵件後綴從後綴首位起進行比較,把和用戶輸入的郵箱後綴不同的提示從菜單中去掉。同時過濾後的第一條提示獲得高亮。在完全匹配之後,下拉菜單隱藏。
⑥用戶刪除郵件輸入框中的字符時,呈現的效果順序是⑤圖3--->⑤圖2---> ⑤圖1---> ④圖
⑦按下鍵盤方向鍵向上或向下使菜單相應提示高亮
⑧鼠標劃過菜單提示時,原先的高亮消失,鼠標劃過的欄目獲得高亮;鼠標移開,保持之前鼠標劃過的欄目高亮,直到用戶繼續輸入字符,欄目失去高亮,同時第一條可見的提示高亮
⑨點擊高亮提示或者高亮處回車,自動填充郵箱元素,選擇完成,菜單收起
⑩在下拉菜單展開時,鼠標點擊頁面任何元素,菜單收起
注:此功能不包括正則驗證郵箱格式。
本功能沒有用到什麼復雜的知識點,只有簡單的JS字符串的匹配 ( match )、位置檢索 ( indexOf ) 、截取 ( substr ) 以及jQuery選擇器的應用等。更主要的是對功能的邏輯分析要思路清晰,以及思考如何設計能真正使用戶操作簡便。這個功能的事件主要包括:
代碼分析:
HTML和CSS代碼:
HTML
<div class="ipt"> <input type="text" name="uemail" id="uemail" placeholder="請輸入常用郵箱"/> <ul class="autoul"></ul> </div>
CSS
.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代碼片段:
$(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事件。
//在郵箱輸入框輸入字符 $("#uemail").keyup(function(){ //........ })
郵件輸入框$("#uemail")的keyup事件:
①有輸入的按鍵,使輸入框有長度( 因為刪除事件也是判斷匹配以及長度,所以只需要添加刪除事件特有的動作,而不必在此處排除刪除事件 )
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)
if(event.keyCode == 8 || event.keyCode == 46){ $(this).next().children().removeClass("lihover"); $(this).next().children("li:visible:eq(0)").addClass("lihover"); }//刪除事件結束
③按鍵為方向鍵↑(38)
if(event.keyCode == 38){ //使光標始終在輸入框文字右邊 $(this).val($(this).val()); }//方向鍵↑結束
④按鍵為回車鍵(13)
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事件
$("#uemail").keydown(function(){ if(event.keyCode == 40){ //按鍵為↓時.... } if(event.keyCode == 38){ //按鍵為↑時.... } })
⑤按鍵為方向鍵 ↓ (40)
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)
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事件結束。
⑦當鼠標點擊下拉菜單的具體一條內容時
$(".autoli").click(function(){ $("#uemail").val($(this).children(".ex").text()+$(this).children(".at").text()+$(this).children(".step").text()); $(".autoul").hide(); }); //鼠標點擊下拉菜單具體內容事件結束
⑧當鼠標點擊document時,下拉隱藏
$("body").click(function(){ $(".autoul").hide(); }); //鼠標點擊document事件結束
⑨鼠標劃過li時
$("ul.autoul li").hover(function(){ if($("ul.autoul li").hasClass("lihover")){ $("ul.autoul li").removeClass("lihover"); } $(this).addClass("lihover"); });
至此,功能完成。