原生JS实现邮箱输入框自动补全与智能提示插件

作者:青柠时间:2026-10-27 19:19:41浏览:278

本文介绍了一个轻量级的JavaScript邮箱自动补全插件。该插件无需复杂HTML结构,仅需指定输入框及父级容器类名即可运行。支持键盘上下键导航、鼠标点击及回车选择,具备精确匹配后缀、动态渲染下拉列表及隐藏域数据同步功能,适用于注册表单等场景。

插件简介与HTML结构要求

该插件设计简洁,无需预先编写特定的HTML标签,只需确保页面中存在一个具备对应class类名的输入框,且其父级元素拥有指定的class类名即可。其余的HTML结构均由插件内部自动生成。

基础HTML结构示例如下:

代码实现如下:

<div class="parentCls">
<input type="text" class="inputElem">
</div>

实际上,外层的div标签并非必需。开发者只需在input输入框及其父级元素上添加相应的class即可(类名可自定义,但需在JS初始化时传入)。默认情况下,父级class命名为parentCls,输入框class为inputElem,隐藏域class为hiddenCls。初始化时直接传入空对象即可使用。考虑到页面可能存在多个输入框,通过父级class进行区分,并配合隐藏域存储数据以便后端开发。

配置项中包含一个邮箱数组参数 mailArr : ["@qq.com","@qq2.com","@gmail.com","@126.com","@163.com","@hotmail.com","@yahoo.com","@yahoo.com.cn","@live.com","@sohu.com","@sina.com"]。该数组定义了默认显示的邮箱后缀提示。无论用户输入什么,下拉框初始化时都会显示这些邮箱提示;当用户输入精确到某一项时,下拉框会进一步筛选并显示精确匹配的结果。当然,根据需求变更,该邮箱参数可在初始化时自定义配置。

代码风格保持与前作一致。

实现的具体功能包括:

1. 支持键盘上下键操作,同时支持鼠标点击及按回车键进行选择。

2. 点击document时,除当前input输入框外,其他下拉框自动隐藏。再次输入时,实现自动匹配等操作。

具体逻辑不再赘述,其效果类似于网上注册时的邮箱自动提示功能。如有任何bug,欢迎留言反馈。时间已晚,直接贴出代码:

CSS样式定义

CSS样式代码如下:

代码实现如下:

<style>
*{margin:0;padding:0;}
ul,li{list-style:none;}
.inputElem {width:198px;height:22px;line-height:22px;border:1px solid #ff4455;}
.parentCls{width:200px;}
.auto-tip li{width:100%;height:22px;line-height:22px;font-size:14px;}
.auto-tip li.hoverBg{background:#ddd;cursor:pointer;}
.red{color:red;}
.hidden {display:none;}
</style>

JavaScript核心实现逻辑

JS核心代码如下:

代码实现如下:

/**
* 邮箱自动提示插件
* @constructor EmailAutoComplete
* @ options {object} 可配置项
 */
function EmailAutoComplete(options) {
 this.config = {
 targetCls : '.inputElem', // 目标input元素
 parentCls : '.parentCls', // 当前input元素的父级类
 hiddenCls : '.hiddenCls', // 当前input隐藏域 
 searchForm : '.jqtransformdone', //form表单
 hoverBg : 'hoverBg', // 鼠标移上去的背景
 inputValColor : 'red', // 输入框输入提示颜色
 mailArr : ["@qq.com","@qq2.com","@gmail.com","@126.com","@163.com","@hotmail.com","@yahoo.com","@yahoo.com.cn","@live.com","@sohu.com","@sina.com"], //邮箱数组
 isSelectHide : true, // 点击下拉框 是否隐藏 默认为true
 callback : null // 点击某一项回调函数
 };
 this.cache = {
 onlyFlag : true, // 只渲染一次
 currentIndex : -1,
 oldIndex : -1
 };
 this.init(options);
 }
EmailAutoComplete.prototype = {
 constructor: EmailAutoComplete,
init: function(options){
 this.config = $.extend(this.config,options || {});
var self = this,
 _config = self.config,
 _cache = self.cache;
 $(_config.targetCls).each(function(index,item){
 $(item).keyup(function(e){
 var target = e.target,
 targetVal = $.trim($(this).val()),
 keycode = e.keyCode,
 elemHeight = $(this).outerHeight(),
 elemWidth = $(this).outerWidth(),
 parentNode = $(this).closest(_config.parentCls);
 $(parentNode).css({'position':'relative'});
 // 如果输入框值为空的话 那么下拉框隐藏
 if(targetVal == '') {
 $(item).attr({'data-html':''});
 // 给隐藏域赋值
 $(_config.hiddenCls,parentNode).val('');
_cache.currentIndex = -1;
 _cache.oldIndex = -1;
 $(".auto-tip",parentNode) && !$(".auto-tip",parentNode).hasClass('hidden') && $(".auto-tip",parentNode).addClass('hidden');
 self._removeBg(parentNode);
 }else {
 $(item).attr({'data-html':targetVal});
// 给隐藏域赋值
 $(_config.hiddenCls,parentNode).val(targetVal);
 $(".auto-tip",parentNode) && $(".auto-tip",parentNode).hasClass('hidden') && $(".auto-tip",parentNode).removeClass('hidden');
 // 渲染下拉框内容
 self._renderHTML({keycode:keycode,e:e,target:target,targetVal:targetVal,height:elemHeight,width:elemWidth,parentNode:parentNode});
 }
 
 });
 });
 // 阻止form表单默认enter键提交
 $(_config.searchForm).each(function(index,item) {
 $(item).keydown(function(e){
 var keyCode = e.keyCode;
 if(keyCode == 13) {
 return false;
 }
 });
 });
// 点击文档document时候 下拉框隐藏掉
 $(document).click(function(e){
 e.stopPropagation();
 var target = e.target,
 tagCls = _config.targetCls.replace(/^\./,'');
if(!$(target).hasClass(tagCls)) {
 $('.auto-tip') && $('.auto-tip').each(function(index,item){
 !$(item).hasClass('hidden') && $(item).addClass('hidden');
 });
 }
 });
 },
 /*
 * 渲染下拉框提示内容
 * @param cfg{object}
 */
 _renderHTML: function(cfg) {
 var self = this,
 _config = self.config,
 _cache = self.cache,
 curVal;
 var curIndex = self._keyCode(cfg.keycode);
 $('.auto-tip',cfg.parentNode).hasClass('hidden') && $('.auto-tip',cfg.parentNode).removeClass('hidden');
 if(curIndex > -1){
 // 键盘上下操作
 self._keyUpAndDown(cfg.targetVal,cfg.e,cfg.parentNode);
 }else {
 if(/@/.test(cfg.targetVal)) {
 curVal = cfg.targetVal.replace(/@.*/,'');
 }else {
 curVal = cfg.targetVal;
 }
 if(_cache.onlyFlag) {
 $(cfg.parentNode).append('<input type="hidden" class="hiddenCls"/>');
 var wrap = '<ul class="auto-tip">';
for(var i = 0; i < _config.mailArr.length; i++) {
wrap += '<li class="p-index'+i+'">'+'<span class="output-num"></span><em class="em" data-html="'+_config.mailArr[i]+'">'+_config.mailArr[i]+'</em></li>';
 }
 wrap += '</ul>';
 _cache.onlyFlag = false;
 $(cfg.parentNode).append(wrap);
 $('.auto-tip',cfg.parentNode).css({'position':'absolute','top':cfg.height,'width':cfg.width - 2 + 'px','left':0,
 'border':'1px solid #ccc','z-index':10000});
 }
 // 给所有li添加属性 data-html
 $('.auto-tip li',cfg.parentNode).each(function(index,item){
 $('.output-num',item).html(curVal);
 !$('.output-num',item).hasClass(_config.inputValColor) && 
 $('.output-num',item).addClass(_config.inputValColor);
 var emVal = $.trim($('.em',item).attr('data-html'));
 $(item).attr({'data-html':curVal + '' +emVal});
 });
// 精确匹配内容
 self._accurateMate({target:cfg.target,parentNode:cfg.parentNode});
// 鼠标移到某一项li上面时候
 self._itemHover(cfg.parentNode);
 // 点击对应的项时
 self._executeClick(cfg.parentNode);
 }
 },
 /**
 * 精确匹配某项内容
 */
 _accurateMate: function(cfg) {
 var self = this,
 _config = self.config,
 _cache = self.cache;
 var curVal = $.trim($(cfg.target,cfg.parentNode).attr('data-html')),
 newArrs = [];
 if(/@/.test(curVal)) {
 // 获得@ 前面 后面的值
 var prefix = curVal.replace(/@.*/, ""),
 suffix = curVal.replace(/.*@/, "");
$.map(_config.mailArr,function(n){
 var reg = new RegExp(suffix);
 if(reg.test(n)) {
 newArrs.push(n);
 }
 });
 if(newArrs.length > 0) {
 $('.auto-tip',cfg.parentNode).html('');
 $(".auto-tip",cfg.parentNode) && $(".auto-tip",cfg.parentNode).hasClass('hidden') && 
 $(".auto-tip",cfg.parentNode).removeClass('hidden');
var html = '';
 for(var j = 0, jlen = newArrs.length; j < jlen; j++) {
 html += '<li class="p-index'+j+'">'+'<span class="output-num"></span><em class="em" data-html="'+newArrs[j]+'">'+newArrs[j]+'</em></li>';
 }
 $('.auto-tip',cfg.parentNode).html(html);
 // 给所有li添加属性 data-html
 $('.auto-tip li',cfg.parentNode).each(function(index,item){
 $('.output-num',item).html(prefix);
 !$('.output-num',item).hasClass(_config.inputValColor) && 
 $('.output-num',item).addClass(_config.inputValColor);
var emVal = $.trim($('.em',item).attr('data-html'));
 $(item).attr('data-html','');
 $(item).attr({'data-html':prefix + '' +emVal});
 });
// 精确匹配到某项时候 让当前的索引等于初始值
 _cache.currentIndex = -1;
 _cache.oldIndex = -1;
 $('.auto-tip .output-num',cfg.parentNode).html(prefix);
// 鼠标移到某一项li上面时候
 self._itemHover(cfg.parentNode);
// 点击对应的项时
 self._executeClick(cfg.parentNode);
 }else {
 $(".auto-tip",cfg.parentNode) && !$(".auto-tip",cfg.parentNode).hasClass('hidden') && 
 $(".auto-tip",cfg.parentNode).addClass('hidden');
 $('.auto-tip',cfg.parentNode).html('');
 }
 }
 },
 /*
 * 鼠标移到某一项li上时
 */
 _itemHover: function(parentNode) {
 var self = this,
 _config = self.config,
 _cache = self.cache;
 $('.auto-tip li',parentNode).hover(function(index,item) {
 !$(this).hasClass(_config.hoverBg) && $(this).addClass(_config.hoverBg);
 },function() {
 $(this).hasClass(_config.hoverBg) && $(this).removeClass(_config.hoverBg);
 });
 },
 /*
 * 当输入框值为空时候 li项都删掉class hoverBg
 */
 _removeBg: function(parentNode){
 var self = this,
 _config = self.config;
$(\".auto-tip li\",parentNode).each(function(index,item){
 $(item).hasClass(_config.hoverBg) && $(item).removeClass(_config.hoverBg);
 }); 
 },
 /**
 * 键盘上下键操作
 */
 _keyUpAndDown: function(targetVal,e,parentNode) {
 var self = this,
 _cache = self.cache,
 _config = self.config;
// 如果请求成功后 返回了数据(根据元素的长度来判断) 执行以下操作
 if($('.auto-tip' + ' li',parentNode) && $('.auto-tip' + ' li').length > 0) {
var plen = $('.auto-tip' + ' li',parentNode).length,
 keyCode = e.keyCode;
 _cache.oldIndex = _cache.currentIndex;
// 上移操作
 if(keyCode == 38) {
 if(_cache.currentIndex == -1) {
 _cache.currentIndex = plen - 1;
 }else {
 _cache.currentIndex = _cache.currentIndex - 1;
 if(_cache.currentIndex < 0) {
 _cache.currentIndex = plen - 1;
 }
 }
 if(_cache.currentIndex !== -1) {
!$('.auto-tip .p-index'+_cache.currentIndex,parentNode).hasClass(_config.hoverBg) &&
 $('.auto-tip .p-index'+_cache.currentIndex,parentNode).addClass(_config.hoverBg).siblings().removeClass(_config.hoverBg);
var curAttr = $('.auto-tip' + ' .p-index'+_cache.currentIndex,parentNode).attr('data-html');
 $(_config.targetCls,parentNode).val(curAttr);
 // 给隐藏域赋值
 $(_config.hiddenCls,parentNode).val(curAttr);
 }
}else if(keyCode == 40) { //下移操作
 if(_cache.currentIndex == plen - 1) {
 _cache.currentIndex = 0;
 }else {
 _cache.currentIndex++;
 if(_cache.currentIndex > plen - 1) {
 _cache.currentIndex = 0;
 }
 }
if(_cache.currentIndex !== -1) {
 !$('.auto-tip .p-index'+_cache.currentIndex,parentNode).hasClass(_config.hoverBg) &&
 $('.auto-tip .p-index'+_cache.currentIndex,parentNode).addClass(_config.hoverBg).siblings().removeClass(_config.hoverBg);
 var curAttr = $('.auto-tip' + ' .p-index'+_cache.currentIndex,parentNode).attr('data-html');
 $(_config.targetCls,parentNode).val(curAttr);
 // 给隐藏域赋值
 $(_config.hiddenCls,parentNode).val(curAttr);
 }
 }else if(keyCode == 13) { //回车操作
 var curVal = $('.auto-tip' + ' .p-index'+_cache.oldIndex,parentNode).attr('data-html');
 $(_config.targetCls,parentNode).val(curVal);
 // 给隐藏域赋值
 $(_config.hiddenCls,parentNode).val(curVal);
if(_config.isSelectHide) {
 !$(\".auto-tip\",parentNode).hasClass('hidden') && $(\".auto-tip\",parentNode).addClass('hidden');
 }
 _config.callback && $.isFunction(_config.callback) && _config.callback();
_cache.currentIndex = -1;
 _cache.oldIndex = -1;
 }
 }
 },
 _keyCode: function(code) {
 var arrs = ['17','18','38','40','37','39','33','34','35','46','36','13','45','44','145','19','20','9'];
 for(var i = 0, ilen = arrs.length; i < ilen; i++) {
 if(code == arrs[i]) {
 return i;
 }
 }
 return -1;
 },
 /**
 * 当数据相同的时 点击对应的项时 返回数据
 */
 _executeClick: function(parentNode) {
 var _self = this,
 _config = _self.config;
$('.auto-tip' + ' li',parentNode).unbind('click');
 $('.auto-tip' + ' li',parentNode).bind('click',function(e){
 var dataAttr = $(this).attr('data-html');
$(_config.targetCls,parentNode).val(dataAttr);
 if(_config.isSelectHide) {
 !$(\".auto-tip\",parentNode).hasClass('hidden') && $(\".auto-tip\",parentNode).addClass('hidden');
 }
 // 给隐藏域赋值
 $(_config.hiddenCls,parentNode).val(dataAttr);
 _config.callback && $.isFunction(_config.callback) && _config.callback();
 });
 }
};
// 初始化
$(function() {
 new EmailAutoComplete({});
});
作者最新文章