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)

头部布局,搜索验证和AJAX自动搜索提示,并封装成组件,提高代码复用性的更多相关文章

  1. jQuery框架学习第十一天:实战jQuery表单验证及jQuery自动完成提示插件

    jQuery框架学习第一天:开始认识jQueryjQuery框架学习第二天:jQuery中万能的选择器jQuery框架学习第三天:如何管理jQuery包装集 jQuery框架学习第四天:使用jQuer ...

  2. POI 导入excel数据自动封装成model对象--代码分析

    上完代码后,对代码进行主要的分析: 1.主要使用反射api将数数据注入javabean对象 2.代码中的日志信息级别为debug级别 3.获取ExcelImport对象后需要调用init()方法初始化 ...

  3. POI 导入excel数据自动封装成model对象--代码

    所有的代码如下: import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; ...

  4. (原)编写JAVA工具之json自动封装成pojo

    代码在最后 我个人是不太喜欢http和json,可能是游戏做的多了的原因的,对通信协议和通信方式特敏感,因此即使是做应用我也会选择rpc而非http,但是有时候因为各种原因,还是不的不处理标准的htt ...

  5. 使用AJAX做关键字查询:输入框变化自动搜索、无刷新页面;

    使用AJAX做关键字查询要求:1.无刷新页面2.输入框变化自动搜索 <style type="text/css"> .k{ width:150px; height:30 ...

  6. DevExpress.LookUpEdit控件实现自动搜索定位功能 兼使用方法(looUpEdit可编辑)

    DevExpress.LookUpEdit 使用方法 设置可手动输入 this.LookUpEdit1.Properties.TextEditStyle = DevExpress.XtraEditor ...

  7. Servlet+Ajax实现搜索框智能提示

    简介:搜索框相信大家都不陌生,几乎每天都会在各类网站进行着搜索.有没有注意到,很多的搜索功能,当输入内容时,下面会出现提示.这类提示就叫做搜索框的智能提示,本门课程就为大家介绍如何使用Servlet和 ...

  8. PHP 实例 - AJAX 实时搜索

    AJAX Live Search 在下面的实例中,我们将演示一个实时的搜索,在您键入数据的同时即可得到搜索结果. 实时的搜索与传统的搜索相比,具有很多优势: 当键入数据时,就会显示出匹配的结果 当继续 ...

  9. 张春晖让视频的每词每句都可搜索:Autotiming 可以自动配字幕,还将改变哪些领域?

    张春晖让视频的每词每句都可搜索:Autotiming 可以自动配字幕,还将改变哪些领域? 对于一些电视观众来说,寻找电视节目字幕中“有趣”的Bug,拍照发到网上与其他人共同嘲笑一下,是一种观看节目之外 ...

随机推荐

  1. 【强化学习RL】model-free的prediction和control —— MC,TD(λ),SARSA,Q-learning等

    本系列强化学习内容来源自对David Silver课程的学习 课程链接http://www0.cs.ucl.ac.uk/staff/D.Silver/web/Teaching.html 本文介绍了在m ...

  2. 约束路由 用正则表达式约束路由 Constraining a Route Using a Regular Expression 精通ASP-NET-MVC-5-弗瑞曼

  3. NOI2.5 4980:拯救行动

    描述 公主被恶人抓走,被关押在牢房的某个地方.牢房用N*M (N, M <= 200)的矩阵来表示.矩阵中的每项可以代表道路(@).墙壁(#).和守卫(x).  英勇的骑士(r)决定孤身一人去拯 ...

  4. ASP.Net Core 发布到IIS Http Error 502.5 官方解决办法

    Http Error 502.5 - Process Failure 在IIS上发布.NET Core程序出现这个错误.网上搜索到的办法为什么总行不通呢? 有可能年代久远,现在的环境与当年不同,所以解 ...

  5. 利用geojson实现模型轨迹运动

    直接上代码 var viewer = new Cesium.Viewer('cesiumContainer'); //Set the random number seed for consistent ...

  6. Docker Swarm 从入门到放弃

    准备工作 我本机是macOS,所以我直接安装了docker desktop,其中包含了docker-machine,不用单独安装. 安装docker主机驱动 我在网上很多人提到了使用virtualbo ...

  7. Docker底层架构之联合文件系统

    联合文件系统(UnionFS)是一种分层.轻量级并且高性能的文件系统,它支持对文件系统的 修改作为一次提交来一层层的叠加,同时可以将不同目录挂载到同一个虚拟文件系统下(unite several di ...

  8. spring4.2.4整合ehcache

    最近工作中遇到个功能需要整合ehcache,由于spring版本用的是4.2.4,而在ehcache官网找到的集成配置文档是spring3.1的,因此配了几次都不成功,在历经一番波折后终于成功集成了s ...

  9. laravel 工厂模式到容器

    下面实现了查人拥有超能力的三种方式 第一种最基本的类引用实现 1 <?php /** * 目的:代码的完善来说明从 基础类的调用到 工厂类的使用 再到容器的出现的原因 * (首先要明白工厂类和容 ...

  10. JDK源码之Boolean类分析

    一 简介 boolean类型的封装类,将基本类型为boolean的值包装在一个对象中,实现序列化接口,和Comparable接口 额外提供了许多便捷方法,比较简单,直接贴代码分析 二 源码分析 //t ...