在web开发中,为了提高用户体验,会经常用到输入框的自动完成功能,不仅帮助用户进行快速输入,最重要的是帮助那些“记不全要输入什么”的用户进行选择。这个功能有很多插件已经实现了,为了适应项目的特殊需求,决定自己编写一个具备通用性、扩展性和灵活性的自动完成类,就当是边写边学习了,一举两得。该功能是比较简单的,核心是数据获取方式和导航的实现,简单写了一个,经测试非常好用,还有很多地方需要修改和改进,例如:在原型中只暴露init方法即可,其他方法都需要放到私有空间内,不让用户访问到,这个以后再完善吧。啥也不说了,小二,上菜:

代码如下:(已更新,最新代码请参考:https://github.com/zjh-neverstop/AutoCompleteMulti

 /**
* 实现自动完成功能的js类
* 1、数据获取方式:设置静态数据集、ajax方式、自定义数据获取函数
* 2、可以控制是否启用匹配项的循环选择
* 3、可以控制是否使用默认静态数据集,在获取动态数据失败的情况下会显示
7 */ (function(){ //封装使用频繁的变量,在构造函数中进行初始化
var commonObj = {}; /**
* 构造函数
* @param option
* @constructor
*/
function AutoComplete(option) {
//需要实现自动完成功能的页面控件ID
this.controlId = option.controlId; //匹配结果div的id
this.resultDivId = option.resultDivId; //当前选中的项索引,第一项索引为0
this.index = -1; //静态数据集
this.datas = option.datas; //动态获取的数据集
this.dynamicDatas = null; //服务器端地址
this.serverUrl = option.serverUrl; //匹配结果集合
this.resultDatas = null; //ajax请求数据
this.ajaxRequestData = option.ajaxRequestData; //主要用在一个后台页面处理多个前端自动完成请求的情况,根据此字段调用具体的后台方法
this.actionName = option.actionName; //是否可以循环选择
this.circleChoose = option.circleChoose || "true"; //是否从服务器端获取数据
this.serverEnabled = option.serverEnabled || "false"; //是否使用静态数据
//一般情况下,自动完成都是获取动态数据的,开启这个标志后,在获取动态数据失败的情况下会使用静态数据,默认为false
this.useStaticDatas = option.useStaticDatas||"false"; //驱动函数
this.drivenFuc = null; //自定义数据获取方法
this.getCompleteDatas = function (){
if( typeof option.getCompleteDatas === 'function' ){
return option.getCompleteDatas();
}
else{
return null;
} }; //私有变量,封装后面常用的4个变量
//var commonObj = {}; //通过匿名函数来构造一个类似面向对象语言中的只读属性
//初始化commonObj变量
//注意:匿名函数中的this指向windows对象,这里需要将AutoComplete对象的引用赋值给that
(function(that){
commonObj = {
control : document.getElementById(that.controlId),
results : document.getElementById(that.resultDivId),
jControl : $(document.getElementById(that.controlId)),
jResults : $(document.getElementById(that.resultDivId))
};
})(this); //只读属性
/*this.getCommonObj = function(){
return commonObj;
}*/ } /**
* 原型方法,除了init方法和构造函数外,其他方法均需要私有化,待完善...
*/
AutoComplete.prototype = { //指定构造函数
constructor: AutoComplete, //获取事件对象
getEvent: function() {
return window.event || arguments[0]; //event ? event : window.event;
}, //获取事件源
getTarget: function(event) {
return event.target || event.srcElement;
}, /**
* 计算div的偏移量
* @param obj
* @returns {{left: (Number|number), top: (Number|number)}}
*/
getOffset: function(obj) {
var x = obj.offsetLeft || 0;
var y = obj.offsetTop || 0;
var temp = obj;
while (temp.offsetParent) {
temp = temp.offsetParent;
x += temp.offsetLeft;
y += temp.offsetTop;
}
//alert("x:"+x+" y:"+y);
return { left: x, top: y };
}, /**
* 将tagetDiv定位到sourceDiv下方,与sourceDic左对齐,宽度一致
* @param sourceDiv
* @param targetDiv
*/
positionDiv: function(sourceDiv, targetDiv) {
var obj = document.getElementById(sourceDiv);
var xy = this.getOffset(obj);
$("#" + targetDiv).css("left", xy.left);
$("#" + targetDiv).css("width", $("#" + sourceDiv).outerWidth());
$("#" + targetDiv).css("top", (xy.top + $("#" + sourceDiv).outerHeight())); }, init: function() {
var control = document.getElementById(this.controlId);
var results = document.getElementById(this.resultDivId);
var jControl = $(control);
var jResults = $(results);
var autoThisObj = this; document.onclick = function(event) {
//$("#"+resultDivId).hide();
var target = autoThisObj.getTarget(autoThisObj.getEvent(event));
//alert(target.id);
if (target.id == autoThisObj.controlId) {
return false;
}
autoThisObj.clearResults();
} //兼容ie(ie浏览器下,当按下up与down键时,输入框会失去焦点,导致up与down键不起作用)
jResults.bind("keydown", function(event) {
jControl.keydown();
return false;
}); //给指定控件绑定keyup事件
$("#" + autoThisObj.controlId).bind("keyup", function(event) {
var e = autoThisObj.getEvent(event);
var keyCode = e.keyCode;
if ((keyCode == '40' || keyCode == '38' || keyCode == '37' || keyCode == '39' || keyCode == '13' || keyCode == '9')) {
return false;
} autoThisObj.index = -1;
results.scrollTop = 0; var keyword = $.trim(jControl.val());
if (keyword.length == 0) {
//jResults.hide();
autoThisObj.clearResults();
return;
} //获取动态数据集,自定义函数的优先级最高
var autoDatas = autoThisObj.getCompleteDatas();//调用自定义数据获取函数
if( (autoDatas instanceof Array) && (autoDatas.length > 0) ){
autoThisObj.dynamicDatas = autoDatas;
}
else if(autoThisObj.serverEnabled=="true"){ //服务器端获取数据
autoThisObj.getAjaxDatas();
} //
if(autoThisObj.dynamicDatas!=null){
autoThisObj.generateHtml(autoThisObj.dynamicDatas);
}
else if(autoThisObj.useStaticDatas=="true" && autoThisObj.datas.length>0){
autoThisObj.generateHtml(autoThisObj.datas);
}
}); //end keyup() this.navigate();
}, // end init() /**
* 定义 up与down 按键功能
* @param event
* @param objectId
* @returns {boolean}
*/
navigate: function(event) { var control = document.getElementById(this.controlId);
var results = document.getElementById(this.resultDivId);
var jControl = $(control);
var jResults = $(results); var autoThisObj = this; this.keyDownBind(jControl); }, // end navigate() /**
* 给指定jquery元素绑定keydown事件,使其可以进行匹配项的选择
*/
keyDownBind: function(jObject) {
var control = document.getElementById(this.controlId);
var results = document.getElementById(this.resultDivId);
var jControl = $(control);
var jResults = $(results);
var autoThisObj = this;
jObject.keydown(function(event) {
var e = autoThisObj.getEvent(event);
var key = e.keyCode;
if (i == "" || !i)
i = -1;
else
i = parseFloat(i); var itemCount = results.childNodes.length; if (key == '40') //Down
{ for (var i = 0, len = itemCount; i < len; i++) {
results.childNodes[i].className = "item"; //重置
} autoThisObj.index++; if (autoThisObj.index > itemCount - 1) {
if (autoThisObj.circleChoose == "true") {
autoThisObj.index = 0;
jResults.scrollTop(0);
}
else {
autoThisObj.index = itemCount - 1;
}
} try {
results.childNodes[autoThisObj.index].className = "item chooseItem";
results.childNodes[autoThisObj.index - 1].className = "item";
}
catch (e) { } //以下两个判断语句用来将当前选中项置于div的可视范围内
if (($(results.childNodes[autoThisObj.index]).height() + 4) * (autoThisObj.index + 1) > $(results).scrollTop() + parseInt(results.style.height)) {
$(results).scrollTop(($(results.childNodes[autoThisObj.index]).height() + 4) * (autoThisObj.index + 1) - parseInt(results.style.height));
}
if (($(results.childNodes[autoThisObj.index]).height() + 4) * (autoThisObj.index) < $(results).scrollTop()) {
$(results).scrollTop(($(results.childNodes[autoThisObj.index]).height() + 4) * (autoThisObj.index));
}
}
else if (key == '38') //UP
{
for (var i = 0, len = itemCount; i < len; i++) {
results.childNodes[i].className = "item"; //重置
} autoThisObj.index--;
if (autoThisObj.index < 0) {
autoThisObj.index = 0;
if (autoThisObj.circleChoose == "true") {
autoThisObj.index = itemCount - 1;
results.scrollTop = results.scrollHeight;
}
else {
autoThisObj.index = 0;
}
} try {
results.childNodes[autoThisObj.index].className = "item chooseItem";
results.childNodes[autoThisObj.index + 1].className = "item";
}
catch (e) { } //以下两个判断语句用来将当前选中项置于div的可视范围内
if (($(results.childNodes[autoThisObj.index]).height() + 4) * (autoThisObj.index + 1) > $(results).scrollTop() + parseInt(results.style.height)) {
$(results).scrollTop(($(results.childNodes[autoThisObj.index]).height() + 4) * (autoThisObj.index + 1) - parseInt(results.style.height));
}
if (($(results.childNodes[autoThisObj.index]).height() + 4) * (autoThisObj.index) < $(results).scrollTop()) {
$(results).scrollTop(($(results.childNodes[autoThisObj.index]).height() + 4) * (autoThisObj.index));
}
}
else if (key == '13' || key == '9') // enter/tab
{
if (autoThisObj.index == -1)
autoThisObj.index = 0; control.value = results.childNodes[autoThisObj.index].innerHTML; autoThisObj.clearResults();
return false;
}
else {
return;
}
}); // end keydown
}, /**
* ajax方式获取匹配结果集
*/
getAjaxDatas:function(){
var autoThisObj = this;
$.ajax({
url: this.serverUrl,
data: this.ajaxRequestData,
type: "POST",
success: function(returnValue) {
if((returnValue instanceof Array)&&(returnValue.length > 0)){
autoThisObj.dynamicDatas = returnValue;
}
else{
autoThisObj.dynamicDatas = null;
}
} //end success()
}); //end ajax()
}, /**
* 获取数据后生成html,并绑定基本事件
*/
generateHtml:function(datas){ //var commonObj = this.getCommonObj();
var autoThisObj = this; var length = datas.length;
var htmlStr = ""; if (length > 0) { for (var i = 0; i < length; i++) {
htmlStr += "<div class='item'>" + datas[i] + "</div>";
} //htmlStr = "<div class='resultCss' id='"+ autoThisObj.resultDivId +"'>" + htmlStr + "</div>"; commonObj.jResults.html(htmlStr).show(); //计算单个item的高度
var itemHeight = $(".item").first().outerHeight(); if (length >= 10) {
commonObj.jResults.height(10 * itemHeight);
}
else {
commonObj.jResults.height(length * itemHeight);
} //调整结果div的宽度并定位
autoThisObj.positionDiv(autoThisObj.controlId, autoThisObj.resultDivId); //默认选中第一项
autoThisObj.index = 0;
commonObj.results.childNodes[autoThisObj.index].className = "item chooseItem"; //给结果集中的每一项添加基本事件
commonObj.jResults.find(".item").each(function() {
//点击事件
$(this).on("click", function() {
commonObj.jControl.val($(this).html());
autoThisObj.clearResults();
}); //mouseover事件
$(this).mouseover(function() {
autoThisObj.index = $(this).index();
var results = document.getElementById(autoThisObj.controlId);
var itemCount = document.getElementById(autoThisObj.resultDivId).childNodes.length;
for (var i = 0, len = itemCount; i < len; i++) {
document.getElementById(autoThisObj.resultDivId).childNodes[i].className = "item"; //重置
}
document.getElementById(autoThisObj.resultDivId).childNodes[autoThisObj.index].className = "item chooseItem"; }); });
}
else {
autoThisObj.clearResults();
}
}, /**
* 清空结果div
*/
clearResults: function() {
var results = document.getElementById(this.resultDivId);
var jResults = $(results);
results.innerHTML = "";
jResults.scrollTop(0);
results.style.display = "none";
jResults.css("height", "auto");
this.dynamicDatas = null;
}, //重置结果集
resetResults: function() {
var results = document.getElementById(this.resultDivId);
var jResults = $(results);
jResults.scrollTop(0);
results.style.display = "none";
this.index = 0; var itemCount = results.childNodes.length;
for (var i = 0, len = itemCount; i < len; i++) {
results.childNodes[i].className = "item"; //重置
} results.childNodes[this.index].className = "item chooseItem";
} } //将AutoComplete暴露到全局范围
window.AutoComplete = AutoComplete;
})();

使用方法示例:

 <html>
<head>
<meta http-equiv="Content-type" content="text/html; charset=utf-8">
<title>autoComplete测试</title>
<style type="text/css">
.item /*每一项的样式*/
{
line-height:20px;
height:20px;
padding: 2px;
width:100%;
overflow: hidden;
}
.chooseItem{ /*选中的当前项样式*/
background-color: #008B8B;
color:White;
cursor: pointer;
height:20px;
padding: 2px;
width:100%;
line-height:20px;
}
.resultsCss{ /*匹配结果集容器样式*/
position:absolute;
overflow-y:auto;
overflow-x:hidden;
display:none;
border:solid 1px gray;
background-color:#F0FFFF;
}
</style>
<script type="text/javascript" src="jquery-1.7.2.min.js"></script>
<script type="text/javascript" src="autoCompleteHome.js"></script> <body>
<div>
<label for="inpt">请输入:</label><input type="text" id="inpt" />
</div>
<div id="tipList" class="resultsCss">
</div>
<script type="text/javascript">
//方式1:静态数据集
var staticDatas = ["asd","axcv","qwerfd","dfghj","cvbnm","bbghty","ertgb","trefgc","cssdavb","abcdefg","trefgc","cssdavb","abcdefg"];
var autoCompleteOption = {
controlId: "inpt",
resultDivId: "tipList",
circleChoose: "true",
serverEndbled: "false",
useStaticDatas:"true",
datas:staticDatas
}; var auto = new AutoComplete(autoCompleteOption);
auto.init(); //方式2:自定义数据获取函数
/*
var autoCompleteOption = {
controlId: "inpt",
resultDivId: "tipList",
circleChoose: "true",
getCompleteDatas:function(){
var datas = null;
//enter your code to get the data
return datas;
}
}; var auto = new AutoComplete(autoCompleteOption);
auto.init();
*/ //方式3:ajax获取数据
/*
var autoCompleteOption = {
controlId: "inpt",
resultDivId: "tipList",
circleChoose: "true",
resultDivId: "tipList",
ajaxRequestData:{},
serverUrl: "AutoCompleteHandler.ashx"
}; var auto = new AutoComplete(autoCompleteOption);
auto.init();
*/
</script>
</body>
</head>
</html>

自己手写的自动完成js类的更多相关文章

  1. 手写代码自动实现自动布局,即Auto Layout的使用

    手写代码自动实现自动布局,即Auto Layout的使用,有需要的朋友可以参考下. 这里要注意几点: 对子视图的约束,若是基于父视图,要通过父视图去添加约束. 对子视图进行自动布局调整,首先对UIVi ...

  2. 手写SpringBoot自动配置及自定义注解搭配Aop,实现升级版@Value()功能

    背景 项目中为了统一管理项目的配置,比如接口地址,操作类别等信息,需要一个统一的配置管理中心,类似nacos. 我根据项目的需求写了一套分布式配置中心,测试无误后,改为单体应用并耦合到项目中.项目中使 ...

  3. 手写网页扫雷之js部分(vue)

    var vm = new Vue({ el:"#ui", data(){ return{ num:0, saoleiStyle:{ width: "0px", ...

  4. 一套手写ajax加一般处理程序的增删查改

    倾述下感受:8天16次驳回.这个惨不忍睹. 好了不说了,说多了都是泪. 直接上代码 : 这个里面的字段我是用动软生成的,感觉自己手写哪些字段太浪费时间了,说多了都是泪 ajax.model层的代码: ...

  5. idea中自动生成实体类

    找到生成实体的路径,找到Database数据表 找到指定的路径即可自动生成entity实体 在创建好的实体类内如此修改 之后的步骤都在脑子里  写给自己看的东西 哪里不会就记录哪里 test类(以前都 ...

  6. 待实践二:MVC3下的3种验证 (1)前台 jquery validate验证 (2)MVC实体验证 (3)EF生成的/自己手写的 自定义实体校验(伙伴类+元素据共享)

    MVC3下的3种验证 (1):前台Jquery Validate脚本验证 引入脚本 <script src="../js/jquery.js" type="text ...

  7. 2019前端面试系列——JS高频手写代码题

    实现 new 方法 /* * 1.创建一个空对象 * 2.链接到原型 * 3.绑定this值 * 4.返回新对象 */ // 第一种实现 function createNew() { let obj ...

  8. 几道JS代码手写面试题

    几道JS代码手写面试题   (1) 高阶段函数实现AOP(面向切面编程)    Function.prototype.before = function (beforefn) {        let ...

  9. 手写一个类SpringBoot的HTTP框架:几十行代码基于Netty搭建一个 HTTP Server

    本文已经收录进 : https://github.com/Snailclimb/netty-practical-tutorial (Netty 从入门到实战:手写 HTTP Server+RPC 框架 ...

随机推荐

  1. Dapper.Contrib:GetAsync<T> only supports an entity with a [Key] or an [ExplicitKey] property

    异常处理:http://www.cnblogs.com/dunitian/p/4523006.html#dapper 原来Model是这样滴 修改后是这样滴 注意点:Model里面的Table和Key ...

  2. ASP.NET MVC5+EF6+EasyUI 后台管理系统(73)-微信公众平台开发-消息管理

    系列目录 前言 回顾上一节,我们熟悉的了解了消息的请求和响应,这一节我们来建立数据库的表,表的设计蛮复杂 你也可以按自己所分析的情形结构来建表 必须非常熟悉表的结果才能运用这张表,这表表的情形涵盖比较 ...

  3. AFNetworking 3.0 源码解读 总结(干货)(下)

    承接上一篇AFNetworking 3.0 源码解读 总结(干货)(上) 21.网络服务类型NSURLRequestNetworkServiceType 示例代码: typedef NS_ENUM(N ...

  4. nodejs操作arduino入门(javascript操作底层硬件)

    用Javascript来操作硬件早就不是一件稀奇的事情了. 所以作为一名电子专业出身的FE,我也打算尝试一下用js来驱动arduino: 要想操作这些底层硬件,肯定是需要一些工具的,我这里介绍的工具主 ...

  5. Redis简单案例(二) 网站最近的访问用户

    我们有时会在网站中看到最后的访问用户.最近的活跃用户等等诸如此类的一些信息.本文就以最后的访问用户为例, 用Redis来实现这个小功能.在这之前,我们可以先简单了解一下在oracle.sqlserve ...

  6. PHP 设计模式概述

    一.设计模式(Design pattern)是什么? 设计模式是一套被反复使用.多数人知晓的.经过分类编目的.代码设计经验的总结.使用设计模式是为了可重用代码.让代码更容易被他人理解.保证代码可靠性. ...

  7. oracle常用函数及示例

    学习oracle也有一段时间了,发现oracle中的函数好多,对于做后台的程序猿来说,大把大把的时间还要学习很多其他的新东西,再把这些函数也都记住是不太现实的,所以总结了一下oracle中的一些常用函 ...

  8. MongoDB学习笔记三—增删改文档上

    插入insert 单条插入 > db.foo.insert({"bar":"baz"}) WriteResult({ }) 批量插入 > db.fo ...

  9. How to accept Track changes in Microsoft Word 2010?

    "Track changes" is wonderful and remarkable tool of Microsoft Word 2010. The feature allow ...

  10. fhq treap最终模板

    新学习了fhq treap,厉害了 先贴个神犇的版, from memphis /* Treap[Merge,Split] by Memphis */ #include<cstdio> # ...