如果有朋友對本篇文章的一些知識點不了解的話,可以先閱讀此篇文章。在這篇文章中,我大概介紹了一下構建淘寶購物車頁面需要的基礎知識。
這篇文章主要探討的是智能搜索框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" } ] } ] } ] ]
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" } ] } ] } ] ]
這里需要注意一點的是,因為是使用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]; /*第一種方法:手寫拼接字符串,效率低,易出錯, 結構與數據未分離,不推薦使用這種方法拼接字符串*/

1 /* 2 var $html = ''; 3 //使用$.each()方法循環每一個$data, 4 //然后動態加載html, 5 //把相應的商品信息放到指定的.commodityContainer容器中 6 $.each($data, function(index, val) { 7 $html+='<div class="mainCommodity">'; 8 $html+='<div class="shopInfo">'; 9 $html+='<div class="shopMsg">'; 10 //$.each()中回調函數中的第二個參數指定的是每一個值, 11 //通過點操作來獲取每個字段。 12 //比如val.label 就為 '李寧2016新款男子籃球鞋音速3高幫反彈籃球場地鞋ABAL031'。 13 //下面的操作相同。 14 $html+='<input type="checkbox" name="shopMsg" id="'+val.label+'" class="shopMsg-input" autocomplete="off">'; 15 $html+='<label for="'+val.label+'">'; 16 $html+='店鋪:'; 17 $html+='</label>'; 18 $html+='<a href="#">'+val.shop+''; 19 $html+='</a>' 20 $html+='</div>'; 21 $html+='</div>'; 22 $html+='<div class="commodityInfo">'; 23 $html+='<ul>'; 24 $html+='<li class="td-chk">'; 25 $html+='<div class="td-inner">'; 26 $html+='<input type="checkbox" name="checkbox" autocomplete="off">'; 27 $html+='</div>'; 28 $html+='</li>'; 29 $html+='<li class="td-item">'; 30 $html+='<div class="td-inner">'; 31 $html+='<a class="desImg" href="#">'; 32 $html+='<img alt="'+val.Txt+'" src="'+val.image+'">'; 33 $html+='</a>'; 34 $html+='<div class="item-info">'; 35 $html+='<div class="item-basis-info">'; 36 $html+='<a href="#">'+val.Txt+''; 37 $html+='</a>'; 38 $html+='</div>'; 39 $html+='<div class="item-other-info">'; 40 $html+='<div class="item-other-space"></div>'; 41 $html+='<div class="item-other-list">'; 42 $html+='<a href="#" title="支持信用卡支付">'; 43 $html+='<img alt="支持信用卡支付" src="'+val.bandCard+'">'; 44 $html+='</a>'; 45 $html+='<a href="#" title="7天無理由" class="sevenDay">'; 46 $html+='<img alt="7天無理由" src="'+val.sevenDay+'">'; 47 $html+='</a>'; 48 $html+='<a href="#" title="消費者保障服務">'; 49 $html+='<img alt="消費者保障服務" src="'+val.guarantee+'">'; 50 $html+='</a>'; 51 $html+='</div>'; 52 $html+='</div>'; 53 $html+='</div>'; 54 $html+'</div>'; 55 $html+='</li>'; 56 $html+='<li class="td-info">'; 57 $html+='<div class="td-info-msg">'; 58 $html+='<p>'+val.color+'</p>'; 59 $html+='<p>'+val.size+'</p>'; 60 $html+='</div>'; 61 $html+='</li>'; 62 $html+='<li class="td-price">'; 63 $html+='<div class="td-inner">'; 64 $html+='<p class="non-discount">'+val.nonDiscount+'</p>'; 65 $html+='<p class="discount">¥'; 66 $html+='<span>'+val.num+'.00</span>'; 67 $html+='</p>'; 68 $html+='<div class="promotion">賣家促銷'; 69 $html+='<i class="promotionIcon"></i>'; 70 $html+='</div>'; 71 $html+='<div class="proSlidedown">'; 72 $html+='<p class="newPro">賣家促銷:秋季特惠</p>'; 73 $html+='<p>優惠:¥'+val.disc+'</p>'; 74 $html+='</div>'; 75 $html+='</div>'; 76 $html+='</li>'; 77 $html+='<li class="td-amount">'; 78 $html+='<div class="item-amount">'; 79 $html+='<a href="#" class="amount-left amount-color">-</a>'; 80 $html+='<input type="text" name="amountNum" value="1" autocomplete="off" />'; 81 $html+='<a href="#" class="amount-right">+</a>'; 82 $html+='</div>'; 83 $html+='<div class="stock">'+val.max+'</div>'; 84 $html+='<div class="outNum">'; 85 $html+='<span class="instr">最多只能購買</span>'; 86 $html+='<span class="stockNum"></span>'; 87 $html+='<em>件</em>'; 88 $html+='</div>'; 89 $html+='</li>'; 90 $html+='<li class="td-sum">'; 91 $html+='<em>¥</em>' 92 $html+='<span>'+val.num+'.00</span>'; 93 $html+='</li>'; 94 $html+='<li class="td-operation">'; 95 $html+='<p>'; 96 $html+='<a href="#" class="delete">刪除</a>'; 97 $html+='</p>'; 98 $html+='</li>'; 99 $html+='</ul>'; 100 $html+='</div>'; 101 $html+='</div>'; 102 //將動態加載的html放到指定的容器中, 103 //這里首先應該在html中放放上一個空容器 104 //<div className="commidityContainer"></div> 105 $('.commodityContainer').html($html); 106 107 108 }); 109 */
/*第二種方法:使用js模板引擎,結構與數據分離, 並且altTemplate效率高,速度快,推薦使用。*/
var $html = template('basketBallShoes',results); $('.commodityContainer').html($html); } } }); }
3.事件代理和事件冒泡
簡單的介紹一下這兩個概念吧。
事件傳播模型會經歷兩個階段。一是事件捕獲,二是事件冒泡。事件捕獲是指事件首先會交給最外層的元素,接着再交給更具體的元素。而事件冒泡是指當事件發生時,會首先發送給最具體的元素,在這個元素獲得響應機會之后,事件會向上冒泡到更一般的元素。
在jQuery中,默認情況下始終會在模型的冒泡階段注冊事件處理程序。因此,可以假定最具體的元素首先獲得響應事件的機會。
事件代理,也叫事件委托。事件委托就是利用事件冒泡的一項高級技術。通過事件委托,可以借助一個元素上的事件處理程序完成很多工作。
有時候我們需要給沒有被渲染到瀏覽器的 (可能將來會被渲染)一段DOM元素綁定事件,比如說給一段通過Ajax請求完成后渲染的DOM節點綁定事件。一般綁定的邏輯會在渲染前執行,綁定的時候找不到元素所以事件會失效,使用事件代理/委托可以解決這種動態加載HTML元素的事件綁定問題。並且,事件代理的性能比單獨綁定事件要好的多。
在這個實戰中,
事件冒泡主要應用在擴大全選按鈕和商品選擇按鈕的點擊范圍。
事件代理主要應用在通過Ajax異步加載的商品。
在這個例子中,把商品數量的輸入框的keypress,keyup,blur事件全部代理到body元素中,這樣就能確保每個事件都能夠生效。而不會因為HTML元素未被瀏覽器渲染而導致事件失效的情況。當然商品數量增加和商品數量減少也是同樣的原理。具體事件的代碼會在下次分享中談及。
源碼地址:Github 淘寶購物車頁面--PC端和移動端項目實戰
完。
感謝大家的閱讀。