原生js实现autocomplete插件

        在实际的项目中,能用别人写好的插件实现相关功能是最好不过,为了节约时间成本,因为有的项目比较紧急,没充分时间让你自己来写,即便写了,你还要花大量时间调试兼容性。但是出于学习的目的,你可以利用闲暇时间,自己动手写写,看一些原生js的东西,根据自己的思路做插件,这样能提高水平。

          说到autotemplete,好多人都用过,引用autotemplete.js,然后就可以实现在输入框输入值的时候提示下拉选项,类似于百度搜索框那种提示功能,下面就来说说自己的思路。

  1.  为输入框添加input事件

      input事件兼容性代码如下:

    

    AddEvt:function(ele, evt, fn) {
                if (document.addEventListener) {
                    ele.addEventListener(evt, fn, false);
                } else if (document.attachEvent) {
                    ele.attachEvent('on' + (evt == "input" ? "propertychange" : evt), fn);
                } else {
                    ele['on' + (evt == "input" ? "propertychange" : evt)] = fn;
                }
            }

 

       input事件和其他的事件不一样,低版本的ie不支持input事件,只能用propertychange事件,高版本的ie和w3c标准浏览器支持input事件

    2.输入事件触发的时候获取数据

        这里数据有两种形式,一种是直接设置的对象数组,一种是ajax请求返回数据

        这时候我们需要一个ajax请求函数,这里写了一个get请求

       

get: function(url, paraobj, fn, timeout) {
                var xhr = null;
                try {

                  ////兼容firefox,chrom
                    if (window.XMLHttpRequest) {
                        xhr = new XMLHttpRequest();
                    }

                 //////兼容IE

                 else if (Window.ActiveXObject) {

                        xhr = new ActiveXObject("Msxml2.Xmlhttp");
                    }
                } catch (e) {
                    //TODO handle the exception
                    xhr = new ActiveXObject('Microsoft.Xmlhttp');
                }
                xhr.onreadystatechange = function() {
                    if (this.readyState == 4 && this.status == 200) {
                        fn.call(this, this.responseText);

                    } else {
                        setTimeout(function() {

                             xhr.abort();
                        }, timeout);
                    }
                };
                var parastr = '';
                parastr += "?";
                for (var prop in paraobj) {
                    parastr += prop + "=" + paraobj[prop] + "&";
                }
                 xhr.open('get', parastr != "?" ? (url + parastr) : url, true);
                 xhr.send();

            }

       

 

    3. ajax请求成功,且有数据的时候创建下拉框并在下拉框中追加选项       ////创建下拉Div
    

       创建下拉框代码:

     

 createShowDiv: function() {

            ///如果下拉div已存在,删除掉
            var parentNode = this.autoElement.parentNode || this.autoElement.parentElement;
            var childNodes = parentNode.childNodes;
            var showDiv = document.getElementById(this.config.showdivId);
            if (showDiv) {
                parentNode.removeChild(showDiv);
            }
            //创建下拉Div
            var div = document.createElement('div');
            div.id = this.config.showdivId;
            //设置下拉div样式
            var style = this.config.style || {
                width: '200px',
                height: 'auto',
                backgroundColor: '#1c5683',
                cursor: 'pointer',
                display: 'block'
            };
for (var prop in style) { div.style[prop] = style[prop]; } this.showdiv = div; }

 

    

      追加选项代码:

     

appendChild: function(data) {
            var self = this;
            var data = data;
            var fragment = document.createDocumentFragment();
            for (var i = 0; i < data.length; i++) {
                var obj = data[i];
                var child = document.createElement('div');
                child.style.width = self.showdiv.style.width;
                child.style.border = '1px';
                child.style.borderStyle = 'solid';
                child.style.borderTopColor = 'white';
                child.setAttribute('key', obj[self.config.valueFiled]);
                child.innerHTML = obj[self.config.textFiled];
                fragment.appendChild(child);
            }
            self.showdiv.appendChild(fragment);
            self.util.insertAfter(self.showdiv, self.autoElement);

            //为下拉框添加点击事件
            self.util.AddEvt(self.showdiv, 'click', function(e) {
                var evt = e || window.event;
                var target = evt.srcElement || evt.target;
                var key = target.getAttribute("key");
                var val = target.innerHTML;
                self.autoElement.value = val;
                self.closeDiv();
                self.config.select.call(self, key, val);
            });
        }

 

上面说的是主要的几步思路,现在看一下怎么将这些代码封装到一个对象中,让它成为插件。这时候我们用到匿名闭包:

 

(function(win) {
	var autocomplete= function() {
		this.Init.apply(this, arguments);

	}

	autocomplete.prototype = {

		////添加相关操作代码

		Init: {}, ///初始化参数

		Render: {},

		createShowDiv: {}, ///创建下拉div

		appendChild: {}, ///在下拉div里面追加显示项

		closeDiv: {}, ////关闭下拉框

		//////工具对象,事件,请求,还有dom节点操作的函数

		util: {

			AddEvt: {}, ///添加事件

			insertAfter: {}, ///在某元素后面追加元素

				get: {} ////  Ajax get请求

		}

	}

	win.Autocomplete= function(paraobj) {
		new autocomplete(paraobj).Render();
	}
})(window)

 

 

主体的代码添加好了,我们把具体的实现代码展示出来:

     

(function(win) {
    var autocomplete = function () {
		this.Init.apply(this, arguments);
	}
    autocomplete.prototype = {
		Init: function() {
			var args = Array.prototype.slice.call(arguments);
			if (args && args.length > 0) {
				var config = args[0];
				var getType = Object.prototype.toString;
				if (config && getType.call(config) == "[object Object]") {
					//				this.config = config;
					this.config = config || {
						id: '', //控件id
						data: [], //数据
						textFiled: '', //显示的文字的属性名
						valueFiled: '', //获取value的属性名
						style: {}, //显示的下拉div的样式设置
						url: '', //ajax请求的url
						paraName:'name',//ajax请求的参数
						select: function() {}, //选择选项时触发的事件,
						showdivId: '' //下拉选择区域的id
					};
				}
			}
		},
		Render: function() {
			var self = this;
			if (self.config) {
				var autoElement = document.getElementById(self.config.id);
				this.autoElement = autoElement;
				if (autoElement) {
					self.util.AddEvt(this.autoElement, 'input', function() {
						try {
						    if (autoElement.value) {
                               ////ajax请求获取数据的方式
						        if (self.config.url && !self.config.data) {
						            var paraobj = {};
						            paraobj[self.config.paraName] = autoElement.value;
						            self.util.get(self.config.url, paraobj, function (data) {
						                self.createShowDiv();
								        self.appendChild(eval('(' + data + ')'));
									}, 10000);
						        }
                               ////直接设置对象数组的形式
						        else if
                                    (!self.config.url && self.config.data) {
						            self.createShowDiv();
									self.appendChild(self.config.data);
								}

							} else {
						        self.closeDiv();
							}

						} catch (e) {
							//TODO handle the exception
							alert(e);
						}
					});
				}

			}
		},
		////创建下拉Div
		createShowDiv: function() {

			///如果下拉div已存在,删除掉
			var parentNode = this.autoElement.parentNode || this.autoElement.parentElement;
			var childNodes = parentNode.childNodes;
			var showDiv = document.getElementById(this.config.showdivId);
			if (showDiv) {
				parentNode.removeChild(showDiv);
			}
			//创建下拉Div
			var div = document.createElement('div');
			div.id = this.config.showdivId;
			//设置下拉div样式
			var style = this.config.style || {
				width: '200px',
				height: 'auto',
				backgroundColor: '#1c5683',
				cursor: 'pointer',
				display: 'block'
			};
			for (var prop in style) {
				div.style[prop] = style[prop];
			}
			this.showdiv = div;
		},
		///在下拉div里面追加显示项
		appendChild: function(data) {
			var self = this;
			var data = data;
			var fragment = document.createDocumentFragment();
			for (var i = 0; i < data.length; i++) {
				var obj = data[i];
				var child = document.createElement('div');
				child.style.width = self.showdiv.style.width;
				child.style.border = '1px';
				child.style.borderStyle = 'solid';
				child.style.borderTopColor = 'white';
				child.setAttribute('key', obj[self.config.valueFiled]);
				child.innerHTML = obj[self.config.textFiled];
				fragment.appendChild(child);
			}
			self.showdiv.appendChild(fragment);
			self.util.insertAfter(self.showdiv, self.autoElement);

            //为下拉框添加点击事件
			self.util.AddEvt(self.showdiv, 'click', function(e) {
				var evt = e || window.event;
				var target = evt.srcElement || evt.target;
				var key = target.getAttribute("key");
				var val = target.innerHTML;
				self.autoElement.value = val;
				self.closeDiv();
				self.config.select.call(self, key, val);
			});
		},
        ////关闭下拉框
		closeDiv: function () {
		    if (this.showdiv) {
		        this.showdiv.style.display = 'none';
		    }

		}
        ,
		util: {
			///添加事件
			AddEvt: function(ele, evt, fn) {
				if (document.addEventListener) {
					ele.addEventListener(evt, fn, false);
				} else if (document.attachEvent) {
					ele.attachEvent('on' + (evt == "input" ? "propertychange" : evt), fn);
				} else {
					ele['on' + (evt == "input" ? "propertychange" : evt)] = fn;
				}
			},
			///在某元素后面追加元素
			insertAfter: function(ele, targetELe) {
				var parentnode = targetELe.parentNode || targetELe.parentElement;
				if (parentnode.lastChild == targetELe) {
					parentnode.appendChild(ele);
				} else {
					parentnode.insertBefore(ele, targetELe.nextSibling);
				}
			},
			///Get请求
			get: function(url, paraobj, fn, timeout) {
				var xhr = null;
				try {
					if (window.XMLHttpRequest) {
						xhr = new XMLHttpRequest();
					} else if (Window.ActiveXObject) {

						xhr = new ActiveXObject("Msxml2.Xmlhttp");
					}
				} catch (e) {
					//TODO handle the exception
					xhr = new ActiveXObject('Microsoft.Xmlhttp');
				}
				xhr.onreadystatechange = function() {
					if (this.readyState == 4 && this.status == 200) {
						fn.call(this, this.responseText);

					} else {
						setTimeout(function() {

							 xhr.abort();
						}, timeout);
					}
				};
				var parastr = '';
				parastr += "?";
				for (var prop in paraobj) {
					parastr += prop + "=" + paraobj[prop] + "&";
				}
				 xhr.open('get', parastr != "?" ? (url + parastr) : url, true);
				 xhr.send();

			}
		}
	}

    win.AutoComplete = function (paraobj) {
        new autocomplete(paraobj).Render();

	}

})(window)

 

 

下面是使用的代码

           页面调用

            

    window.onload = function () {
        AutoComplete({
            id: 'txtTest', //控件id
            url: '/Home/Test4', //数据
            paraName:'name',
            textFiled: 'name', //显示的文字的属性名
            valueFiled: 'id', //获取value的属性名
            //					style: {}, //显示的下拉div的样式设置
            //						url: '', //ajax请求的url
            select: function (val, text) {
                alert(val + '---' + text);
            }, //选择选项时触发的事件,
            showdivId: 'showDIv' //下拉选择区域的id});
        });

    }
 

 

          后台代码如下,这里我用的是mvc

         

        public JsonResult Test4(string   name)
        {
            var list=new  List<Student>();
            list.Add(new Student {  id="1",name="aaaaa"});
            list.Add(new Student { id = "2", name = "aacc" });

            list.Add(new Student { id = "3", name = "aabb" });
            list.Add(new Student { id = "4", name = "bbcc" });

            if (!string.IsNullOrEmpty(name))
            {
                list = list.Where(p => p.name.Contains(name)).ToList();
            }
            return Json(list,JsonRequestBehavior.AllowGet);
        }

 

  现在基本的功能实现和调用讲完了,从开始到最后的过程是比较麻烦的,每个方法都是一步步实现,没有引用其他的库,要考虑到各个浏览器的兼容性。如果你有什么疑问,请留言,我会回复。

      

    

posted @ 2015-08-24 14:56  kevinzw  阅读(5659)  评论(4编辑  收藏  举报