如果有朋友对本篇文章的一些知识点不了解的话,可以先阅读此篇文章。在这篇文章中,我大概介绍了一下构建淘宝购物车页面需要的基础知识。
这篇文章主要探讨的是智能搜索框Ajax异步加载数据。jQuery的社区非常的活跃,许多朋友都在不同地方分享了很多优秀的插件。我在相关的网站上找过想实现类似功能的插件,但是没有找到。于是乎,自己动手丰衣足食。自己来搭建智能搜索框下拉列表。当然,如果有类似功能并且常维护Bug的插件,望留言交流。
源码地址:Github 淘宝购物车页面--PC端和移动端项目实战
首先需要先给大家打一根预防针。本人实现的智能搜索框下拉列表只供学习参考使用。因为智能搜索和模糊匹配的实现没有那么简单,它需要自己的一套系统。如果想实现相同的功能,可以看看这个 智能搜索框制作 的视频。 视频中远人老师介绍了如何去获取微软必应搜索引擎的数据库然后实现智能搜索和模糊匹配的功能。
简单的介绍一下智能搜索框实现的一些功能:
1.当输入特定字符(在JSON数据定义的是查询字符串('lan'))时,会有一个下拉列表弹出,并且每一个li元素都有相应的数据(调用了search.json)。这里使用的是$.get方法获取JSON数据,然后动态加载HTML,最后插入到客户端的某个空容器中。
2:在搜索框输入字符串'lan',按下回车键,会有相应的商品被异步加载到页面中。这里也是使用了jQuery Ajax的$.get方法,调用了basketballShoes.json文件。
3:当我想查询某个商品,比如李宁的音速3篮球鞋。在搜索框输入'音速3',按下回车,相应的商品被异步加载到页面中。
4:在搜索框中查询,并且被异步加载到页面中的商品,所有的事件都需要绑定到非动态加载HTML的元素上(此实战绑定在了body元素上),不能绑定在该元素上。否则,所有事件都会失效。这里涉及了事件代理和事件冒泡的原理。
以下的分享会分为如下部分
1.智能搜索下拉列表的实现
2.ajax异步获取商品
3.事件代理和事件冒泡
1.智能搜索下拉列表的实现
首先,需要做的是定义想要查询的JSON数据。
[ [ { "Query":"lan", "Results":[ { "Type":"AS", "Suggests":[ { "Txt":"Nike 耐克官方 ZOOM KOBE 男子篮球鞋 " }, { "Txt":"adidas Regulate 篮球鞋 " }, { "Txt":"李宁2016新款男子音速3高帮反弹篮球鞋" }, { "Txt":"李宁2016新款男子减震CBA魅影篮球鞋" }, { "Txt":"李宁男子专业篮球鞋CBA球迷空袭" }, { "Txt":"adidas罗斯系列场上款篮球鞋" }, { "Txt":"adidas Boost 罗斯系列篮球鞋 JXO25 " }, { "Txt":"adidas场上款篮球鞋 D Rose 7 Primeknit" } ] } ] } ] ]
search.json
JSON是指Javascript对象字面量表示法,是一种用于数据交换的文本格式。每一个JSON对象,都是一个值。要么是简单类型的值,要么是复合类型的值。JSON对值的类型和格式有着严格的规定,比如说对象的名称(键名)必须放在双引号内,数组或对象的最后一个成员的后面,不能加逗号等等。所以构建JSON时一定要放到网上去检查一下是否书写正确。否则,JSON一旦出错,浏览器也不会报错。这个查错的成本就很高了。
以下是智能搜索的js代码。输入'lan' ,就有相应的下拉框弹出。
//搜索框下拉列表 $('body').on('keyup','.header-search-input',function(event){ //获取输入的值 var $val = $(this).val(); //使用$.get()方法,并且将查询的值放在URI后面 $.get('search.json',{'Query':$val}, function(data) { for (var i = 0; i < data.length; i++) { //如果值与json中的query字段匹配,动态加载html if ($val === data[i][0].Query) { var $data = data[i][0].Results[0].Suggests; var $html= ''; $html+='<ul>'; //全局函数$.each,也可以使用for循环 $.each($data, function(index, val) { $html+='<li>'+val.Txt+'</li>'; }); $html+='</ul>'; //下列列表dispaly:none的,当符合条件后 //调用show()函数,然后设定css样式 $('.list').html($html).show().css({ 'position':'absolute', 'left':0, 'top':$('.header-search-input').height()+5 }) } } //当点击每一条li数据时,会相应的将数据作为搜索框的值 $('.list li').click(function(event) { var $liText = $(this).text(); $('.header-search-input').val($liText); }); }); //如果值为空,则隐藏整个列表 if ($(this).val() === '') { $('.list').hide(); } //按下回车时,调用shoppingCart()函数。 if (event.which === 13) { shoppingCart(); } });
2.Ajax异步获取商品
在输入相应的查询字符串('lan' 或者是 '音速3'),按下回车键之后,会有相应的商品被动态加载到html结构当中,这里调用了baskedballShoes.json。
[ [ { "Query":"lan", "Results":[ { "Type":"AS", "Suggests":[ { "Txt":"李宁2016新款男子篮球鞋音速3高帮反弹篮球场地鞋ABAL031", "num":339, "max":764, "label":"liningBas", "shop":"李宁官方网店", "image":"css/images/lining-bas.png", "color":"颜色分类:荧光果粉/木梅红", "size":"鞋码:42", "nonDiscount":"¥539.00", "bandCard":"css/images/bankCard.png", "sevenDay":"css/images/sevenDay.png", "guarantee":"css/images/guarantee.png" }, { "Txt":" adidas阿迪达斯篮球男子篮球鞋Regulate", "num":419, "max":18, "label":"adidas", "nonDiscount":"¥539.00", "image":"css/images/adidas.png", "color":"颜色分类:银金属/深藏青蓝", "shop":"adidas官方旗舰店", "size":"鞋码:43.5", "bandCard":"css/images/bankCard.png", "sevenDay":"css/images/sevenDay.png", "guarantee":"css/images/guarantee.png" } ] } ] } ], [ { "Query":"音速3", "Results":[ { "Type":"AS", "Suggests":[ { "Txt":"李宁2016新款男子篮球鞋音速3高帮反弹篮球场地鞋ABAL031", "num":339, "max":764, "shop":"李宁官方网店", "image":"css/images/lining-bas.png", "color":"颜色分类:荧光果粉/木梅红", "size":"鞋码:42", "nonDiscount":"¥539.00", "bandCard":"css/images/bankCard.png", "sevenDay":"css/images/sevenDay.png", "guarantee":"css/images/guarantee.png" } ] } ] } ] ]
basketballShoes.json
这里需要注意一点的是,因为是使用Ajax 异步加载商品,动态创建html然后返回客户端,所以需要把某件商品的信息全部写到JSON中,比如商品图片,商品信息,单价,数量,金额等。然后再循环遍历每一个数组中的元素,通过点操作将数据写入浏览器中。
以下是Ajax异步获取商品的js代码。输入'lan'回车或者输入'音速3'回车。当然,可以在JSON中自行修改字符串的匹配。需要注意的是,因为是按下回车之后的查询,所以当event.which===13的时候,调用了shoppingCart()函数。
修改:感谢@troy.cui 和 @Genius Zhang 留言中提的意见,我已经对字符串拼接做了相应的调整。废弃了手写拼接字符串的方法,使用腾讯CDC的altTemplate.js模板引擎,这种方法的原理实质上就是在拼接字符串,并且让数据和结构相分离。相应的源代码已经放在GitHub 淘宝购物车页面--PC端和移动端项目实战了。
//购物车存放产品--- 通用function function shoppingCart(){ //获取输入框的值,用于字符串匹配 var $val = $('.header-search-input').val(); $.get('basketballShoes.json',{'Query':$val}, function(data) { for (var i = 0; i < data.length; i++) { if ($val === data[i][0].Query) { //字符串匹配 //当输入'lan'时,会匹配第一个数组 //当输入'音速3'时,会匹配第二个数组。 //也可以自行修改字符串匹配规则。 var $data = data[i][0].Results[0].Suggests; var results = data[i][0].Results[0]; /*第一种方法:手写拼接字符串,效率低,易出错, 结构与数据未分离,不推荐使用这种方法拼接字符串*/
/* var $html = ''; //使用$.each()方法循环每一个$data, //然后动态加载html, //把相应的商品信息放到指定的.commodityContainer容器中 $.each($data, function(index, val) { $html+='<div class="mainCommodity">'; $html+='<div class="shopInfo">'; $html+='<div class="shopMsg">'; //$.each()中回调函数中的第二个参数指定的是每一个值, //通过点操作来获取每个字段。 //比如val.label 就为 '李宁2016新款男子篮球鞋音速3高帮反弹篮球场地鞋ABAL031'。 //下面的操作相同。 $html+='<input type="checkbox" name="shopMsg" id="'+val.label+'" class="shopMsg-input" autocomplete="off">'; $html+='<label for="'+val.label+'">'; $html+='店铺:'; $html+='</label>'; $html+='<a href="#">'+val.shop+''; $html+='</a>' $html+='</div>'; $html+='</div>'; $html+='<div class="commodityInfo">'; $html+='<ul>'; $html+='<li class="td-chk">'; $html+='<div class="td-inner">'; $html+='<input type="checkbox" name="checkbox" autocomplete="off">'; $html+='</div>'; $html+='</li>'; $html+='<li class="td-item">'; $html+='<div class="td-inner">'; $html+='<a class="desImg" href="#">'; $html+='<img alt="'+val.Txt+'" src="'+val.image+'">'; $html+='</a>'; $html+='<div class="item-info">'; $html+='<div class="item-basis-info">'; $html+='<a href="#">'+val.Txt+''; $html+='</a>'; $html+='</div>'; $html+='<div class="item-other-info">'; $html+='<div class="item-other-space"></div>'; $html+='<div class="item-other-list">'; $html+='<a href="#" title="支持信用卡支付">'; $html+='<img alt="支持信用卡支付" src="'+val.bandCard+'">'; $html+='</a>'; $html+='<a href="#" title="7天无理由" class="sevenDay">'; $html+='<img alt="7天无理由" src="'+val.sevenDay+'">'; $html+='</a>'; $html+='<a href="#" title="消费者保障服务">'; $html+='<img alt="消费者保障服务" src="'+val.guarantee+'">'; $html+='</a>'; $html+='</div>'; $html+='</div>'; $html+='</div>'; $html+'</div>'; $html+='</li>'; $html+='<li class="td-info">'; $html+='<div class="td-info-msg">'; $html+='<p>'+val.color+'</p>'; $html+='<p>'+val.size+'</p>'; $html+='</div>'; $html+='</li>'; $html+='<li class="td-price">'; $html+='<div class="td-inner">'; $html+='<p class="non-discount">'+val.nonDiscount+'</p>'; $html+='<p class="discount">¥'; $html+='<span>'+val.num+'.00</span>'; $html+='</p>'; $html+='<div class="promotion">卖家促销'; $html+='<i class="promotionIcon"></i>'; $html+='</div>'; $html+='<div class="proSlidedown">'; $html+='<p class="newPro">卖家促销:秋季特惠</p>'; $html+='<p>优惠:¥'+val.disc+'</p>'; $html+='</div>'; $html+='</div>'; $html+='</li>'; $html+='<li class="td-amount">'; $html+='<div class="item-amount">'; $html+='<a href="#" class="amount-left amount-color">-</a>'; $html+='<input type="text" name="amountNum" value="1" autocomplete="off" />'; $html+='<a href="#" class="amount-right">+</a>'; $html+='</div>'; $html+='<div class="stock">'+val.max+'</div>'; $html+='<div class="outNum">'; $html+='<span class="instr">最多只能购买</span>'; $html+='<span class="stockNum"></span>'; $html+='<em>件</em>'; $html+='</div>'; $html+='</li>'; $html+='<li class="td-sum">'; $html+='<em>¥</em>' $html+='<span>'+val.num+'.00</span>'; $html+='</li>'; $html+='<li class="td-operation">'; $html+='<p>'; $html+='<a href="#" class="delete">删除</a>'; $html+='</p>'; $html+='</li>'; $html+='</ul>'; $html+='</div>'; $html+='</div>'; //将动态加载的html放到指定的容器中, //这里首先应该在html中放放上一个空容器 //<div className="commidityContainer"></div> $('.commodityContainer').html($html); }); */
手写拼接字符串,易出错,效率低
/*第二种方法:使用js模板引擎,结构与数据分离, 并且altTemplate效率高,速度快,推荐使用。*/
var $html = template('basketBallShoes',results); $('.commodityContainer').html($html); } } }); }
3.事件代理和事件冒泡
简单的介绍一下这两个概念吧。
事件传播模型会经历两个阶段。一是事件捕获,二是事件冒泡。事件捕获是指事件首先会交给最外层的元素,接着再交给更具体的元素。而事件冒泡是指当事件发生时,会首先发送给最具体的元素,在这个元素获得响应机会之后,事件会向上冒泡到更一般的元素。
在jQuery中,默认情况下始终会在模型的冒泡阶段注册事件处理程序。因此,可以假定最具体的元素首先获得响应事件的机会。
事件代理,也叫事件委托。事件委托就是利用事件冒泡的一项高级技术。通过事件委托,可以借助一个元素上的事件处理程序完成很多工作。
有时候我们需要给没有被渲染到浏览器的 (可能将来会被渲染)一段DOM元素绑定事件,比如说给一段通过Ajax请求完成后渲染的DOM节点绑定事件。一般绑定的逻辑会在渲染前执行,绑定的时候找不到元素所以事件会失效,使用事件代理/委托可以解决这种动态加载HTML元素的事件绑定问题。并且,事件代理的性能比单独绑定事件要好的多。
在这个实战中,
事件冒泡主要应用在扩大全选按钮和商品选择按钮的点击范围。
事件代理主要应用在通过Ajax异步加载的商品。
在这个例子中,把商品数量的输入框的keypress,keyup,blur事件全部代理到body元素中,这样就能确保每个事件都能够生效。而不会因为HTML元素未被浏览器渲染而导致事件失效的情况。当然商品数量增加和商品数量减少也是同样的原理。具体事件的代码会在下次分享中谈及。
源码地址:Github 淘宝购物车页面--PC端和移动端项目实战
完。
感谢大家的阅读。