一場由股票提醒助手插件引起的血案——淺入淺出 jquery autocomplete

 
我沒有學過前端,因此這篇文章註定要班門弄斧了。

一般,需要用到什麼技術什麼語言時,我纔去學,學了也不必定掌握,就是記不住!因此現在明確了。學習的時候,亦或是攻克難點的時候,必定要記錄下來。並不必定非要呈現什麼高大上的技術。但求復原本身的心路歷程足以。
你們都知道。近期股市很是火爆。固然這幾天正在調整期呵呵,神車復牌都交出一天漲停一天跌停的答卷自降爲靈車,相比於528垂直過山車,見怪不怪了。很是多人儘管沒投入大量精力炒股,但仍是時刻關注着股市行情,上班族們總不能時刻盯着手機看吧。因此我就想到了股票插件,chrome瀏覽器(361極速瀏覽器也OK)中的股票插件 https://chrome.google.com/webstore/detail/%E8%82%A1%E7%A5%A8%E6%8F%90%E9%86%92%E5%8A%A9%E6%89%8B/goiffchdhlcehhgdpdbocefkohlhmlom#,這個玩意可以加入自選股,並且差點兒實時地經過新浪財經同步(就是看着數字就放心了),試用事後發現挺有意思,因而嘗試獲取源代碼,把這個插件的所有html/js/css/image文件都下載下來,並且依照chrome插件開發教程把文件結構、manifest都一步步搞定,現在已經在github開源:https://github.com/hustlbj/chrome-extensions倉庫中的StockHelper。
簡介一下這個小插件:在瀏覽器插件欄中的圖標是background.html,實時獲取自選股的價格,而後刷新到圖標和標題中;popup.html是點擊圖標之後彈出的詳情頁面,可以查看每支股票的價格折線圖、日K線圖、成交量之類的詳情,選中某支股票,則該支股票的當前價會實時刷新到圖標中。options.html是點擊自選股後打開的頁面。進行自選股的加入刪除,輸入名稱、簡寫、代碼都可以查找。

它的股票代碼是保存在一個stocks.js文件裏的。咱們在加入新股票輸入部分字母時,會運行autocomplete來本身主動搜索匹配stock.js中的股票而後爲咱們呈現一個選項列表。這就是本身主動填充的功能,例如如下圖:css

那麼問題來了,將候選數據保存在本地文件(變量)中。儘管檢索速度快,但是新股IPO之後還得更新插件版本號或者你本身把代碼加到stocks.js中。雖然說不難,但是我可替廣大小白表示忍不了。。好嘛,既然源碼都搞定了,修復個小bug也是順便的事,不修復不舒服斯基。


查看源代碼,options.html中引用的是
jquery 1.7
autocomplete 1.1(注意版本號,新的autocomplete的參數列表可能不一樣,這裏不作討論)
默認參數:
咱們看autocomplete是怎樣使用的(input表明輸入框對象):
input.autocomplete(stockInfos, {
     省略很是多參數設置, 
     formatItem: function(){}, 
     fomatMatch: function(){}, 
     formatResult: function(){}
}).result(function() {})。

很是明顯,stockInfos就是從本地文件裏讀入的候選數據源,format相關函數就是規定本身主動補全時怎樣來匹配,最後的result則是選中某項候選值後界面需要作的顯示工做。

首先。需要解決 怎樣用遠程數據源替換掉本地的靜態數據源stockInfos來做爲autocomplete的候選數據源(本文不探討本地變量做爲數據源。。)。不懂怎麼辦,搜索之,參考 http://www.cnblogs.com/weixing/archive/2013/06/06/3120535.html 遠程數據源的調用方法, 這裏要注意的是jquery.autocomplete或者是jquery.UI版本號不正確的話,可能並不是這些參數設置,比方新版本號的jquery.UI中的autocomplete  http://api.jqueryui.com/autocomplete/,咱們仍是 使用autocomplete 1.1,看源代碼的話最好不要用min版本號。改動的方法就是。把上面調用autocomplete時第一個參數stockInfos替換成 URL。同一時候設置 dataType參數。當需要向遠程傳遞參數時還需要 extraParams參數,假設遠程傳回的數據格式不適合autocomplete,咱們還需要 本身定義parse函數。而後我先是改爲了例如如下(http://suggest3.sinajs.cn/suggest/key=你的查找,這是新浪提供的接口,相似的還有很是多。這裏不作詳述):

在瀏覽器中執行。當在輸入框中輸入幾個字後,autocomplete向遠程url請求數據,在瀏覽器的控制檯Network界面,發現有請求和響應數據,並且響應數據和咱們直接在瀏覽器中訪問該url的內容同樣比方 http://suggest3.sinajs.cn/suggest/?

q=shang&limit=10&timestamp=1433840697966&key=shang除了key字段是咱們的遠程URL查找必需的之外,autocomplete本身主動加入了q、limit、timestamp參數但是收到數據(瀏覽器控制檯的Response中輸出了var suggestvalue="shanghai ind h,31,00363,00363,上海實業控股,shsykg;shanghai tonva。html

。。是正確地)後並無運行parse函數和興許的format相關函數。前端


錯誤排查:
首先檢查autocomplete源代碼(不要看min.js,下載相應版本號的autocomplete源代碼),打開這個文件發現,autocomplete的參數列表:delay、scroll、highlight、max、minChar、dataType、extraParams等參數都是有的。formatItem等函數、parse函數也都有,但是彷佛並無運行parse()。

autocomplete中 原始的parse函數,咱們作一些log調試:
 
咱們在調用autocomplete時本身定義的parse函數,因爲瀏覽器控制檯並未輸出本身定義parse的log。因此該函數並未運行。

原始的parse()函數也並未運行。jquery


再依據autocomplete源代碼進行溯源, 調用parse的函數是request函數,也就是依據url參數來向遠程請求,由於瀏覽器控制檯中有請求和響應,因此request函數確定運行了,分析其源代碼, ajax發送請求後假設成功則運行success標籤相應的處理程序這裏並無error相應的處理程序,推測多是收到的錯誤響應。因此並無運行success,咱們加入error處理函數:
在瀏覽器控制檯觀察其執行。如我所料執行到了error處理程序中:
果真,ajax請求收到的是返回被斷定爲錯誤類型,因此並無運行success處理函數。請求響應Headers例如如下:
發現響應的Headers的Content-Type是text/html,而咱們在調用autocomplete時設置的dataType類型是json,顯然是衝突的(不能盲目照抄別人的代碼!仍是要依據本身的實際狀況!

),因此request函數中以爲收到的信息是錯誤的,因此沒有運行success處理,也就沒有運行parse()函數進行解析。git


解決方式:
調用autocomplete時, dataType參數依據本身的數據源類型來定,比方個人 數據源返回的是text/html,並不是json。因此我這裏改爲text。再次運行:
成功運行了本身定義的parse函數。
這裏,個人parse函數解析還有bug,因此解析返回的數組是空,請不要在乎,依據原始數據來作調整處理就能夠。
parse函數解析成功後,構造一個數組parsed返回,在這裏parse函數返回的是[Object, Object, Object, ...]例如如下:

而後會運行formatItem函數:
控制檯報出item是undefined,並且item.name也報出了Uncaught TypeError: Cannot read property 'name' of undefined錯誤。一個問題解決,又一個問題來了。

github

web


排查和解決parse()解析源數據(數據格式不正確。undefined錯誤)的問題:
繼續追蹤autocomplete源碼, request函數在success處理程序中運行parse函數結果保存在parsed變量中,而後調用success(term, parsed)函數,success()函數是調用request函數時傳入的函數見request聲明function request(term, success, failure)。繼續追蹤request調用的地方有兩處,一處使search中的request(value, findValueCallback, findValueCallback),還有一處是 onChange()函數中的request(currentValue, receiveData, hideResultsNow),在這兩處分別打上console.log("search")和console.log("onChange")。又一次在瀏覽器中執行,發現Console端輸出了onChange,說明是 onChange函數調用了request來發起請求、處理返回結果等等興許

因此,request在執行完parse函數之後。調用了傳進來的reveiceData函數。繼續追蹤receiveData函數,在該函數中打上console.log("receiveData"),還可以輸出一下data[0],又一次執行後發現輸出了receiveData 和data[0]並沒有差錯。ajax

receiveData(q, data)中調用了select.display(data, q),display中也打上console.log("display")輸出日誌,裏面調用了init()函數和fillList()函數,init中並沒有數據處理相關代碼。因此咱們關注fillList()。進入fillList()函數,打上console.log("fillList")輸出日誌做爲函數跟蹤。不難發現,fillList()函數對咱們的parse後的data作了一些處理而後傳給了formatItem函數,至此,整條路徑基本完畢。觀察它是怎樣調用formatItem函數的:chrome

var formatted = options.formatItem(data[i].data, i+1, max, data[i].value, term);
而咱們的data格式爲:
data是一個數組,data[i]索引到數組中的元素,而data[i].data卻沒有定義!因此咱們在最初調用autocomplete時的formatItem(item, i, max)接收了三個參數data[i].data、i+一、max,而data[i].data是沒有定義的,因此formatItem時報出了item.name undefined錯誤!

!OK。排查階段最終完了。問題出在了parse返回的數據data並不符合fullList()函數中對data的操做!json

因此。改動本身定義的parse函數:

再次在瀏覽器中執行,成功了!

最後,總結一下autocomplete()的運行流程:
你的js中調用$(你的輸入框).autocomplete(本地數據源或者遠程URL, {
     參數列表, 
     dataType: json或者text等等, 
     extraParams:{參數名: function(){ return 參數值如$(你的輸入框).val()}}, 
     parse: function(){}, 
     format相關函數,  
     }).result(function(){});
而後,當你在輸入框中輸入時。autocomplete就調用onChange函數->request()函數向URL發送請求->parse()函數處理遠程的數據,而後產出一個數組[{data:{ }, value{ }}, {...}, {...}, {...}, ...] -> receiverData() -> display() -> fillList() -> formatItem()。


不管如何。感謝七一〇提供的股票提醒助手,想研究的同窗到個人github下載就能夠https://github.com/hustlbj/chrome-extensions
相關文章
相關標籤/搜索