jQuery想必各個web工程師都再熟悉不過了,不過現現在不少網站還採用了很古老的jQuery版本。其實若是早期版本使用不當,可能會有DOMXSS漏洞,很是建議升級到jQuery 1.9.x或以上版本。前段時間我就主導了這件事情,把公司裏咱們組負責的項目jQuery版本從1.4.2升級到了jQuery 1.11.3。jQuery官方也爲相似升級工做提供了jQuery Migrate插件。javascript
言歸正傳。css
jQuery 1.11.3是1.x時代的最後一個版本(做者更新:2016年1月8日,jQuery 1.12.0上線,jQuery 1.11.3再也不是1.x時代最後一個版本了),因爲個人部門項目已經有必定年頭了,當時仍是採用的jQuery 1.4.2,此次升級步子邁得算是比較大。早期時候jQuery的不少寫法,在新版本中已經被廢棄,亦或者有些不規範的寫法,當時版本還能支持,可是如今已經不支持。更糟糕的狀況是,新版本還支持,可是功能已經和之前不同了……這種狀況連個錯都不會報,須要深刻到代碼邏輯裏面去看。html
jQuery官方推薦了jQuery Migrate 庫來解決jQuery升級問題。不過一直採用這個庫終究不是長久之計,開發中建議使用jQuery Migrate的開發版,能夠在瀏覽器控制檯上打印出來不兼容的地方詳細信息。須要注意的是開發中必定要使用jQuery Migrate的開發版,由於壓縮版的是不會在控制檯給出警告的……把jQuery Migrate的庫緊跟在jQuery庫後面引用便可:前端
<script src="<path>/<to>/jquery-1.11.3.js"></script> <script src="<path>/<to>/jquery-migrate-1.2.1.js"></script>
等升級完畢,肯定沒問題了以後,再將jQuery Migrate庫去掉就能夠了。根據我的經驗,下面我把坑分紅 常見坑,少見坑兩類來論述。java
jQuery.fn.live
方法jQuery Migrate庫對此錯誤也在控制檯有相應的警告:jquery
JQMIGRATE: jQuery.fn.live() is deprecated
live
方法本來的做用是設置事件代理,該方法在jQuery 1.7以後就不推薦使用了,取代之的是jQuery.fn.on
函數。他們的接口分別是:程序員
$(selector).live('click', function(){/* some code */});
$(selector).on('click', [selector,] function(){/* some code */});
乍一看,中括號裏面的參數能夠省略掉,倆函數不是如出一轍麼?因而天真地把函數名live直接替換成on,大部分時候,這麼作好像沒有引發任何異常。可是若是在你調用on函數的時候,前面的$(selector)
在當前的網頁上根本不匹配任何元素(該元素多是後面的代碼才加到DOM裏的),那是不會綁定成功的。事實上,live函數將$(selector)
代理到了document
元素上,這個元素是確定存在的,因此不會出現相似狀況。正確的替換方法應該是:web
$(selector).live('click', function(){/* some code */}); 替換爲
$(document).on('click', selector, function(){/* some code */});
jQuery.fn.die
方法jQuery Migrate對此錯誤的警告是:ajax
JQMIGRATE: jQuery.fn.die() is deprecated
這個方法和前面的live恰好反過來,取消事件處理函數的綁定。新版本中應該使用off函數代替之,替換方式相似。正則表達式
jQuery.fn.toggle
函數jQuery Migrate對此錯誤的警告是:
JQMIGRATE: jQuery.fn.toggle(handler, handler...) is deprecated
早期jQuery中名字叫toggle的函數有兩個,一個是用於控制元素的顯示和隱藏,這個用途的函數目前jQuery中依舊存在;另外一個就是上面提到的被廢棄的toggle函數,它用於綁定至少兩個函數到同一個元素,點擊該元素的時候兩個函數交替着執行。這兩個同名函數功能相差甚遠,爲了避免引發誤導,在jQuery 1.8中就再也不建議使用了。替換的方式是把兩個函數合併成一個函數的if-else兩個區段,而後本身設置一個boolean變量,控制每次點擊時應該執行哪一個區段便可。
jQuery.browser
屬性jQuery Migrate對此錯誤的警告是
JQMIGRATE: jQuery.browser is deprecated
在前端開發中咱們常常要根據不一樣的瀏覽器版本作出不一樣的處理,jQuery.browser
原本是經過瀏覽器的userAgent字段來提取瀏覽器相關信息的。新版本中已經將其廢棄,而是建議使用特徵檢測的方法去判斷,而且給了一個Modernizr庫做爲推薦。不過,改爲這個庫可能改動成本有點大,若是你仍是想沿用jQuery.browser
的思路的話,能夠本身去實現一下它。例如,判斷是否是IE瀏覽器,能夠用
/msie/.test(navigator.userAgent.toLowerCase());
即本身手動獲取userAgent字段,而且作一個正則表達式匹配。其餘瀏覽器思路相似,都是對navigator.userAgent
作一個正則匹配。
$(html)
格式書寫錯誤在jQuery Migrate中,出現如下三種警告中的任何一種,都是屬於這個錯誤:
1. JQMIGRATE: $(html) HTML strings must start with '<' character 2. JQMIGRATE: $(html) HTML text after last tag is ignored 3. JQMIGRATE: HTML string cannot start with a '#' character
這個錯誤仍是蠻值得注意的,由於咱們文章開頭所說的jQuery低版本有XSS漏洞,其實就是和這個錯誤有關係。在javascript中咱們常常會直接將一段html格式的字符串寫在jQuery引用裏面,好比$('<div></div>')
。按照新版本的jQuery要求,這段html格式的字符串必須是以左尖括號(小於號)開頭,其餘字符都不能夠。如下幾種寫法,都是錯誤的:
1. $(" <div></div>"); //錯誤,字符串最開頭有一個空格,不是以小於號'<'開頭的 2. $("<div></div>test"); //不標準,html標籤結束後後面還有多餘的"test",它會被忽略 3. $("#<div></div>); //錯誤,以井號開頭而且後面並非一個css選擇器
這一點在書寫的時候注意一下就能夠了,其實仍是很容易避免的。其中第三種錯誤其實就不只僅是警告了,jQuery會直接拋出一個錯誤,中止javascript代碼的繼續執行。通常狀況以井號開頭,例如$("#test")
,其實就是一個普通的選擇器,可是上面例子中後面又夾雜着html字符串,這會被jQuery判斷爲潛在的XSS攻擊。
jQuery.fn.attr
方法的錯誤使用(這是個很是易犯的錯誤!)jQuery Migrate中,關於attr方法的警告有如下這些:
1. JQMIGRATE: jQuery.fn.attr('value', val) no longer sets properties
2. JQMIGRATE: jQuery.fn.attr('value') no longer gets properties
3. JQMIGRATE: jQuery.fn.attr('checked') may use property instead of attribute
4. JQMIGRATE: jQuery.fn.attr( props, pass ) is deprecated
實踐中我發現,早期寫的代碼裏面,獲取一個input輸入表單的值時,是怎麼獲取的呢?$('input').attr('value')
;又是怎麼設置的呢?$('input').attr('value', 'helloworld')
。這在新版本中都是不正確的!正確的作法應該是
$('input').val(); //獲取input表單如今所輸入的值
$('input').val('helloworld'); //設置input表單輸入的值
究竟是獲取仍是設置,只取決於調用val方法時有沒有帶着參數。
若是你想手動設置單選框(例如<input type="radio" >
)被選中,應該怎麼設置呢?老的代碼裏面可能會看到這樣 $('input').attr('checked', true)
或者$('input').attr('checked', 'checked')
。這些如今也都是不正確的!正確的作法應該是
$('input').prop('checked', true); //把單選框設爲選中狀態
$('input').prop('checked'); //獲取單選框是否是被選中了,返回true或false
這是從jQuery 1.6版本開始使用的寫法。若是設置disabled和selected屬性,也是使用prop方法。那到底何時使用attr方法呢?二者的區別是:prop設置的是某元素固有的屬性,而attr設置的是寫在html標籤上的自定義屬性。舉個例子:
<input type="checkbox" checked="checked" haha="hello" > var v1 = $('input').prop("checked"); //返回true/false,是否被選中,隨狀態改變而改變 var v2 = $('input').attr("checked"); //返回"checked",這是你設置在標籤上的,不會變 var v3 = $('input').attr("haha"); //返回"hello",自定義屬性 var v4 = $('input').prop("haha"); //返回undefined,根本沒有這個固有屬性
上面提到的第四個錯誤,jQuery.fn.attr(props, pass) is deprecated
這個警告在真實項目中從未見到過,看了一下源碼,觸發該警告的jQuery寫法不多見,可忽略。
$.parseJSON
傳入了非法的參數在jQuery Migrate中,該錯誤產生以下警告
JQMIGRATE: jQuery.parseJSON requires a valid JSON string
jQuery之因此改這個接口,是爲了和瀏覽器自帶的JSON.parse接口對齊,從jQuery 1.9開始生效。這個問題常見於AJAX接收服務端返回值的時候。服務端可能返回一個空字符串,這時候調用該接口會產生錯誤。必須向$.parseJSON
傳入合法的JSON字符串。修正方法以下:
var v1 = $.parseJSON(str); 替換爲
var v1 = $.parseJSON( str ? str : "null" );
在jQuery Migrate中該錯誤產生以下警告
JQMIGRATE: 'hover' pseudo-event is deprecated, use 'mouseenter mouseleave'
在註冊事件處理函數時,'hover'之前能夠看做是'mouseenter mouseleave'兩個事件的別稱。目前已經將該別稱去掉了,因此代碼中請用'mouseenter mouseleave'替換之。
jQuery.fn.andSelf
已經被替換,不能再使用jQuery Migrate中是這樣的警告:
JQMIGRATE: jQuery.fn.andSelf() replaced by jQuery.fn.addBack()
兩個函數功能是徹底同樣的,能夠直接替換。
以上,就是在jQuery升級中常見的問題,固然,本着精益求精的精神,咱們仍是須要研究一下不常見的問題是什麼樣子的。須要指出的是:下面的問題在個人實際項目中歷來沒有碰到過,比較少見,但也沒法保證必定不會出如今你的項目中,僅供感興趣的程序員們參考吧。
這個錯誤的觸發方式很是簡單,直接把html頁面最頂端的<!DOCTYPE html>
標籤刪掉就能夠了。瀏覽器怪異模式是爲了兼容老古董網頁而設計的,詳情可參考這篇文章:連接。我想如今的WEB程序員應該不會傻到不寫DOCTYPE,也不多使用這種模式下的瀏覽器吧。
jQuery Migrate展現的錯誤警告以下:
jQuery Migrate中的警告以下:
JQMIGRATE: AJAX events should be attached to document: ajaxStart
jQuery中AJAX全局事件包括以下接口ajaxStart
, ajaxStop
, ajaxSend
, ajaxComplete
, ajaxError
, ajaxSuccess
。由於這些事件使用的比較少,因此也歸在少見坑當中。從jQuery 1.9開始,這些事件只能綁定到$(document)
上。改正方法以下(摘自jQuery官網):
$("#status").ajaxStart(function(){ $(this).text("Ajax started"); }); 修改成
$(document).ajaxStart(function(){ $("#status").text("Ajax started"); });
在jQuery Migrate中是這樣的警告:
JQMIGRATE: Can't change the 'type' of an input or button in IE 6/7/8
改變input的表單的type屬性,你能夠直接把文本框改爲單選框,改爲多選框等等。雖然我感受這是一種並不算優雅的行爲,可是不少瀏覽器都是支持這麼作的,除了IE6/7/8。建議在實際中也是少用這個功能爲好。
$.clean
, $.event.handle
, $.attrFn
, $.fn.data('events')
, jQuery.event.trigger
屬性與方法在jQuery Migrate中是這樣的警告:
1. JQMIGRATE: jQuery.clean() is deprecated
2. JQMIGRATE: jQuery.event.handle is undocumented and deprecated
3. JQMIGRATE: jQuery.attrFn is deprecated
4. JQMIGRATE: Use of jQuery.fn.data('events') is deprecated
5. JQMIGRATE: Global events are undocumented and deprecated
若是你在本身的代碼中使用過這五個接口,那確實是仔細研究過jQuery源代碼的高人啊。由於這五個接口歷來沒有出如今jQuery的官方文檔中,而且有些在後續版本中已經刪除,可謂來無影去無蹤。看源代碼的話在早期版本有機會找到他們的存在,可是並不建議使用。建議採用其餘方法實現相應的功能。什麼?你不知道這五個函數是什麼功能?那最好了,你如今也不須要知道了……
$.sub()
方法jQuery Migrate中對本問題的警告以下:
JQMIGRATE: jQuery.sub() is deprecated
這個接口很是簡單,不接受任何參數。它用來建立一個jQuery的副本。該方法在jQuery 1.7版本開始就已經再也不使用。
jQuery.fn.error
方法jQuery Migrate中對本問題的警告以下:
JQMIGRATE: jQuery.fn.error() is deprecated
在jQuery中,error
也是和click
同樣的事件。註冊該事件的處理函數,之前是$(selector).error(function(){})
,如今已經被廢棄,可使用$(selector).on('error', function(){})
來替代。
本文既然自稱爲「XX大全」,那就應該儘可能的全面一些。爲了搞明白這些坑是怎麼踩進去的,咱們最後來寫一段js代碼,要求是用最少的代碼,把jQuery Migration庫中全部的坑都踩一遍……也就是讓jQuery Migration庫打印出來它能打印的全部警告。最終的代碼以下所示(博客園居然沒有辦法上傳附件,只能貼代碼了),很是簡單易懂。打開index.html文件,而後再按F12鍵打開控制檯,你就能夠看到壯觀宏偉的控制檯警告了^_^
<!-- filename : index.html -->
<!--<!DOCTYPE html>--> //keng0 怪異模式 <html> <head> <meta charset="utf-8" /> <title>jQuery升級踩坑大全</title> <script type="text/javascript" src="http://code.jquery.com/jquery-2.1.4.min.js" ></script> <script type="text/javascript" src="http://code.jquery.com/jquery-migrate-1.2.1.js" ></script> </head> <body> <div class="test" id="a">a</div> <input type="radio" id="b" value="b" /> <input type="radio" id="c" value="c" /> <div id="d" value="d">test</div> <script type="text/javascript"> //開始踩坑 //使用被廢棄分$.attrFn方法 var keng1 = $.attrFn || {}; //該函數在jQuery內部調用,真實項目中從未見過,可忽略,這裏只是爲了觸發一下錯誤警告 var keng2 = $.attr($("#a"), "class", "xxx", true); //IE六、七、8中不支持改變輸入框的類型 var keng3 = $("input#b").attr("type", "text"); //在該使用prop的地方使用了attr var keng4 = $("input#c").attr("checked", true); //使用attr獲取property的值,正確的是應該使用 .val() var keng5 = $("div#d").attr("value"); //使用attr設置property的值,正確的是應該使用 .val('somevalue') var keng6 = $("div#d").attr("value", "abcd"); //html字符串必須以'<'開頭(下面這個是以空格開頭) var keng7 = $(" <div></div>"); //最後一個tag後面還有多餘字符串 var keng8 = $("<div></div>abc"); //html字符串不能夠以井號‘#’開頭 try{ var keng9 = $("#<div></div>"); }catch(e){ console.error(e); } //$.parseJSON的參數必須是合法的JSON字符串 var keng10 = $.parseJSON(undefined); //使用被廢棄的$.browser var keng11 = $.browser; //使用被廢棄的$.sub var keng12 = $.sub(); $("#c").on("click", function(){}); var keng13 = $("#c").data("events"); //調用了已經再也不使用的函數andSelf,該函數已經被addBack替代 var keng14 = $("#c").nextAll().andSelf(); //使用被廢棄的$.clean方法 try{ var keng15 = $.clean(); }catch(e){ console.error(e); } //"hover"字符串註冊事件已經被拆成"mouseenter"和"mouseleave"兩個 var keng16 = $("#d").on("hover", function(){/*some code*/}); //jQuery.event.handle並無收錄到官方的API中,新版本已經被移除 var keng17 = function(){ $.event.handle.apply(this, arguments); }; //全局AJAX事件處理必須綁定到document對象上 var keng18 = $("#c").ajaxStart(function(){}); //使用了被廢棄的error方法 var keng19 = $("#c").error(function(){}); //使用了被廢棄的toggle方法 var keng20 = $("#d").toggle(function(){/*some code*/}, function(){/*some code*/}); //使用了被廢棄的live方法,應該使用on方法替代之 var keng21 = $("#a").live("click", function(){/*some code*/}); //使用了被廢棄的die方法,應該使用off方法替代之 var keng22 = $("#a").die("click"); //使用了全局事件函數,目前全局事件只支持AJAX那幾個,其餘全局事件都不支持 var keng23 = $.event.trigger("click"); </script> </body> </html>