JavaScript实现邮箱后缀提示功能的示例代码
先来个基础的
需求
根据下面需求实现如示意图所示的邮箱输入提示功能,注意,根据要求只需实现下面功能
当用户没有任何输入时,提示框消失
当用户输入字符后,显示提示框,并且把用户输入的内容自动拼上邮箱后缀进行显示
暂时不用考虑示意图中的红色和蓝色背景色的逻辑
注意用户输入中前后空格需要去除
小优化编码
需求
如果我们输入的是 abc@1,这个时候出现的提示框内容是
abc@1@163.com
abc@1@gmail.com
abc@1@126.com
……
很明显,上面的提示框不是一个符合用户需求的提示,我们需要做一些优化:
当用户输入含有 @ 符号时,我们选取用户输入的@前面的字符来和后缀拼接
需求
这下出现的提示好多了,不过用户如果已经输入了@1,说明他大概率要输入163或者126,我们需要让我们的提示更加符合用户的期望。满足以下需求:
当用户输入了 @ 及部分后缀时,只从 postfixList 选取符合用户输入预期的后缀,我们以前缀匹配为要求。
当用户输入不满足任何前缀匹配时,则显示全部提示
测试用例
输入a@1->出现提示框,提示a@163.com, a@126.com
输入a@g->出现提示框,提示a@gmail.com
输入a@2->出现提示框,提示a@263.net
输入a@qq->出现提示框,提示a@qq.com
输入a@163.->出现提示框,提示a@163.com
输入a@126.com->出现提示框,提示a@126.com
输入a@qq.com (两个空格)->出现提示框,提示a@qq.com
输入a@qq.comm->出现提示框,出现全部提示 代码1
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 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 | <!DOCTYPE html> <html> <head> <meta charset="utf-8" /> <title>邮箱后缀提示1-完成基本提示</title> </head> <body> <div class="wrapper"> <input type="text" id="input-email"> <ul class="email-sug" id="email-sug-wrapper"> </ul> </div> <script> var postfixList = ["163.com", "gmail.com", "126.com", "qq.com", "263.net"]; var txt = document.getElementById("input-email"); var sug = document.getElementById("email-sug-wrapper"); // keys.addEventListener("keyup",function(){ // console.log("event handle1"); // }) // keys.addEventListener("keypress",function(){ // console.log("event handle2"); // }) // keys.addEventListener("keydown",function(){ // console.log("event handle3"); // }) // keys.addEventListener("input",function(){ // console.log("event handle4"); // }) //经过查看各个效果,oninput效果最符合需求。 txt.oninput = function () { console.log("event handle4"); judge(); add(); } function getText() { var inputText = txt.value.trim(); return inputText; } //判断是否生成新的数组 function postlist() { var userinput = getText(); var newpostlist = new Array(); if (userinput.search('@') != 0) { var len = userinput.search('@'); //用来拼接的用户输入内容 = 只使用@之后的字符串 var x = userinput.substring(len + 1, userinput.length); //取@之后的部分 for (var i = 0; i < postfixList.length; i++) { if (postfixList[i].search(x) == 0) { newpostlist.push(postfixList[i]); } } //若@后面没有字符或者新数组newpostlist为空,就返回原来的postfixlist if (x === '' || newpostlist == '') { return postfixList; } return newpostlist; } else { return postfixList; } } //根据输入内容和匹配来生成提示数组 function promptContent() { var x = getText(); var tips = new Array(); if (x.indexOf("@") != -1) { var p = x.slice(0, x.indexOf("@")); for (i = 0; i < postlist().length; i++) { tips[i] = p + "@" + postlist()[i]; } } else { for (i = 0; i < postfixList.length; i++) { tips[i] = x + "@" + postfixList[i]; } } return tips; } //添加提示数组进入li function add() { var sug = document.getElementById("email-sug-wrapper"); var tips = promptContent(); while (sug.hasChildNodes()) { sug.removeChild(sug.firstChild); } //将之前的列表清除掉,然后重新生成新的列表 for (i = 0; i < tips.length; i++) { var tip_li = document.createElement("li"); tip_li.innerHTML = tips[i]; sug.appendChild(tip_li); } } function judge() { //判空,是“”没有内容,不能为“ ” if (getText() == "") { hide(); } else { display(); } } function hide() { sug.style.display = "none"; } function display() { sug.style.display = "block"; } </script> </body> </html> |
新的需求编码
需求
上面我们只完成了提示,但提示还没有直接作用到选择中,我们现在完成以下需求:
使用CSS实现:鼠标滑过提示框的某一个提示时,这个提示内容背景色变化,表示鼠标经过了这个DOM节点
鼠标如果点击某个提示,则提示内容进入输入框,同时提示框消失
在上个步骤结束后,在输入框中任意再输入字符或删除字符,则重新开始出现提示框
需求
尝试在输入框中输入<b>,看看提示框发生了什么
阅读
Web安全之XSS攻防
javascript对HTML字符转义与反转义
设计
我们需要在两个地方进行处理,一个是在生成提示内容那里,对于特殊字符进行转义编码,另一个是在把鼠标点击的提示框内容转回输入框时进行解码。
代码2
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 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 | <!DOCTYPE html> <html> <head> <meta charset="utf-8" /> <title>邮箱后缀提示2-添加样式和监听鼠标点击和转码内容</title> <style> #input-email{ width: 300px; height: 30px; } .email-sug{ width: 300px; list-style: none; padding: 0px; margin: 0px; border: 2px solid rgba(134, 132, 132,0.3); border-top:none; display: none; /* 初始不显示,避免边框出现 */ } .email-sug li{ width: 300px; height: 30px; background-color: #ffffff; color: darkgrey; line-height: 30px; } .email-sug li:hover{ background-color:pink; } </style> </head> <body> <div class="wrapper"> <input type="text" id="input-email"> <ul class="email-sug" id="email-sug-wrapper"> </ul> </div> <script> var postfixList = ["163.com", "gmail.com", "126.com", "qq.com", "263.net"]; var txt = document.getElementById("input-email"); var sug = document.getElementById("email-sug-wrapper"); sug.addEventListener("click",function(ev){ //采用事件代理,监听父级点击事件,通过target获取当前li var ev=ev||window.event; var target=ev.target||ev.srcElement; if(target.nodeName.toLowerCase()=="li"){ hide(); return txt.value=htmlDecode( target.innerHTML); //解码 //return txt.value= target.innerHTML; } }) txt.oninput = function () { console.log("event handle4"); judge(); add(); } function getText() { var inputText = txt.value.trim(); return inputText; } //判断是否生成新的数组 function postlist() { var userinput = getText(); var newpostlist = new Array(); if (userinput.search('@') != 0) { var len = userinput.search('@'); //用来拼接的用户输入内容 = 只使用@之后的字符串 var x = userinput.substring(len + 1, userinput.length); //取@之后的部分 for (var i = 0; i < postfixList.length; i++) { if (postfixList[i].search(x) == 0) { newpostlist.push(postfixList[i]); } } //若@后面没有字符或者新数组newpostlist为空,就返回原来的postfixlist if (x === '' || newpostlist == '') { return postfixList; } return newpostlist; } else { return postfixList; } } //根据输入内容和匹配来生成提示数组 function promptContent() { var x = htmlEncode(getText()) //转码; // var x=getText(); var tips = new Array(); if (x.indexOf("@") != -1) { var p = x.slice(0, x.indexOf("@")); for (i = 0; i < postlist().length; i++) { tips[i] = p + "@" + postlist()[i]; } } else { for (i = 0; i < postfixList.length; i++) { tips[i] = x + "@" + postfixList[i]; } } return tips; } //添加提示数组进入li function add() { var sug = document.getElementById("email-sug-wrapper"); var tips = promptContent(); while (sug.hasChildNodes()) { sug.removeChild(sug.firstChild); } //将之前的列表清除掉,然后重新生成新的列表 for (i = 0; i < tips.length; i++) { var tip_li = document.createElement("li"); tip_li.innerHTML = tips[i]; sug.appendChild(tip_li); } } function judge() { //判空,是“”没有内容,不能为“ ” if (getText() == "") { hide(); } else { display(); } } function hide() { sug.style.display = "none"; } function display() { sug.style.display = "block"; } /*1.用浏览器内部转换器实现html转码*/ function htmlEncode(html){ //1.首先动态创建一个容器标签元素,如DIV var temp = document.createElement ("div"); //2.然后将要转换的字符串设置为这个元素的innerText(ie支持)或者textContent(火狐,google支持) (temp.textContent != undefined ) ? (temp.textContent = html) : (temp.innerText = html); //3.最后返回这个元素的innerHTML,即得到经过HTML编码转换的字符串了 var output = temp.innerHTML; temp = null; return output; } /*2.用浏览器内部转换器实现html解码*/ function htmlDecode(text){ //1.首先动态创建一个容器标签元素,如DIV var temp = document.createElement("div"); //2.然后将要转换的字符串设置为这个元素的innerHTML(ie,火狐,google都支持) temp.innerHTML = text; //3.最后返回这个元素的innerText(ie支持)或者textContent(火狐,google支持),即得到经过HTML解码的字符串了。 var output = temp.innerText || temp.textContent; temp = null; return output; } </script> </body> </html> |
加上键盘
需求
我们给提示框加上3个按键的功能,分别是回车和上下键,使得可以通过键盘操作进行提示框的选择
当有提示框的时候,默认第一个提示为被选择状态,用一个和鼠标滑过不一样的背景色来标识
当有输入框的时候,按上键,可以向上移动选择状态,如果按键之前的被选择提示是第一个,则被选状态移到最下面一个
当有输入框的时候,按下键,可以向下移动选择状态,如果按键之前的被选择提示是最后一个,则被选状态移到第一个
当有输入框时,按回车键,则将当前被选中状态的提示内容,放到输入框中,并隐藏提示框
当没有输入框的时候,这3个键盘按键无响应
当用户输入发生改变的时候,选择状态都重新切回到第一个提示
优化体验
需求
当我们进入页面,或者当我们点击鼠标进行提示选择后,输入框的焦点就不在了,所以请你优化一下用户体验:
一进入页面就将焦点放在输入框中
用户点击鼠标,进行提示选择后,焦点依然在输入框中
用户按ESC键的时候,对用户输入进行全选
代码3
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 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 | <!DOCTYPE html> <html> <head> <meta charset="utf-8" /> <title>邮箱后缀提示3-添加键盘响应及输入框焦点优化</title> <style> #input-email{ width: 300px; height: 30px; } .email-sug{ width: 300px; list-style: none; padding: 0px; margin: 0px; border: 2px solid rgba(134, 132, 132,0.3); border-top:none; display: none; /* 初始不显示,避免边框出现 */ } .email-sug li{ width: 300px; height: 30px; background-color: #ffffff; color: darkgrey; line-height: 30px; overflow: hidden; padding-left: 10px; box-sizing: border-box; } .email-sug li:hover{ background-color:skyblue; } .email-sug li.active{ background-color:pink; } </style> </head> <body> <div class="wrapper"> <input type="text" id="input-email" autofocus="autofocus"> <ul class="email-sug" id="email-sug-wrapper"> </ul> </div> <script> var postfixList = ["163.com", "gmail.com", "126.com", "qq.com", "263.net"]; var txt = document.getElementById("input-email"); var sug = document.getElementById("email-sug-wrapper"); var nowSelectTipIndex = 0; //获取输入文本 txt.oninput = function (e) { console.log("event handle4"); //按下的是内容,则重置选中状态,坐标清零,避免光标位置已经计算存入。 if (!(e.keyCode == 40 || e.keyCode == 38 || e.keyCode == 13)) { nowSelectTipIndex = 0; } judge(); add(); } //点击事件响应 sug.addEventListener("click", function (ev) { //采用事件代理,监听父级点击事件,通过target获取当前li var ev = ev || window.event; var target = ev.target || ev.srcElement; if (target.nodeName.toLowerCase() == "li") { hide(); txt.focus(); //写在return之前,不然无效 return txt.value = htmlDecode(target.innerHTML); //解码 //return txt.value= target.innerHTML; } }) //键盘事件响应 document.addEventListener("keydown", function (e) { var e = e || window.event; var key = e.which || e.keyCode; var list = document.getElementsByTagName("li"); //向下键 if (key == 40) { for (i = 0; i < list.length; i++) { list[i].setAttribute("class", ""); } nowSelectTipIndex++; if (nowSelectTipIndex + 1 > list.length) { nowSelectTipIndex = 0; } list[nowSelectTipIndex].setAttribute("class", "active"); } //向上键 if (key == 38) { for (i = 0; i < list.length; i++) { list[i].setAttribute("class", ""); } nowSelectTipIndex--; if (nowSelectTipIndex < 0) { nowSelectTipIndex = list.length - 1; } list[nowSelectTipIndex].setAttribute("class", "active"); } //回车键 if (key == 13) { var x = document.getElementsByClassName("active"); txt.value = htmlDecode(x[0].innerHTML); //用textcontent会去除html标签例如<b>。。 hide(); } if (key == 27) { txt.setSelectionRange(0, -1); //ESC全选上文本框内容 hide(); } }) //获取输入内容,并去除首尾空格 function getText() { var inputText = txt.value.trim(); return inputText; } //判断是否生成新的数组 function postlist() { var userinput = getText(); var newpostlist = new Array(); if (userinput.search('@') != 0) { var len = userinput.search('@'); //用来拼接的用户输入内容 = 只使用@之后的字符串 var x = userinput.substring(len + 1, userinput.length); //取@之后的部分 for (var i = 0; i < postfixList.length; i++) { if (postfixList[i].search(x) == 0) { newpostlist.push(postfixList[i]); } } //若@后面没有字符或者新数组newpostlist为空,就返回原来的postfixlist if (x === '' || newpostlist == '') { return postfixList; } return newpostlist; } else { return postfixList; } } //根据输入内容和匹配来生成提示数组 function promptContent() { var x = htmlEncode(getText()); //转码; // var x=getText(); var tips = new Array(); if (x.indexOf("@") != -1) { var p = x.slice(0, x.indexOf("@")); for (i = 0; i < postlist().length; i++) { tips[i] = p + "@" + postlist()[i]; } } else { for (i = 0; i < postfixList.length; i++) { tips[i] = x + "@" + postfixList[i]; } } return tips; } //添加提示数组进入li function add() { var sug = document.getElementById("email-sug-wrapper"); var tips = promptContent(); while (sug.hasChildNodes()) { sug.removeChild(sug.firstChild); } //将之前的列表清除掉,然后重新生成新的列表 for (i = 0; i < tips.length; i++) { var tip_li = document.createElement("li"); tip_li.innerHTML = tips[i]; sug.appendChild(tip_li); } //初始选择第一项为选中状态,加类名变粉色(需要生成li之后再调用)。 var list = document.getElementsByTagName("li"); list[0].setAttribute("class", "active"); } function judge() { //判空,是“”没有内容,不能为“ ” if (getText() == "") { hide(); } else { display(); } } //控制提示列表隐藏 function hide() { sug.style.display = "none"; } //控制提示列表显示 function display() { sug.style.display = "block"; } /*1.用浏览器内部转换器实现html转码*/ function htmlEncode(html) { //1.首先动态创建一个容器标签元素,如DIV var temp = document.createElement("div"); //2.然后将要转换的字符串设置为这个元素的innerText(ie支持)或者textContent(火狐,google支持) (temp.textContent != undefined) ? (temp.textContent = html) : (temp.innerText = html); //3.最后返回这个元素的innerHTML,即得到经过HTML编码转换的字符串了 var output = temp.innerHTML; temp = null; return output; } /*2.用浏览器内部转换器实现html解码*/ function htmlDecode(text) { //1.首先动态创建一个容器标签元素,如DIV var temp = document.createElement("div"); //2.然后将要转换的字符串设置为这个元素的innerHTML(ie,火狐,google都支持) temp.innerHTML = text; //3.最后返回这个元素的innerText(ie支持)或者textContent(火狐,google支持),即得到经过HTML解码的字符串了。 var output = temp.innerText || temp.textContent; temp = null; return output; } </script> </body> </html> |
最终效果如图: