先来个基础的
需求
- 根据下面需求实现如示意图所示的邮箱输入提示功能,注意,根据要求只需实现下面功能
- 当用户没有任何输入时,提示框消失
- 当用户输入字符后,显示提示框,并且把用户输入的内容自动拼上邮箱后缀进行显示
- 暂时不用考虑示意图中的红色和蓝色背景色的逻辑
- 注意用户输入中前后空格需要去除
小优化编码
需求
如果我们输入的是 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
<!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
<!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 ) "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
<!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) "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>
最终效果如图:
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持。