index.html 头部区结构和样式
效果图
静态样式
index.html中的部分
<!-- 头部 --> <div class="header"> <div class="container"> <!-- h1标签是为了搜索引擎优化,表示重要 但是页面内不要出现太多 --> <h1 class="fl"><a href="#" class="header-logo text-hidden">慕淘网</a></h1> <div class="search fl"> <!-- 由于没有自己的搜索页,演示时设置为提交到淘宝,参考淘宝设置 --> <form action="https://s.taobao.com/search"> <!-- 由于input是内联块,相当于display:inline-block 如果换行写,会造成空隙,空隙大小一般是默认字体的一半 可以不换行书写,但是可读性较差 都添加左浮动可以解决 --> <!-- 设置name才能提交 --> <input type="text" class="search-input fl" name="q" placeholder="灵魂美食一元抢" autocomplete="off"> <input type="submit" value="搜索" class="search-btn fl"> </form> <ul class="search-list"> <li class="search-item text-ellipsis" title="111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111">111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111</li> <li class="search-item text-ellipsis" title="222">222</li> <li class="search-item text-ellipsis" title="333">333</li> </ul> </div> <div class="header-cart fr"></div> </div> </div>
common.css中新增搜索框组件公共样式
/*搜索框组件 search*/ .search{ position: relative; width:679px; border:1px solid #cfd2d5; } .search-input{ width:586px; height:40px; line-height:40px; background-color:#fff; border:none; padding:0 10px; } .search-btn{ width:73px; height:40px; background-color:#07111b; color:#fff; line-height:40px; text-align: center; cursor:pointer; border:none; } .search-list{ display: none; background-color:#fff; position: absolute; width:586px; top:100%;/*父容器的高度*/ left:-1px; border:1px solid #cfd2d5; padding:0 10px; } .search-item{ height:24px; line-height:24px; }
index.css中新增header中独有的样式
/*header*/ .header{ height:124px; background-color: #f3f5f7; } .header-logo{ display: block; background:url(../img/header-logo.png) no-repeat; width:136px; height:48px; margin-top:36px; margin-left:15px; } .header .search{ margin-top:36px; margin-left:144px; }
引入search.js文件
这里补充下几个文本框事件的触发条件的区别:
change 文本框内容改变 + blur
keypress 按键触发,如果鼠标不抬起连续按键,则连续触发
keyup 按键释放触发,不管按什么键(包括上下箭头等无文字意义符号),而且鼠标粘贴过来的文本无法触发
input 文本输入,跟change的区别是不需要 blur 即可触发;鼠标粘贴也可触发(兼容性不好:IE8以下不支持)
综上所述,最理想的选择是 input,但有时为了兼容性,只能选择 keyup,并可以自己做一些约束改造
查看淘宝搜索的form表单提交action
//s.taobao.com/search
自己在表单使用时参考淘宝需要在前面加上https:协议
即:https://s.taobao.com/search
查看淘宝搜索输入框的name属性
name="q"
查看淘宝提交时ajax请求的url地址
1、打开网址,打开控制台,找到network,点击下面的JS
2、可以先用绿色框的那个按钮将下面的内容清空一下,然后在输入框中写内容,下面Name的地方就会出来信息
3、点击任意一个进去,就会出现右侧的Headers,里面的Request URL粘贴复制在浏览器地址栏中就可以看到了。
添加搜索验证和获取数据功能:
search.js
(function($){ "use strict"; //验证 var search=$(".search"), searchInput=search.find(".search-input"), searchBtn=search.find(".search-btn"), searchList=search.find(".search-list"); searchBtn.on("click",function(){ //submit按钮默认行为是提交表单,return false可以阻止默认行为 //$.trim() 去掉字符串两边的空格,阻止无内容提交 if($.trim(searchInput.val())==="") return false; }); //自动完成 searchInput.on("input",function(){ var url = ‘https://suggest.taobao.com/sug?code=utf-8&_ksTS=1484204931352_18291&callback=jsonp18292&k=1&area=c2c&bucketid=6&q=‘ + encodeURIComponent($.trim(searchInput.val())); $.ajax({ url:url, dataType:"jsonp",//jsonp用于跨域 success:function(data){ console.log(data); }, error:function(data){ console.log(error); } }); }); })(jQuery)
由于jQuery.ajax返回的是jqXHR对象,它是浏览器原生XMLHttpRequest对象的一个超集,为了让回调函数名字统一,便于$.ajax中使用,jqXHR提供了.error(),.success(),.complete()
由于版本升级,才有了相应的.fail(),.done(),.always()三个方法
使用done(), fail(), always()是 为了避免代码嵌套在ajax里面,方便阅读
因此$.ajax写法可以做如下修改,异步避免回调:
// 异步避免回调 $.ajax({ url:url, timeout:1,//失败常见原因是超时,这里为了演示,将超时设置为1毫秒 dataType:"jsonp" }).done(function(data){//成功执行 console.log(data); }).fail(function(){//失败执行 console.log("fail"); }).always(function(){//总是执行 console.log("always"); });
失败有很多种原因,其中超时是一个很常见的原因
为了演示超时,设置timeout:1 (1毫秒)
$.trim(searchInput.val()) 这边默认是使用的utf-8编码
如果在页面为其他编码格式,如:gbk 时,可能会因为编码问题造成读取数据失败
因此使用 encodeURIComponent( ) 来解决编码问题
1、encodeURIComponent(URIstring ) 函数可把字符串作为 URI 组件进行编码,返回值是URIstring 的副本,其中的某些字符将被十六进制的转义序列进行替换。简单来说作用就是进行编码,能够被后台识别,后台开发语言都有相应的解码 api,这样就可以成功的返回数据。
2、网页的编码会影响到发送请求时数据的编码,所以不一致时需要编码。
var url = ‘https://suggest.taobao.com/sug?code=utf-8&_ksTS=1484204931352_18291&callback=jsonp18292&k=1&area=c2c&bucketid=6&q=‘ + encodeURIComponent($.trim(searchInput.val()));
生成下拉层数据结构
(function($){ "use strict"; //验证 var search=$(".search"), searchInput=search.find(".search-input"), searchBtn=search.find(".search-btn"), searchList=search.find(".search-list"); searchBtn.on("click",function(){ //submit按钮默认行为是提交表单,return false可以阻止默认行为 //$.trim() 去掉字符串两边的空格,阻止无内容提交 if($.trim(searchInput.val())==="") return false; }); //自动完成 searchInput.on("input",function(){ var url = ‘https://suggest.taobao.com/sug?code=utf-8&_ksTS=1484204931352_18291&callback=jsonp18292&k=1&area=c2c&bucketid=6&q=‘ + encodeURIComponent($.trim(searchInput.val())); // 异步避免回调 $.ajax({ url:url, dataType:"jsonp" }).done(function(data){//成功执行 //console.log(data["result"]); var html=""; var dataNum=data["result"].length;//实际数据量 var maxNum=10;//最大显示数据量 if(dataNum===0) searchList.hide().html(""); for(var i=0;i<dataNum;i++){ if(i>=maxNum) break; //console.log(data["result"][i][0]); html+=‘<li class="search-item text-ellipsis" title="‘+data["result"][i][0]+‘">‘+data["result"][i][0]+‘</li>‘; } searchList.html(html).show(); }).fail(function(){//失败执行 searchList.hide().html(""); }).always(function(){//总是执行 console.log("always"); }); }); })(jQuery)
顺便将之前index.html中这部分注释掉
<ul class="search-list"> <!-- <li class="search-item text-ellipsis" title="111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111">111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111</li> <li class="search-item text-ellipsis" title="222">222</li> <li class="search-item text-ellipsis" title="333">333</li> --> </ul>
效果
事件代理和显示隐藏下拉层
(function($){ "use strict"; //验证 var search=$(".search"), searchForm=search.find(".search-form"), searchInput=search.find(".search-input"), searchBtn=search.find(".search-btn"), searchList=search.find(".search-list"); searchForm.on("submit",function(){ //return false可以阻止默认行为,即阻止表单提交 //$.trim() 去掉字符串两边的空格,阻止无内容提交 if($.trim(searchInput.val())==="") return false; }); //自动完成 searchInput.on("input",function(){ var url = ‘https://suggest.taobao.com/sug?code=utf-8&_ksTS=1484204931352_18291&callback=jsonp18292&k=1&area=c2c&bucketid=6&q=‘ + encodeURIComponent($.trim(searchInput.val())); // 异步避免回调 $.ajax({ url:url, dataType:"jsonp" }).done(function(data){//成功执行 //console.log(data["result"]); var html=""; var dataNum=data["result"].length;//实际数据量 var maxNum=10;//最大显示数据量 if(dataNum===0) searchList.hide().html(""); for(var i=0;i<dataNum;i++){ if(i>=maxNum) break; //console.log(data["result"][i][0]); html+=‘<li class="search-item text-ellipsis" title="‘+data["result"][i][0]+‘">‘+data["result"][i][0]+‘</li>‘; } searchList.html(html).show(); }).fail(function(){//失败执行 searchList.hide().html(""); }).always(function(){//总是执行 console.log("always"); }); }); //jquery的事件代理 //事件绑定在父元素上,第一个参数是事件,第二个参数是被代理的子元素们,第三个参数是函数 //函数里的$(this)指向的是被代理的子元素 searchList.on("click",".search-item",function(){ searchInput.val(removeHTML($(this).html())); searchForm.submit(); }); //显示隐藏下拉层 // searchInput.on("focus",function(){ // searchList.show(); // }).on("blur",function(){ // searchList.hide(); // }); //以上这种方法不可行,会导致点击列表项失效 //因为blur事件与click事件冲突 //因为在选项上按下鼠标时,已经触发了input的blur事件,导致列表隐藏;再出发列表项的点击时,列表已经被隐藏 searchInput.on("focus",function(){ searchList.show(); }).on("click",function(){ return false;//阻止点击时冒泡到document }); $(document).on("click",function(){ searchList.hide(); }); //去掉下拉列表项目的html标签,不然点击后会显示在搜索框中 function removeHTML(str){ // 示例:<input type="text" value=">" name=‘username‘ /> // 1、标签中的属性使用的引号 可能是双引号,也可能是单引号,所以匹配引号外面的内容使用^取反,双引号,单引号和>不能获取,其他都是可以获取的。 // 2、匹配引号里面的内容,不能匹配到引号,其他都是可以的,数量上可以为0,也就是引号中间没有内容。单引号和双引号都有可能,所以写了两次。 // 3、然后把这匹配的三个作为一组,需要全部都进行匹配,数量上可以为0,不需要捕获分组的内容,所以使用了?: // 4、最外层是<> return str.replace(/<(?:[^‘">]|"[^"]*"|‘[^‘]*‘)*>/g,""); } })(jQuery)
知识点:
点击 input 显示下拉层,点击其他地方隐藏下拉层
这个功能,不能用下面这段代码
searchInput.on("focus",function(){ searchList.show(); }).on("blur",function(){ searchList.hide(); });
因为这里的blur事件与click事件存在冲突
在选项上按下鼠标时,已经触发了input的blur事件,导致列表隐藏;再出发列表项的点击时,列表已经被隐藏
需要使用下面这段代码:(注意要阻止冒泡)
//显示隐藏下拉层 searchInput.on("focus",function(){ searchList.show(); }).on("click",function(){ return false;//阻止点击时冒泡到document }); $(document).on("click",function(){ searchList.hide(); });
去掉字符串中 html 标签的正则:
//去掉下拉列表项目的html标签,不然点击后会显示在搜索框中 function removeHTML(str){ return str.replace(/<(?:[^‘">]|"[^"]*"|‘[^‘]*‘)*>/g,""); }
示例:<input type="text" value=">" name=‘username‘ />
1、标签中的属性使用的引号 可能是双引号,也可能是单引号,所以匹配引号外面的内容使用^取反,双引号,单引号和>不能获取,其他都是可以获取的。
2、匹配引号里面的内容,不能匹配到引号,其他都是可以的,数量上可以为0,也就是引号中间没有内容。单引号和双引号都有可能,所以写了两次。
3、然后把这匹配的三个作为一组,需要全部都进行匹配,数量上可以为0,不需要捕获分组的内容,所以使用了?:
4、最外层是<>
面向对象方式封装搜索框功能
search.js
(function($){ "use strict"; function Search(elem,options){ this.elem=elem;//已经是传入的jquery对象 this.options=options; this.form=this.elem.find(".search-form"); this.input=this.elem.find(".search-input"); this.list=this.elem.find(".search-list"); //绑定提交事件,事件代理 this.elem.on("click",".search-btn",$.proxy(this.submit,this)); //如果设置了自动完成 if(this.options.autocomplete) this.autocomplete(); } //默认参数 Search.defaults={ autocomplete:false, url:"https://suggest.taobao.com/sug?code=utf-8&_ksTS=1484204931352_18291&callback=jsonp18292&k=1&area=c2c&bucketid=6&q=", css3:false, js:false, animation:"fade" } Search.prototype.submit=function(){ if($.trim(this.input.val())==="") return false; this.form.submit(); } Search.prototype.autocomplete=function(){ this.input.on("input",$.proxy(this.getData,this)); this.list.showHide(this.options);//向showhide组件传参,初始化 //显示隐藏下拉层 this.input.on("focus",$.proxy(this.showList,this)) .on("click",function(){ return false;//阻止点击时冒泡到document }); $(document).on("click",$.proxy(this.hideList,this)); } Search.prototype.getData=function(){ var self=this; $.ajax({ url:this.options.url+encodeURIComponent($.trim(this.input.val())), dataType:"jsonp" }).done(function(data){ //发送data数据,触发事件 self.elem.trigger("search-getData",[data,self.list]);//数据需要用数组形式 }).fail(function(){ //发送失败数据,触发事件 self.elem.trigger("search-noData",[self.list]); }); } Search.prototype.showList=function(){ //list里有内容才显示 if(this.list.children().length===0) return; this.list.showHide("show");//使用showhide组件的show方法 } Search.prototype.hideList=function(){ this.list.showHide("hide");//使用showhide组件的hide方法 } Search.prototype.setInput=function(val){ this.input.val(val); } //插件形式 $.fn.extend({ search:function(opt,value){ return this.each(function(){ var ui=$(this); var search=ui.data("search"); //opt是参数对象 var options=$.extend({},Search.defaults,ui.data(),typeof opt==="object"&&opt); //单例:一个DOM元素对应一个实例,如果已经存在则不需要反复实例化 if(!search){ search=new Search(ui,options); ui.data("search",search); } //暴露出一些方法供外部调用 if(typeof search[opt]==="function"){ search[opt](value); } }); } }); })(jQuery)
index.js
// 不要暴露在全局,使用匿名函数自执行 (function($){ "use strict"; //menu //绑定事件 显示之前加载数据 $(".dropdown").on("dropdown-show",function(e){ var ui=$(this); var dataLoad=ui.data("load"); if(!dataLoad) return; if(!ui.data("loaded")){ var list=ui.find(".dropdown-list"); var html=""; setTimeout(function(){ $.getJSON(dataLoad,function(data){ for(var i=0;i<data.length;i++){ console.log(data[i]); html+=‘<li class="menu-item"><a href="‘+data[i]["url"]+‘">‘+data[i]["name"]+‘</a></li>‘; } list.html(html); ui.data("loaded",true); }); },500); } }); //插件形式调用 $(".dropdown").dropdown({ css3:true, js:true }); //search var headerSearch=$("#header-search"); var html=""; var maxNum=10;//最大显示数据量 headerSearch.search({ autocomplete:true, css3:false, js:false, animation:"fade" }); //接收事件 headerSearch.on("search-getData",function(e,data,list){ //console.log(e.type); //console.log(data); var ui=$(this); //获取数据之后的处理 html=createHeaderList(data,maxNum); list.html(html); if(html){ ui.search("showList"); }else{ ui.search("hideList"); } }); headerSearch.on("search-noData",function(e,list){ ui.search("hideList");//隐藏下拉列表 list.html("");//清空内容 }); headerSearch.on("click",".search-item",function(){ headerSearch.search("setInput",$(this).text()); headerSearch.search("submit"); }); //创建header中搜索框的下拉列表结构 function createHeaderList(data,maxNum){ var html=""; var dataNum=data["result"].length;//实际数据量 if(dataNum===0) return ""; for(var i=0;i<dataNum;i++){ if(i>=maxNum) break; html+=‘<li class="search-item text-ellipsis" title="‘+data["result"][i][0]+‘">‘+data["result"][i][0]+‘</li>‘; } return html; } })(jQuery);
接下来进行代码的优化:
1、上面这句代码是对DOM的操作,比较耗费性能。优化:引入loaded变量来判断
2、发送ajax请求时,需要新增判断数据为空的情况
3、如果一次ajax请求还没完成,就发送了下一次ajax请求,那么会导致返回的数据无法判断是哪一次的。优化:进行下一次请求时先终止之前的请求
4、搜索框每输入一个字符,就会发送一次ajax请求,即使两次间隔很短;建议:根据用户需求,判断是否需要加入延迟
优化后的search.js
(function($){ "use strict"; function Search(elem,options){ this.elem=elem;//已经是传入的jquery对象 this.options=options; this.form=this.elem.find(".search-form"); this.input=this.elem.find(".search-input"); this.list=this.elem.find(".search-list"); this.loaded=false;//是否装载html //绑定提交事件,事件代理 this.elem.on("click",".search-btn",$.proxy(this.submit,this)); //如果设置了自动完成 if(this.options.autocomplete) this.autocomplete(); } //默认参数 Search.defaults={ autocomplete:false, url:"https://suggest.taobao.com/sug?code=utf-8&_ksTS=1484204931352_18291&callback=jsonp18292&k=1&area=c2c&bucketid=6&q=", css3:false, js:false, animation:"fade", delay:200//默认200毫秒延迟 } Search.prototype.submit=function(){ if($.trim(this.input.val())==="") return false; this.form.submit(); } Search.prototype.autocomplete=function(){ var self=this; var timer=null; this.input.on("input",function(){ if(self.options.delay){ clearTimeout(timer); timer=setTimeout(function(){ self.getData(); },self.options.delay); }else{ self.getData(); //delay为0时,不需要开定时器 //因为定时器属于异步,即使延迟为0,也会进入排队状态,无法立刻执行 } }); this.list.showHide(this.options);//向showhide组件传参,初始化 //显示隐藏下拉层 this.input.on("focus",$.proxy(this.showList,this)) .on("click",function(){ return false;//阻止点击时冒泡到document }); $(document).on("click",$.proxy(this.hideList,this)); } Search.prototype.getData=function(){ var self=this; var inputVal=this.input.val(); if(!inputVal) return self.elem.trigger("search-noData"); if(this.jqXHR) this.jqXHR.abort();//进行ajax请求时,先终止之前的请求 this.jqXHR=$.ajax({ url:this.options.url+encodeURIComponent($.trim(inputVal)), dataType:"jsonp" }).done(function(data){ //发送data数据,触发事件 self.elem.trigger("search-getData",[data]);//数据需要用数组形式 }).fail(function(){ //发送失败数据,触发事件 self.elem.trigger("search-noData"); }).always(function(){ //执行完毕后 self.jqXHR=null; }); } Search.prototype.showList=function(){ //list里有内容才显示 if(!this.loaded) return; this.list.showHide("show");//使用showhide组件的show方法 } Search.prototype.hideList=function(){ this.list.showHide("hide");//使用showhide组件的hide方法 } Search.prototype.setInput=function(val){ this.input.val(val); } Search.prototype.appendHTML=function(html){ this.list.html(html); this.loaded=!!html;//!!转布尔值,如果html有内容,转为真;否则为假 } //插件形式 $.fn.extend({ search:function(opt,value){ return this.each(function(){ var ui=$(this); var search=ui.data("search"); //opt是参数对象 var options=$.extend({},Search.defaults,ui.data(),typeof opt==="object"&&opt); //单例:一个DOM元素对应一个实例,如果已经存在则不需要反复实例化 if(!search){ search=new Search(ui,options); ui.data("search",search); } //暴露出一些方法供外部调用 if(typeof search[opt]==="function"){ search[opt](value); } }); } }); })(jQuery)
index.js
// 不要暴露在全局,使用匿名函数自执行 (function($){ "use strict"; //menu //绑定事件 显示之前加载数据 $(".dropdown").on("dropdown-show",function(e){ var ui=$(this); var dataLoad=ui.data("load"); if(!dataLoad) return; if(!ui.data("loaded")){ var list=ui.find(".dropdown-list"); var html=""; setTimeout(function(){ $.getJSON(dataLoad,function(data){ for(var i=0;i<data.length;i++){ console.log(data[i]); html+=‘<li class="menu-item"><a href="‘+data[i]["url"]+‘">‘+data[i]["name"]+‘</a></li>‘; } list.html(html); ui.data("loaded",true); }); },500); } }); //插件形式调用 $(".dropdown").dropdown({ css3:true, js:true }); //search var headerSearch=$("#header-search"); var html=""; var maxNum=10;//最大显示数据量 headerSearch.search({ autocomplete:true, css3:false, js:false, animation:"fade", delay:0 }); //接收事件 headerSearch.on("search-getData",function(e,data){ //console.log(e.type); console.log(data); var ui=$(this); //获取数据之后的处理 html=createHeaderList(data,maxNum); ui.search("appendHTML",html); if(html){ ui.search("showList"); }else{ ui.search("hideList"); } }).on("search-noData",function(e){ $(this).search("hideList");//隐藏下拉列表 $(this).search("appendHTML","");//清空内容 }).on("click",".search-item",function(){ $(this).search("setInput",$(this).text()); $(this).search("submit"); }); //创建header中搜索框的下拉列表结构 function createHeaderList(data,maxNum){ var html=""; var dataNum=data["result"].length;//实际数据量 if(dataNum===0) return ""; for(var i=0;i<dataNum;i++){ if(i>=maxNum) break; html+=‘<li class="search-item text-ellipsis" title="‘+data["result"][i][0]+‘">‘+data["result"][i][0]+‘</li>‘; } return html; } })(jQuery);
index.html
<!DOCTYPE html> <html lang="zh-CN"><!-- 设置简体中文 --> <head> <meta charset="UTF-8"> <title>index</title> <link rel="stylesheet" href="css/base.css"> <link rel="stylesheet" href="css/index.css"> <link rel="stylesheet" href="css/common.css"> <!-- css一般放在DOM加载前,防止DOM裸奔 --> </head> <body> <!-- 导航 --> <div class="nav-site"> <div class="container"> <ul class="fl"> <li class="fl"><a href="javascript:;" class="nav-site-login">亲,请登录</a></li> <li class="fl"><a href="javascript:;" class="nav-site-reg link">免费注册</a></li> <li class="fl"><a href="#" class="nav-site-shop link">手机逛慕淘</a></li> </ul> <ul class="fr"> <li class="fl dropdown menu" data-active="menu"> <a href="javascript:;" class="dropdown-toggle link transition">我的慕淘<i class="dropdown-arrow iconfont transition"></i></a> <ul class="dropdown-list dropdown-left"> <li class="menu-item"><a href="#">已买到的宝贝</a></li> <li class="menu-item"><a href="#">我的足迹</a></li> </ul> </li> <li class="fl dropdown menu" data-active="menu"> <a href="javascript:;" class="dropdown-toggle link transition">收藏夹<i class="dropdown-arrow iconfont transition"></i></a> <ul class="dropdown-list dropdown-left"> <li class="menu-item"><a href="#">收藏的宝贝</a></li> <li class="menu-item"><a href="#">收藏的店铺</a></li> </ul> </li> <li class="fl dropdown"> <a href="javascript:;" class="nav-site-cat link">商品分类</i></a> </li> <li class="fl dropdown menu" data-active="menu" data-load="js/dropdown-seller.json"> <a href="javascript:;" class="dropdown-toggle link transition">卖家中心<i class="dropdown-arrow iconfont transition"></i></a> <ul class="dropdown-list dropdown-left"> <li class="dropdown-loading"></li> <!-- <li class="menu-item"><a href="#">免费开店</a></li> <li class="menu-item"><a href="#">已卖出的宝贝</a></li> <li class="menu-item"><a href="#">出售中的宝贝</a></li> <li class="menu-item"><a href="#">卖家服务市场</a></li> <li class="menu-item"><a href="#">卖家培训中心</a></li> <li class="menu-item"><a href="#">体验中心</a></li> --> </ul> </li> <li class="nav-site-service fl dropdown menu" data-active="menu"> <a href="javascript:;" class="dropdown-toggle link transition">联系客服<i class="dropdown-arrow iconfont transition"></i></a> <ul class="dropdown-list dropdown-right"> <li class="menu-item"><a href="#">已买到的宝贝</a></li> <li class="menu-item"><a href="#">我的足迹</a></li> </ul> </li> </ul> </div> </div> <!-- 头部 --> <div class="header"> <div class="container"> <!-- h1标签是为了搜索引擎优化,表示重要 但是页面内不要出现太多 --> <h1 class="fl"><a href="#" class="header-logo text-hidden">慕淘网</a></h1> <div id="header-search" class="search fl"> <!-- 由于没有自己的搜索页,演示时设置为提交到淘宝,参考淘宝设置 --> <form action="https://s.taobao.com/search" class="search-form"> <!-- 由于input是内联块,相当于display:inline-block 如果换行写,会造成空隙,空隙大小一般是默认字体的一半 可以不换行书写,但是可读性较差 都添加左浮动可以解决 --> <!-- 设置name才能提交 --> <input type="text" class="search-input fl" name="q" placeholder="灵魂美食一元抢" autocomplete="off"> <input type="submit" value="搜索" class="search-btn fl"> </form> <ul class="search-list"> <!-- <li class="search-item text-ellipsis" title="111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111">111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111</li> <li class="search-item text-ellipsis" title="222">222</li> <li class="search-item text-ellipsis" title="333">333</li> --> </ul> </div> <div class="header-cart fr"></div> </div> </div> <!-- <script src="https://cdn.bootcss.com/jquery/1.12.4/jquery.min.js"></script> <script> //短路操作,如果cdn的jquery没有引用成功,则会执行后面一句,引入本地jquery //括号中的<\/script>标签会被当做当前标签的结束标签,因此需要转义 window.jQuery || document.write(‘<script src="js/jquery.js"><\/script>‘); </script> --> <script src="js/jquery.js"></script> <script src="js/transition.js"></script> <script src="js/showhide.js"></script> <script src="js/dropdown.js"></script> <script src="js/search.js"></script> <script src="js/index.js"></script> </body> </html>
最后,优化之数据缓存
思路:获取到的数据存入变量、cookie、或是本地存储:session storage / local storage (HTML5) 或是本地数据库
search.js
(function($){ "use strict"; var cache={ data:{}, count:0,//数据条数 addData:function(data,key){ if(!this.data[key]){ this.data[key]=data; this.count++; } }, readData:function(key){ return this.data[key]; }, deleteDataByKey:function(key){ delete this.data[key]; this.count--; }, deleteDataByNum:function(num){ var count=0; //对象没有length属性,只能通过for in来遍历 for(var p in this.data){ if(count>=num) break; this.deleteDataByKey(p); this.count++; } } }; function Search(elem,options){ this.elem=elem;//已经是传入的jquery对象 this.options=options; this.form=this.elem.find(".search-form"); this.input=this.elem.find(".search-input"); this.list=this.elem.find(".search-list"); this.loaded=false;//是否装载html //绑定提交事件,事件代理 this.elem.on("click",".search-btn",$.proxy(this.submit,this)); //如果设置了自动完成 if(this.options.autocomplete) this.autocomplete(); } //默认参数 Search.defaults={ autocomplete:false, url:"https://suggest.taobao.com/sug?code=utf-8&_ksTS=1484204931352_18291&callback=jsonp18292&k=1&area=c2c&bucketid=6&q=", css3:false, js:false, animation:"fade", delay:200//默认200毫秒延迟 } Search.prototype.submit=function(){ if($.trim(this.input.val())==="") return false; this.form.submit(); } Search.prototype.autocomplete=function(){ var self=this; var timer=null; this.input.on("input",function(){ if(self.options.delay){ clearTimeout(timer); timer=setTimeout(function(){ self.getData(); },self.options.delay); }else{ self.getData(); //delay为0时,不需要开定时器 //因为定时器属于异步,即使延迟为0,也会进入排队状态,无法立刻执行 } }); this.list.showHide(this.options);//向showhide组件传参,初始化 //显示隐藏下拉层 this.input.on("focus",$.proxy(this.showList,this)) .on("click",function(){ return false;//阻止点击时冒泡到document }); $(document).on("click",$.proxy(this.hideList,this)); } Search.prototype.getData=function(){ var self=this; var inputVal=this.input.val(); if(!inputVal) return self.elem.trigger("search-noData"); //判断是否已有缓存 if(cache.readData(inputVal)) return self.elem.trigger("search-getData",[cache.readData(inputVal)]); if(this.jqXHR) this.jqXHR.abort();//进行ajax请求时,先终止之前的请求 this.jqXHR=$.ajax({ url:this.options.url+encodeURIComponent($.trim(inputVal)), dataType:"jsonp" }).done(function(data){ //发送data数据,触发事件 cache.addData(data,inputVal);//添加缓存 console.log(cache.data); console.log(cache.count); self.elem.trigger("search-getData",[data]);//数据需要用数组形式 }).fail(function(){ //发送失败数据,触发事件 self.elem.trigger("search-noData"); }).always(function(){ //执行完毕后 self.jqXHR=null; }); } Search.prototype.showList=function(){ //list里有内容才显示 if(!this.loaded) return; this.list.showHide("show");//使用showhide组件的show方法 } Search.prototype.hideList=function(){ this.list.showHide("hide");//使用showhide组件的hide方法 } Search.prototype.setInput=function(val){ this.input.val(val); } Search.prototype.appendHTML=function(html){ this.list.html(html); this.loaded=!!html;//!!转布尔值,如果html有内容,转为真;否则为假 } //插件形式 $.fn.extend({ search:function(opt,value){ return this.each(function(){ var ui=$(this); var search=ui.data("search"); //opt是参数对象 var options=$.extend({},Search.defaults,ui.data(),typeof opt==="object"&&opt); //单例:一个DOM元素对应一个实例,如果已经存在则不需要反复实例化 if(!search){ search=new Search(ui,options); ui.data("search",search); } //暴露出一些方法供外部调用 if(typeof search[opt]==="function"){ search[opt](value); } }); } }); })(jQuery)
原文地址:https://www.cnblogs.com/chenyingying0/p/12347806.html