<ruby id="bdb3f"></ruby>

    <p id="bdb3f"><cite id="bdb3f"></cite></p>

      <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
        <p id="bdb3f"><cite id="bdb3f"></cite></p>

          <pre id="bdb3f"></pre>
          <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

          <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
          <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

          <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                <ruby id="bdb3f"></ruby>

                ThinkChat2.0新版上線,更智能更精彩,支持會話、畫圖、視頻、閱讀、搜索等,送10W Token,即刻開啟你的AI之旅 廣告
                <h2 id="11.1">概述</h2> jQuery是目前使用最廣泛的JavaScript函數庫。據[統計](http://w3techs.com/technologies/details/js-jquery/all/all),全世界57.5%的網站使用jQuery,在使用JavaScript函數庫的網站中,93.0%使用jQuery。它已經成了開發者必須學會的技能。 jQuery的最大優勢有兩個。首先,它基本是一個DOM操作工具,可以使操作DOM對象變得異常容易。其次,它統一了不同瀏覽器的API接口,使得代碼在所有現代瀏覽器均能運行,開發者不用擔心瀏覽器之間的差異。 ## jQuery的加載 一般采用下面的寫法,在網頁中加載jQuery。 ```html <script type="text/javascript" src="//code.jquery.com/jquery-1.11.0.min.js"> </script> <script> window.jQuery || document.write( '<script src="js/jquery-1.11.0.min.js" type="text/javascript"><\/script>' ); </script> ``` 上面代碼有兩點需要注意。一是采用[CDN](http://jquery.com/download/#using-jquery-with-a-cdn)加載。如果CDN加載失敗,則退回到本地加載。二是采用協議無關的加載網址(使用雙斜線表示),同時支持http協議和https協議。 目前常用的jQuery CDN有以下這些。 - [Google CDN](https://developers.google.com/speed/libraries/devguide#jquery) - [Microsoft CDN](http://www.asp.net/ajax/cdn#jQuery_Releases_on_the_CDN_0) - [jQuery CDN](http://jquery.com/download/#jquery-39-s-cdn-provided-by-maxcdn) - [CDNJS CDN](http://cdnjs.com/libraries/jquery/) - [jsDelivr CDN](http://www.jsdelivr.com/#!jquery) 上面這段代碼最好放到網頁尾部。如果需要支持IE 6/7/8,就使用jQuery 1.x版,否則使用最新版。 ## jQuery基礎 ### jQuery對象 jQuery最重要的概念,就是`jQuery`對象。它是一個全局對象,可以簡寫為美元符號`$`。也就是說,`jQuery`和`$`兩者是等價的。 在網頁中加載jQuery函數庫以后,就可以使用jQuery對象了。jQuery的全部方法,都定義在這個對象上面。 ```javascript var listItems = jQuery('li'); // or var listItems = $('li'); ``` 上面兩行代碼是等價的,表示選中網頁中所有的`li`元素。 ### jQuery構造函數 `jQuery`對象本質上是一個構造函數,主要作用是返回`jQuery`對象的實例。比如,上面代碼表面上是選中`li`元素,實際上是返回對應于`li`元素的`jQuery`實例。因為只有這樣,才能在DOM對象上使用jQuery提供的各種方法。 ```javascript $('body').nodeType // undefined $('body') instanceof jQuery // true ``` 上面代碼表示,由于jQuery返回的不是DOM對象,所以沒有DOM屬性`nodeType`。它返回的是jQuery對象的實例。 jQuery構造函數可以多種參數,返回不同的值。 **(1)CSS選擇器作為參數** jQuery構造函數的參數,主要是CSS選擇器,就像上面的那個例子。下面是另外一些CSS選擇器的例子。 ```javascript $("*") $("#lastname") $(".intro") $("h1,div,p") $("p:last") $("tr:even") $("p:first-child") $("p:nth-of-type(2)") $("div + p") $("div:has(p)") $(":empty") $("[title^='Tom']") ``` 本書不講解CSS選擇器,請讀者參考有關書籍和jQuery文檔。 除了CSS選擇器,jQuery還定義了一些自有的選擇器,比如`contains`選擇器用來選擇包含特定文本的元素。下面是一個例子。 ```javascript var search = $('#search').val(); $('div:not(:contains("' + search + '"))').hide(); ``` 上面代碼用來選中包含搜索框輸入文本的元素。 **(2)DOM對象作為參數** jQuery構造函數的參數,還可以是DOM對象。它也會被轉為jQuery對象的實例。 ```javascript $(document.body) instanceof jQuery // true ``` 上面代碼中,jQuery的參數不是CSS選擇器,而是一個DOM對象,返回的依然是jQuery對象的實例。 如果有多個DOM元素要轉為jQuery對象的實例,可以把DOM元素放在一個數組里,輸入jQuery構造函數。 ```javascript $([document.body, document.head]) ``` **(3)HTML字符串作為參數** 如果直接在jQuery構造函數中輸入HTML字符串,返回的也是jQuery實例。 ```javascript $('<li class="greet">test</li>') ``` 上面代碼從HTML代碼生成了一個jQuery實例,它與從CSS選擇器生成的jQuery實例完全一樣。唯一的區別就是,它對應的DOM結構不屬于當前文檔。 上面代碼也可以寫成下面這樣。 ```javascript $( '<li>', { html: 'test', 'class': 'greet' }); ``` 上面代碼中,由于`class`是javaScript的保留字,所以只能放在引號中。 通常來說,上面第二種寫法是更好的寫法。 ```javascript $('<input class="form-control" type="hidden" name="foo" value="bar" />') // 相當于 $('<input/>', { 'class': 'form-control', type: 'hidden', name: 'foo', value: 'bar' }) // 或者 $('<input/>') .addClass('form-control') .attr('type', 'hidden') .attr('name', 'foo') .val('bar') ``` 由于新增的DOM節點不屬于當前文檔,所以可以用這種寫法預加載圖片。 ```javascript $.preloadImages = function () { for (var i = 0; i < arguments.length; i++) { $('<img>').attr('src', arguments[i]); } }; $.preloadImages('img/hover-on.png', 'img/hover-off.png'); ``` **(4)第二個參數** 默認情況下,jQuery將文檔的根元素(`html`)作為尋找匹配對象的起點。如果要指定某個網頁元素(比如某個`div`元素)作為尋找的起點,可以將它放在jQuery函數的第二個參數。 ```javascript $('li', someElement); ``` 上面代碼表示,只尋找屬于someElement對象下屬的li元素。someElement可以是jQuery對象的實例,也可以是DOM對象。 ### jQuery構造函數返回的結果集 jQuery的核心思想是“先選中某些網頁元素,然后對其進行某種處理”(find something, do something),也就是說,先選擇后處理,這是jQuery的基本操作模式。所以,絕大多數jQuery操作都是從選擇器開始的,返回一個選中的結果集。 **(1)length屬性** jQuery對象返回的結果集是一個類似數組的對象,包含了所有被選中的網頁元素。查看該對象的length屬性,可以知道到底選中了多少個結果。 ```javascript if ( $('li').length === 0 ) { console.log('不含li元素'); } ``` 上面代碼表示,如果網頁沒有li元素,則返回對象的length屬性等于0。這就是測試有沒有選中的標準方法。 所以,如果想知道jQuery有沒有選中相應的元素,不能寫成下面這樣。 ```javascript if ($('div.foo')) { ... } ``` 因為不管有沒有選中,jQuery構造函數總是返回一個實例對象,而對象的布爾值永遠是true。使用length屬性才是判斷有沒有選中的正確方法。 ```javascript if ($('div.foo').length) { ... } ``` **(2)下標運算符** jQuery選擇器返回的是一個類似數組的對象。但是,使用下標運算符取出的單個對象,并不是jQuery對象的實例,而是一個DOM對象。 ```javascript $('li')[0] instanceof jQuery // false $('li')[0] instanceof Element // true ``` 上面代碼表示,下標運算符取出的是Element節點的實例。所以,通常使用下標運算符將jQuery實例轉回DOM對象。 **(3)is方法** is方法返回一個布爾值,表示選中的結果是否符合某個條件。這個用來驗證的條件,可以是CSS選擇器,也可以是一個函數,或者DOM元素和jQuery實例。 ```javascript $('li').is('li') // true $('li').is($('.item')) $('li').is(document.querySelector('li')) $('li').is(function() { return $("strong", this).length === 0; }); ``` **(4)get方法** jQuery實例的get方法是下標運算符的另一種寫法。 ```javascript $('li').get(0) instanceof Element // true ``` **(5)eq方法** 如果想要在結果集取出一個jQuery對象的實例,不需要取出DOM對象,則使用eq方法,它的參數是實例在結果集中的位置(從0開始)。 ```javascript $('li').eq(0) instanceof jQuery // true ``` 由于eq方法返回的是jQuery的實例,所以可以在返回結果上使用jQuery實例對象的方法。 **(6)each方法,map方法** 這兩個方法用于遍歷結果集,對每一個成員進行某種操作。 each方法接受一個函數作為參數,依次處理集合中的每一個元素。 ```javascript $('li').each(function( index, element) { $(element).prepend( '<em>' + index + ': </em>' ); }); // <li>Hello</li> // <li>World</li> // 變為 // <li><em>0: </em>Hello</li> // <li><em>1: </em>World</li> ``` 從上面代碼可以看出,作為each方法參數的函數,本身有兩個參數,第一個是當前元素在集合中的位置,第二個是當前元素對應的DOM對象。 map方法的用法與each方法完全一樣,區別在于each方法沒有返回值,只是對每一個元素執行某種操作,而map方法返回一個新的jQuery對象。 ```javascript $("input").map(function (index, element){ return $(this).val(); }) .get() .join(", ") ``` 上面代碼表示,將所有input元素依次取出值,然后通過get方法得到一個包含這些值的數組,最后通過數組的join方法返回一個逗號分割的字符串。 **(8)內置循環** jQuery默認對當前結果集進行循環處理,所以如果直接使用jQuery內置的某種方法,each和map方法是不必要的。 ```javascript $(".class").addClass("highlight"); ``` 上面代碼會執行一個內部循環,對每一個選中的元素進行addClass操作。由于這個原因,對上面操作加上each方法是不必要的。 ```javascript $(".class").each(function(index,element){ $(element).addClass("highlight"); }); // 或者 $(".class").each(function(){ $(this).addClass("highlight"); }); ``` 上面代碼的each方法,都是沒必要使用的。 由于內置循環的存在,從性能考慮,應該盡量減少不必要的操作步驟。 ```javascript $(".class").css("color", "green").css("font-size", "16px"); // 應該寫成 $(".class").css({ "color": "green", "font-size": "16px" }); ``` ### 鏈式操作 jQuery最方便的一點就是,它的大部分方法返回的都是jQuery對象,因此可以鏈式操作。也就是說,后一個方法可以緊跟著寫在前一個方法后面。 ```javascript $('li').click(function (){ $(this).addClass('clicked'); }) .find('span') .attr( 'title', 'Hover over me' ); ``` ### $(document).ready() $(document).ready方法接受一個函數作為參數,將該參數作為document對象的DOMContentLoaded事件的回調函數。也就是說,當頁面解析完成(即下載完&lt;/html&gt;標簽)以后,在所有外部資源(圖片、腳本等)完成加載之前,該函數就會立刻運行。 ```javascript $( document ).ready(function() { console.log( 'ready!' ); }); ``` 上面代碼表示,一旦頁面完成解析,就會運行ready方法指定的函數,在控制臺顯示“ready!”。 該方法通常作為網頁初始化手段使用,jQuery提供了一種簡寫法,就是直接把回調函數放在jQuery對象中。 ```javascript $(function() { console.log( 'ready!' ); }); ``` 上面代碼與前一段代碼是等價的。 ### $.noConflict方法 jQuery使用美元符號($)指代jQuery對象。某些情況下,其他函數庫也會用到美元符號,為了避免沖突,$.noConflict方法允許將美元符號與jQuery脫鉤。 ```html <script src="other_lib.js"></script> <script src="jquery.js"></script> <script>$.noConflict();</script> ``` 上面代碼就是$.noConflict方法的一般用法。在加載jQuery之后,立即調用該方法,會使得美元符號還給前面一個函數庫。這意味著,其后再調用jQuery,只能寫成jQuery.methond的形式,而不能用$.method了。 為了避免沖突,可以考慮從一開始就只使用jQuery代替美元符號。 ## jQuery實例對象的方法 除了上一節提到的is、get、eq方法,jQuery實例還有許多其他方法。 ### 結果集的過濾方法 選擇器選出一組符合條件的網頁元素以后,jQuery提供了許多方法,可以過濾結果集,返回更準確的目標。 **(1)first方法,last方法** first方法返回結果集的第一個成員,last方法返回結果集的最后一個成員。 ```javascri $("li").first() $("li").last() ``` **(2)next方法,prev方法** next方法返回緊鄰的下一個同級元素,prev方法返回緊鄰的上一個同級元素。 ```javascript $("li").first().next() $("li").last().prev() $("li").first().next('.item') $("li").last().prev('.item') ``` 如果`next`方法和`prev`方法帶有參數,表示選擇符合該參數的同級元素。 **(3)parent方法,parents方法,children方法** parent方法返回當前元素的父元素,parents方法返回當前元素的所有上級元素(直到html元素)。 ```javascript $("p").parent() $("p").parent(".selected") $("p").parents() $("p").parents("div") ``` children方法返回選中元素的所有子元素。 ```javascript $("div").children() $("div").children(".selected") // 下面的寫法結果相同,但是效率較低 $('div > *') $('div > .selected') ``` 上面這三個方法都接受一個選擇器作為參數。 **(4)siblings方法,nextAll方法,prevAll方法** siblings方法返回當前元素的所有同級元素。 ```javascript $('li').first().siblings() $('li').first().siblings('.item') ``` nextAll方法返回當前元素其后的所有同級元素,prevAll方法返回當前元素前面的所有同級元素。 ```javascript $('li').first().nextAll() $('li').last().prevAll() ``` **(5)closest方法,find方法** closest方法返回當前元素,以及當前元素的所有上級元素之中,第一個符合條件的元素。find方法返回當前元素的所有符合條件的下級元素。 ```javascript $('li').closest('div') $('div').find('li') ``` 上面代碼中的find方法,選中所有div元素下面的li元素,等同于$('li', 'div')。由于這樣寫縮小了搜索范圍,所以要優于$('div li')的寫法。 **(6)find方法,add方法,addBack方法,end方法** add方法用于為結果集添加元素。 ```javascript $('li').add('p') ``` addBack方法將當前元素加回原始的結果集。 ```javascript $('li').parent().addBack() ``` end方法用于返回原始的結果集。 ```javascrip $('li').first().end() ``` **(7)filter方法,not方法,has方法** filter方法用于過濾結果集,它可以接受多種類型的參數,只返回與參數一致的結果。 ```javascript // 返回符合CSS選擇器的結果 $('li').filter('.item') // 返回函數返回值為true的結果 $("li").filter(function(index) { return index % 2 === 1; }) // 返回符合特定DOM對象的結果 $("li").filter(document.getElementById("unique")) // 返回符合特定jQuery實例的結果 $("li").filter($("#unique")) ``` `not`方法的用法與`filter`方法完全一致,但是返回相反的結果,即過濾掉匹配項。 ```javascript $('li').not('.item') ``` has方法與filter方法作用相同,但是只過濾出子元素符合條件的元素。 ```javascript $("li").has("ul") ``` 上面代碼返回具有ul子元素的li元素。 ### DOM相關方法 許多方法可以對DOM元素進行處理。 **(1)html方法和text方法** html方法返回該元素包含的HTML代碼,text方法返回該元素包含的文本。 假定網頁只含有一個p元素。 ```html <p><em>Hello World!</em></p> ``` html方法和text方法的返回結果分別如下。 ```javascript $('p').html() // <em>Hello World!</em> $('p').text() // Hello World! ``` jQuery的許多方法都是取值器(getter)與賦值器(setter)的合一,即取值和賦值都是同一個方法,不使用參數的時候為取值器,使用參數的時候為賦值器。 上面代碼的html方法和text方法都沒有參數,就會當作取值器使用,取回結果集的第一個元素所包含的內容。如果對這兩個方法提供參數,就是當作賦值器使用,修改結果集所有成員的內容,并返回原來的結果集,以便進行鏈式操作。 ```javascript $('p').html('<strong>你好</strong>') // 網頁代碼變為<p><strong>你好</strong></p> $('p').text('你好') // 網頁代碼變為<p>你好</p> ``` 下面要講到的jQuery其他許多方法,都采用這種同一個方法既是取值器又是賦值器的模式。 html方法和text方法還可以接受一個函數作為參數,函數的返回值就是網頁元素所要包含的新的代碼和文本。這個函數接受兩個參數,第一個是網頁元素在集合中的位置,第二個參數是網頁元素原來的代碼或文本。 ```javascript $('li').html(function (i, v){ return (i + ': ' + v); }) // <li>Hello</li> // <li>World</li> // 變為 // <li>0: Hello</li> // <li>1: World</li> ``` **(2)addClass方法,removeClass方法,toggleClass方法** addClass方法用于添加一個類,removeClass方法用于移除一個類,toggleClass方法用于折疊一個類(如果無就添加,如果有就移除)。 ```javascript $('li').addClass('special') $('li').removeClass('special') $('li').toggleClass('special') ``` **(3)css方法** css方法用于改變CSS設置。 該方法可以作為取值器使用。 ```javascript $('h1').css('fontSize'); ``` css方法的參數是css屬性名。這里需要注意,CSS屬性名的CSS寫法和DOM寫法,兩者都可以接受,比如font-size和fontSize都行。 css方法也可以作為賦值器使用。 ```javascript $('li').css('padding-left', '20px') // 或者 $('li').css({ 'padding-left': '20px' }); ``` 上面兩種形式都可以用于賦值,jQuery賦值器基本上都是如此。 **(4)val方法** val方法返回結果集第一個元素的值,或者設置當前結果集所有元素的值。 ```javascript $('input[type="text"]').val() $('input[type="text"]').val('new value') ``` **(5)prop方法,attr方法** 首先,這里要區分兩種屬性。 一種是網頁元素的屬性,比如`a`元素的`href`屬性、`img`元素的`src`屬性。這要使用`attr`方法讀寫。 ```javascript // 讀取屬性值 $('textarea').attr(name) //寫入屬性值 $('textarea').attr(name, val) ``` 下面是通過設置`a`元素的`target`屬性,使得網頁上的外部鏈接在新窗口打開的例子。 ```javascript $('a[href^="http"]').attr('target', '_blank'); $('a[href^="//"]').attr('target', '_blank'); $('a[href^="' + window.location.origin + '"]').attr('target', '_self'); ``` 另一種是DOM元素的屬性,比如`tagName`、`nodeName`、`nodeType`等等。這要使用`prop`方法讀寫。 ```javascript // 讀取屬性值 $('textarea').prop(name) // 寫入屬性值 $('textarea').prop(name, val) ``` 所以,`attr`方法和`prop`方法針對的是不同的屬性。在英語中,`attr`是attribute的縮寫,`prop`是property的縮寫,中文很難表達出這種差異。有時,`attr`方法和`prop`方法對同一個屬性會讀到不一樣的值。比如,網頁上有一個單選框。 ```html <input type="checkbox" checked="checked" /> ``` 對于checked屬性,attr方法讀到的是checked,prop方法讀到的是true。 ```javascript $(input[type=checkbox]).attr("checked") // "checked" $(input[type=checkbox]).prop("checked") // true ``` 可以看到,attr方法讀取的是網頁上該屬性的值,而prop方法讀取的是DOM元素的該屬性的值,根據規范,element.checked應該返回一個布爾值。所以,判斷單選框是否選中,要使用prop方法。事實上,不管這個單選框是否選中,attr("checked")的返回值都是checked。 ```javascript if ($(elem).prop("checked")) { /*... */ }; // 下面兩種方法亦可 if ( elem.checked ) { /*...*/ }; if ( $(elem).is(":checked") ) { /*...*/ }; ``` **(6)removeProp方法,removeAttr方法** removeProp方法移除某個DOM屬性,removeAttr方法移除某個HTML屬性。 ```javascript $("a").prop("oldValue",1234).removeProp('oldValue') $('a').removeAttr("title") ``` **(7)data方法** data方法用于在一個DOM對象上儲存數據。 ```javascript // 儲存數據 $("body").data("foo", 52); // 讀取數據 $("body").data("foo"); ``` 該方法可以在DOM節點上儲存各種類型的數據。 ### 添加、復制和移動網頁元素的方法 jQuery方法提供一系列方法,可以改變元素在文檔中的位置。 **(1)append方法,appendTo方法** append方法將參數中的元素插入當前元素的尾部。 ```javascript $("div").append("<p>World</p>") // <div>Hello </div> // 變為 // <div>Hello <p>World</p></div> ``` appendTo方法將當前元素插入參數中的元素尾部。 ```javascript $("<p>World</p>").appendTo("div") ``` 上面代碼返回與前一個例子一樣的結果。 **(2)prepend方法,prependTo方法** prepend方法將參數中的元素,變為當前元素的第一個子元素。 ```javascript $("p").prepend("Hello ") // <p>World</p> // 變為 // <p>Hello World</p> ``` 如果prepend方法的參數不是新生成的元素,而是當前頁面已存在的元素,則會產生移動元素的效果。 ```javascript $("p").prepend("strong") // <strong>Hello </strong><p>World</p> // 變為 // <p><strong>Hello </strong>World</p> ``` 上面代碼運行后,strong元素的位置將發生移動,而不是克隆一個新的strong元素。不過,如果當前結果集包含多個元素,則除了第一個以后,后面的p元素都將插入一個克隆的strong子元素。 prependTo方法將當前元素變為參數中的元素的第一個子元素。 ```javascript $("<p></p>").prependTo("div") // <div></div> // 變為 // <div><p></p></div> ``` **(3)after方法,insertAfter方法** after方法將參數中的元素插在當前元素后面。 ```javascript $("div").after("<p></p>") // <div></div> // 變為 // <div></div><p></p> ``` insertAfter方法將當前元素插在參數中的元素后面。 ```javascript $("<p></p>").insertAfter("div") ``` 上面代碼返回與前一個例子一樣的結果。 **(4)before方法,insertBefore方法** before方法將參數中的元素插在當前元素的前面。 ```javascript $("div").before("<p></p>") // <div></div> // 變為 // <p></p><div></div> ``` insertBefore方法將當前元素插在參數中的元素的前面。 ```javascript $("<p></p>").insertBefore("div") ``` 上面代碼返回與前一個例子一樣的結果。 **(5)wrap方法,wrapAll方法,unwrap方法,wrapInner方法** wrap方法將參數中的元素變成當前元素的父元素。 ```javascript $("p").wrap("<div></div>") // <p></p> // 變為 // <div><p></p></div> ``` wrap方法的參數還可以是一個函數。 ```javascript $("p").wrap(function() { return "<div></div>"; }) ``` 上面代碼返回與前一個例子一樣的結果。 wrapAll方法為結果集的所有元素,添加一個共同的父元素。 ```javascript $("p").wrapAll("<div></div>") // <p></p><p></p> // 變為 // <div><p></p><p></p></div> ``` unwrap方法移除當前元素的父元素。 ```javascript $("p").unwrap() // <div><p></p></div> // 變為 // <p></p> ``` wrapInner方法為當前元素的所有子元素,添加一個父元素。 ```javascript $("p").wrapInner('<strong></strong>') // <p>Hello</p> // 變為 // <p><strong>Hello</strong></p> ``` **(6)clone方法** clone方法克隆當前元素。 ```javascript var clones = $('li').clone(); ``` 對于那些有id屬性的節點,clone方法會連id屬性一起克隆。所以,要把克隆的節點插入文檔的時候,務必要修改或移除id屬性。 **(7)remove方法,detach方法,replaceWith方法** remove方法移除并返回一個元素,取消該元素上所有事件的綁定。detach方法也是移除并返回一個元素,但是不取消該元素上所有事件的綁定。 ```javascript $('p').remove() $('p').detach() ``` replaceWith方法用參數中的元素,替換并返回當前元素,取消當前元素的所有事件的綁定。 ```javascript $('p').replaceWith('<div></div>') ``` ### 動畫效果方法 jQuery提供一些方法,可以很容易地顯示網頁動畫效果。但是,總體上來說,它們不如CSS動畫強大和節省資源,所以應該優先考慮使用CSS動畫。 如果將jQuery.fx.off設為true,就可以將所有動畫效果關閉,使得網頁元素的各種變化一步到位,沒有中間過渡的動畫效果。 **(1)動畫效果的簡便方法** jQuery提供以下一些動畫效果方法。 - show:顯示當前元素。 - hide:隱藏當前元素。 - toggle:顯示或隱藏當前元素。 - fadeIn:將當前元素的不透明度(opacity)逐步提升到100%。 - fadeOut:將當前元素的不透明度逐步降為0%。 - fadeToggle:以逐漸透明或逐漸不透明的方式,折疊顯示當前元素。 - slideDown:以從上向下滑入的方式顯示當前元素。 - slideUp:以從下向上滑出的方式隱藏當前元素。 - slideToggle:以垂直滑入或滑出的方式,折疊顯示當前元素。 上面這些方法可以不帶參數調用,也可以接受毫秒或預定義的關鍵字作為參數。 ```javascript $('.hidden').show(); $('.hidden').show(300); $('.hidden').show('slow'); ``` 上面三行代碼分別表示,以默認速度、300毫秒、較慢的速度隱藏一個元素。 jQuery預定義的關鍵字是在`jQuery.fx.speeds`對象上面,可以自行改動這些值,或者創造新的值。 ```javascript jQuery.fx.speeds.fast = 50; jQuery.fx.speeds.slow = 3000; jQuery.fx.speeds.normal = 1000; ``` 上面三行代碼重新定義fast、normal、slow關鍵字對應的毫秒數。 你還可以定義自己的關鍵字。 ```javascript jQuery.fx.speeds.blazing = 30; // 調用 $('.hidden').show('blazing'); ``` 這些方法還可以接受一個函數,作為第二個參數,表示動畫結束后的回調函數。 ```javascript $('p').fadeOut(300, function() { $(this).remove(); }); ``` 上面代碼表示,`p`元素以300毫秒的速度淡出,然后調用回調函數,將其從DOM中移除。 使用按鈕控制某個元素折疊顯示的代碼如下。 ```javascript // Fade $('.btn').click(function () { $('.element').fadeToggle('slow'); }); // Toggle $('.btn').click(function () { $('.element').slideToggle('slow'); }); ``` **(2)animate方法** 上面這些動畫效果方法,實際上都是animate方法的簡便寫法。在幕后,jQuery都是統一使用animate方法生成各種動畫效果。 ```javascript $('a.top').click(function (e) { e.preventDefault(); $('html, body').animate({scrollTop: 0}, 800); }); ``` 上面代碼是點擊鏈接,回到頁面頭部的寫法。其中,`animate`方法接受兩個參數,第一個參數是一個對象,表示動畫結束時相關CSS屬性的值,第二個參數是動畫持續的毫秒數。需要注意的是,第一個參數對象的成員名稱,必須與CSS屬性名稱一致,如果CSS屬性名稱帶有連字號,則需要用“駱駝拼寫法”改寫。 animate方法還可以接受第三個參數,表示動畫結束時的回調函數。 ```javascript $('div').animate({ left: '+=50', // 增加50 opacity: 0.25, fontSize: '12px' }, 300, // 持續時間 function() { // 回調函數 console.log('done!'); } ); ``` 上面代碼表示,動畫結束時,在控制臺輸出“done!”。 **(3)stop方法,delay方法** stop方法表示立即停止執行當前的動畫。 ```javascript $("#stop").click(function() { $(".block").stop(); }); ``` 上面代碼表示,點擊按鈕后,block元素的動畫效果停止。 delay方法接受一個時間參數,表示暫停多少毫秒后繼續執行。 ```javascript $("#foo").slideUp(300).delay(800).fadeIn(400) ``` 上面代碼表示,slideUp動畫之后,暫停800毫秒,然后繼續執行fadeIn動畫。 ### 其他方法 jQuery還提供一些供特定元素使用的方法。 serialize方法用于將表單元素的值,轉為url使用的查詢字符串。 ```javascript $( "form" ).on( "submit", function( event ) { event.preventDefault(); console.log( $( this ).serialize() ); }); // single=Single&multiple=Multiple&check=check2&radio=radio1 ``` serializeArray方法用于將表單元素的值轉為數組。 ```javascript $("form").submit(function (event){ console.log($(this).serializeArray()); event.preventDefault(); }); // [ // {name : 'field1', value : 123}, // {name : 'field2', value : 'hello world'} // ] ``` ## 事件處理 ### 事件綁定的簡便方法 jQuery提供一系列方法,允許直接為常見事件綁定回調函數。比如,click方法可以為一個元素綁定click事件的回調函數。 ```javascript $('li').click(function (e){ console.log($(this).text()); }); ``` 上面代碼為li元素綁定click事件的回調函數,點擊后在控制臺顯示li元素包含的文本。 這樣綁定事件的簡便方法有如下一些: - click - keydown - keypress - keyup - mouseover - mouseout - mouseenter - mouseleave - scroll - focus - blur - resize - hover 如果不帶參數調用這些方法,就是觸發相應的事件,從而引發回調函數的運行。 ```javascript $('li').click() ``` 上面代碼將觸發click事件的回調函數。 需要注意的是,通過這種方法觸發回調函數,將不會引發瀏覽器對該事件的默認行為。比如,對a元素調用click方法,將只觸發事先綁定的回調函數,而不會導致瀏覽器將頁面導向href屬性指定的網址。 下面是一個捕捉用戶按下escape鍵的函數。 ```javascript $(document).keyup(function(e) { if (e.keyCode == 27) { $('body').toggleClass('show-nav'); // $('body').removeClass('show-nav'); } }); ``` 上面代碼中,用戶按下escape鍵,jQuery就會為body元素添加/去除名為show-nav的class。 `hover`方法需要特別說明。它接受兩個回調函數作為參數,分別代表`mouseenter`和`mouseleave`事件的回調函數。 ```javascript $(selector).hover(handlerIn, handlerOut) // 等同于 $(selector).mouseenter(handlerIn).mouseleave(handlerOut) ``` 下面是一個例子,當按鈕發生`hover`事件,添加一個class樣式,當`hover`事件結束時,再取消這個class。 ```javascript $('.btn').hover(function () { $(this).addClass('hover'); }, function () { $(this).removeClass('hover'); }); ``` 使用`toggleClass`可以簡化上面的代碼。 ```javascript $('.btn').hover(function () { $(this).toggleClass('hover'); }); ``` ### on方法,trigger方法,off方法 除了簡便方法,jQuery還提供事件處理的通用方法。 **(1)on方法** `on`方法是jQuery事件綁定的統一接口。事件綁定的那些簡便方法,其實都是`on`方法的簡寫形式。 `on`方法接受兩個參數,第一個是事件名稱,第二個是回調函數。 ```javascript $('li').on('click', function (e){ console.log($(this).text()); }); ``` 上面代碼為`li`元素綁定`click`事件的回調函數。 > 注意,在回調函數內部,`this`關鍵字指的是發生該事件的DOM對象。為了使用jQuery提供的方法,必須將DOM對象轉為jQuery對象,因此寫成`$(this)`。 `on`方法允許一次為多個事件指定同樣的回調函數。 ```javascript $('input[type="text"]').on('focus blur', function (){ console.log('focus or blur'); }); ``` 上面代碼為文本框的`focus`和`blur`事件綁定同一個回調函數。 下面是一個例子,當圖片加載失敗,使用`error`事件,替換另一張圖片。 ```javascript $('img').on('error', function () { if(!$(this).hasClass('broken-image')) { $(this).prop('src', 'img/broken.png').addClass('broken-image'); } }); ``` 下面是檢查用戶是否切換瀏覽器tab的例子。 ```javascript $(document).on('visibilitychange', function (e) { if (e.target.visibilityState === "visible") { console.log('Tab is now in view!'); } else if (e.target.visibilityState === "hidden") { console.log('Tab is now hidden!'); } }); ``` `on`方法還可以為當前元素的某一個子元素,添加回調函數。 ```javascript $('ul').on('click', 'li', function (e){ console.log(this); }); ``` 上面代碼為`ul`的子元素`li`綁定click事件的回調函數。采用這種寫法時,on方法接受三個參數,子元素選擇器作為第二個參數,夾在事件名稱和回調函數之間。 這種寫法有兩個好處。首先,click事件還是在ul元素上觸發回調函數,但是會檢查event對象的target屬性是否為li子元素,如果為true,再調用回調函數。這樣就比為li元素一一綁定回調函數,節省了內存空間。其次,這種綁定的回調函數,對于在綁定后生成的li元素依然有效。 on方法還允許向回調函數傳入數據。 ```javascript $("ul" ).on("click", {name: "張三"}, function (event){ console.log(event.data.name); }); ``` 上面代碼在發生click事件之后,會通過event對象的data屬性,在控制臺打印出所傳入的數據(即“張三”)。 **(2)trigger方法** trigger方法用于觸發回調函數,它的參數就是事件的名稱。 ```javascript $('li').trigger('click') ``` 上面代碼觸發li元素的click事件回調函數。與那些簡便方法一樣,trigger方法只觸發回調函數,而不會引發瀏覽器的默認行為。 **(3)off方法** off方法用于移除事件的回調函數。 ```javascript $('li').off('click') ``` 上面代碼移除li元素所有的click事件回調函數。 **(4)事件的名稱空間** 同一個事件有時綁定了多個回調函數,這時如果想移除其中的一個回調函數,可以采用“名稱空間”的方式,即為每一個回調函數指定一個二級事件名,然后再用off方法移除這個二級事件的回調函數。 ```javascript $('li').on('click.logging', function (){ console.log('click.logging callback removed'); }); $('li').off('click.logging'); ``` 上面代碼為li元素定義了二級事件click.logging的回調函數,click.logging屬于click名稱空間,當發生click事件時會觸發該回調函數。將click.logging作為off方法的參數,就會移除這個回調函數,但是對其他click事件的回調函數沒有影響。 trigger方法也適用帶名稱空間的事件。 ```javascript $('li').trigger('click.logging') ``` ### event對象 當回調函數被觸發后,它們的參數通常是一個事件對象event。 ```javascript $(document).on('click', function (e){ // ... }); ``` 上面代碼的回調函數的參數e,就代表事件對象event。 event對象有以下屬性: - type:事件類型,比如click。 - which:觸發該事件的鼠標按鈕或鍵盤的鍵。 - target:事件發生的初始對象。 - data:傳入事件對象的數據。 - pageX:事件發生時,鼠標位置的水平坐標(相對于頁面左上角)。 - pageY:事件發生時,鼠標位置的垂直坐標(相對于頁面左上角)。 event對象有以下方法: - preventDefault:取消瀏覽器默認行為。 - stopPropagation:阻止事件向上層元素傳播。 ### 一次性事件 one方法指定一次性的回調函數,即這個函數只能運行一次。這對提交表單很有用。 ```javascript $("#button").one( "click", function() { return false; } ); ``` one方法本質上是回調函數運行一次,即解除對事件的監聽。 ```javascript document.getElementById("#button").addEventListener("click", handler); function handler(e) { e.target.removeEventListener(e.type, arguments.callee); return false; } ``` 上面的代碼在點擊一次以后,取消了對click事件的監聽。如果有特殊需要,可以設定點擊2次或3次之后取消監聽,這都是可以的。 <h2 id="11.2">jQuery工具方法</h2> jQuery函數庫提供了一個jQuery對象(簡寫為$),這個對象本身是一個構造函數,可以用來生成jQuery對象的實例。有了實例以后,就可以調用許多針對實例的方法,它們定義jQuery.prototype對象上面(簡寫為$.fn)。 除了實例對象的方法以外,jQuery對象本身還提供一些方法(即直接定義jQuery對象上面),不需要生成實例就能使用。由于這些方法類似“通用工具”的性質,所以我們把它們稱為“工具方法”(utilities)。 ## 常用工具方法 **(1)$.trim** $.trim方法用于移除字符串頭部和尾部多余的空格。 ```javascript $.trim(' Hello ') // Hello ``` **(2)$.contains** $.contains方法返回一個布爾值,表示某個DOM元素(第二個參數)是否為另一個DOM元素(第一個參數)的下級元素。 ```javascript $.contains(document.documentElement, document.body); // true $.contains(document.body, document.documentElement); // false ``` **(3)$.each,$.map** $.each方法用于遍歷數組和對象,然后返回原始對象。它接受兩個參數,分別是數據集合和回調函數。 ```javascript $.each([ 52, 97 ], function( index, value ) { console.log( index + ": " + value ); }); // 0: 52 // 1: 97 var obj = { p1: "hello", p2: "world" }; $.each( obj, function( key, value ) { console.log( key + ": " + value ); }); // p1: hello // p2: world ``` 需要注意的,jQuery對象實例也有一個each方法($.fn.each),兩者的作用差不多。 $.map方法也是用來遍歷數組和對象,但是會返回一個新對象。 ```javascript var a = ["a", "b", "c", "d", "e"]; a = $.map(a, function (n, i){ return (n.toUpperCase() + i); }); // ["A0", "B1", "C2", "D3", "E4"] ``` **(4)$.inArray** $.inArray方法返回一個值在數組中的位置(從0開始)。如果該值不在數組中,則返回-1。 ```javascript var a = [1,2,3,4]; $.inArray(4,a) // 3 ``` **(5)$.extend** $.extend方法用于將多個對象合并進第一個對象。 ```javascript var o1 = {p1:'a',p2:'b'}; var o2 = {p1:'c'}; $.extend(o1,o2); o1.p1 // "c" ``` $.extend的另一種用法是生成一個新對象,用來繼承原有對象。這時,它的第一個參數應該是一個空對象。 ```javascript var o1 = {p1:'a',p2:'b'}; var o2 = {p1:'c'}; var o = $.extend({},o1,o2); o // Object {p1: "c", p2: "b"} ``` 默認情況下,extend方法生成的對象是“淺拷貝”,也就是說,如果某個屬性是對象或數組,那么只會生成指向這個對象或數組的指針,而不會復制值。如果想要“深拷貝”,可以在extend方法的第一個參數傳入布爾值true。 ```javascript var o1 = {p1:['a','b']}; var o2 = $.extend({},o1); var o3 = $.extend(true,{},o1); o1.p1[0]='c'; o2.p1 // ["c", "b"] o3.p1 // ["a", "b"] ``` 上面代碼中,o2是淺拷貝,o3是深拷貝。結果,改變原始數組的屬性,o2會跟著一起變,而o3不會。 **(6)$.proxy** $.proxy方法類似于ECMAScript 5的bind方法,可以綁定函數的上下文(也就是this對象)和參數,返回一個新函數。 jQuery.proxy()的主要用處是為回調函數綁定上下文對象。 ```javascript var o = { type: "object", test: function(event) { console.log(this.type); } }; $("#button") .on("click", o.test) // 無輸出 .on("click", $.proxy(o.test, o)) // object ``` 上面的代碼中,第一個回調函數沒有綁定上下文,所以結果為空,沒有任何輸出;第二個回調函數將上下文綁定為對象o,結果就為object。 這個例子的另一種等價的寫法是: ```javascript $("#button").on( "click", $.proxy(o, test)) ``` 上面代碼的$.proxy(o, test)的意思是,將o的方法test與o綁定。 這個例子表明,proxy方法的寫法主要有兩種。 ```javascript jQuery.proxy(function, context) // or jQuery.proxy(context, name) ``` 第一種寫法是為函數(function)指定上下文對象(context),第二種寫法是指定上下文對象(context)和它的某個方法名(name)。 再看一個例子。正常情況下,下面代碼中的this對象指向發生click事件的DOM對象。 ```javascript $('#myElement').click(function() { $(this).addClass('aNewClass'); }); ``` 如果我們想讓回調函數延遲運行,使用setTimeout方法,代碼就會出錯,因為setTimeout使得回調函數在全局環境運行,this將指向全局對象。 ```javascript $('#myElement').click(function() { setTimeout(function() { $(this).addClass('aNewClass'); }, 1000); }); ``` 上面代碼中的this,將指向全局對象window,導致出錯。 這時,就可以用proxy方法,將this對象綁定到myElement對象。 ```javascript $('#myElement').click(function() { setTimeout($.proxy(function() { $(this).addClass('aNewClass'); }, this), 1000); }); ``` **(7)$.data,$.removeData** $.data方法可以用來在DOM節點上儲存數據。 ```javascript // 存入數據 $.data(document.body, "foo", 52 ); // 讀取數據 $.data(document.body, "foo"); // 讀取所有數據 $.data(document.body); ``` 上面代碼在網頁元素body上儲存了一個鍵值對,鍵名為“foo”,鍵值為52。 $.removeData方法用于移除$.data方法所儲存的數據。 ```javascript $.data(div, "test1", "VALUE-1"); $.removeData(div, "test1"); ``` **(8)$.parseHTML,$.parseJSON,$.parseXML** $.parseHTML方法用于將字符串解析為DOM對象。 $.parseJSON方法用于將JSON字符串解析為JavaScript對象,作用與原生的JSON.parse()類似。但是,jQuery沒有提供類似JSON.stringify()的方法,即不提供將JavaScript對象轉為JSON對象的方法。 $.parseXML方法用于將字符串解析為XML對象。 ```javascript var html = $.parseHTML("hello, <b>my name is</b> jQuery."); var obj = $.parseJSON('{"name": "John"}'); var xml = "<rss version='2.0'><channel><title>RSS Title</title></channel></rss>"; var xmlDoc = $.parseXML(xml); ``` **(9)$.makeArray** $.makeArray方法將一個類似數組的對象,轉化為真正的數組。 ```javascript var a = $.makeArray(document.getElementsByTagName("div")); ``` **(10)$.merge** $.merge方法用于將一個數組(第二個參數)合并到另一個數組(第一個參數)之中。 ```javascript var a1 = [0,1,2]; var a2 = [2,3,4]; $.merge(a1, a2); a1 // [0, 1, 2, 2, 3, 4] ``` **(11)$.now** $.now方法返回當前時間距離1970年1月1日00:00:00 UTC對應的毫秒數,等同于(new Date).getTime()。 ```javascript $.now() // 1388212221489 ``` ## 判斷數據類型的方法 jQuery提供一系列工具方法,用來判斷數據類型,以彌補JavaScript原生的typeof運算符的不足。以下方法對參數進行判斷,返回一個布爾值。 - jQuery.isArray():是否為數組。 - jQuery.isEmptyObject():是否為空對象(不含可枚舉的屬性)。 - jQuery.isFunction():是否為函數。 - jQuery.isNumeric():是否為數值(整數或浮點數)。 - jQuery.isPlainObject():是否為使用“{}”或“new Object”生成的對象,而不是瀏覽器原生提供的對象。 - jQuery.isWindow():是否為window對象。 - jQuery.isXMLDoc():判斷一個DOM節點是否處于XML文檔之中。 下面是一些例子。 ```javascript $.isEmptyObject({}) // true $.isPlainObject(document.location) // false $.isWindow(window) // true $.isXMLDoc(document.body) // false ``` 除了上面這些方法以外,還有一個$.type方法,可以返回一個變量的數據類型。它的實質是用Object.prototype.toString方法讀取對象內部的[[Class]]屬性(參見《標準庫》的Object對象一節)。 ```javascript $.type(/test/) // "regexp" ``` ## Ajax操作 ### $.ajax jQuery對象上面還定義了Ajax方法($.ajax()),用來處理Ajax操作。調用該方法后,瀏覽器就會向服務器發出一個HTTP請求。 $.ajax()的用法主要有兩種。 ```javascript $.ajax(url[, options]) $.ajax([options]) ``` 上面代碼中的url,指的是服務器網址,options則是一個對象參數,設置Ajax請求的具體參數。 ```javascript $.ajax({ async: true, url: '/url/to/json', type: 'GET', data : { id : 123 }, dataType: 'json', timeout: 30000, success: successCallback, error: errorCallback, complete: completeCallback, statusCode: { 404: handler404, 500: handler500 } }) function successCallback(json) { $('<h1/>').text(json.title).appendTo('body'); } function errorCallback(xhr, status){ console.log('出問題了!'); } function completeCallback(xhr, status){ console.log('Ajax請求已結束。'); } ``` 上面代碼的對象參數有多個屬性,含義如下: - accepts:將本機所能處理的數據類型,告訴服務器。 - async:該項默認為true,如果設為false,則表示發出的是同步請求。 - beforeSend:指定發出請求前,所要調用的函數,通常用來對發出的數據進行修改。 - cache:該項默認為true,如果設為false,則瀏覽器不緩存返回服務器返回的數據。注意,瀏覽器本身就不會緩存POST請求返回的數據,所以即使設為false,也只對HEAD和GET請求有效。 - complete:指定當HTTP請求結束時(請求成功或請求失敗的回調函數,此時已經運行完畢)的回調函數。不管請求成功或失敗,該回調函數都會執行。它的參數為發出請求的原始對象以及返回的狀態信息。 - contentType:發送到服務器的數據類型。 - context:指定一個對象,作為所有Ajax相關的回調函數的this對象。 - crossDomain:該屬性設為true,將強制向相同域名發送一個跨域請求(比如JSONP)。 - data:向服務器發送的數據,如果使用GET方法,此項將轉為查詢字符串,附在網址的最后。 - dataType:向服務器請求的數據類型,可以設為text、html、script、json、jsonp和xml。 - error:請求失敗時的回調函數,函數參數為發出請求的原始對象以及返回的狀態信息。 - headers:指定HTTP請求的頭信息。 - ifModified:如果該屬性設為true,則只有當服務器端的內容與上次請求不一樣時,才會發出本次請求。 - jsonp:指定JSONP請求“callback=?”中的callback的名稱。 - jsonpCallback: 指定JSONP請求中回調函數的名稱。 - mimeType:指定HTTP請求的mime type。 - password:指定HTTP認證所需要的密碼。 - statusCode:值為一個對象,為服務器返回的狀態碼,指定特別的回調函數。 - success:請求成功時的回調函數,函數參數為服務器傳回的數據、狀態信息、發出請求的原始對象。 - timeout: 等待的最長毫秒數。如果過了這個時間,請求還沒有返回,則自動將請求狀態改為失敗。 - type:向服務器發送信息所使用的HTTP動詞,默認為GET,其他動詞有POST、PUT、DELETE。 - url:服務器端網址。這是唯一必需的一個屬性,其他屬性都可以省略。 - username:指定HTTP認證的用戶名。 - xhr:指定生成XMLHttpRequest對象時的回調函數。 這些參數之中,url可以獨立出來,作為ajax方法的第一個參數。也就是說,上面代碼還可以寫成下面這樣。 ```javascript $.ajax('/url/to/json',{ type: 'GET', dataType: 'json', success: successCallback, error: errorCallback }) ``` 作為向服務器發送的數據,data屬性也可以寫成一個對象。 ```javascript $.ajax({ url: '/remote/url', data: { param1: 'value1', param2: 'value2', ... } }); // 相當于 $.ajax({ url: '/remote/url?param1=value1&param2=value2...' }}); ``` ### 簡便寫法 ajax方法還有一些簡便寫法。 - $.get():發出GET請求。 - $.getScript():讀取一個JavaScript腳本文件并執行。 - $.getJSON():發出GET請求,讀取一個JSON文件。 - $.post():發出POST請求。 - $.fn.load():讀取一個html文件,并將其放入當前元素之中。 一般來說,這些簡便方法依次接受三個參數:url、數據、成功時的回調函數。 **(1)$.get(),$.post()** 這兩個方法分別對應HTTP的GET方法和POST方法。 ```javascript $.get('/data/people.html', function(html){ $('#target').html(html); }); $.post('/data/save', {name: 'Rebecca'}, function (resp){ console.log(JSON.parse(resp)); }); ``` get方法和post方法的參數相同,第一個參數是服務器網址,該參數是必需的,其他參數都是可選的。第二個參數是發送給服務器的數據,第三個參數是操作成功后的回調函數。 上面的post方法對應的ajax寫法如下。 ```javascript $.ajax({ type: 'POST', url: '/data/save', data: {name: 'Rebecca'}, dataType: 'json', success: function (resp){ console.log(JSON.parse(resp)); } }); ``` **(2)$.getJSON()** ajax方法的另一個簡便寫法是getJSON方法。當服務器端返回JSON格式的數據,可以用這個方法代替$.ajax方法。 ```javascript $.getJSON('url/to/json', {'a': 1}, function(data){ console.log(data); }); ``` 上面的代碼等同于下面的寫法。 ```javascript $.ajax({ dataType: "json", url: '/url/to/data', data: {'a': 1}, success: function(data){ console.log(data); } }); ``` **(3)$.getScript()** $.getScript方法用于從服務器端加載一個腳本文件。 ```javascript $.getScript('/static/js/myScript.js', function() { functionFromMyScript(); }); ``` 上面代碼先從服務器加載myScript.js腳本,然后在回調函數中執行該腳本提供的函數。 getScript的回調函數接受三個參數,分別是腳本文件的內容,HTTP響應的狀態信息和ajax對象實例。 ```javascript $.getScript( "ajax/test.js", function (data, textStatus, jqxhr){ console.log( data ); // test.js的內容 console.log( textStatus ); // Success console.log( jqxhr.status ); // 200 }); ``` getScript是ajax方法的簡便寫法,因此返回的是一個deferred對象,可以使用deferred接口。 ```javascript jQuery.getScript("/path/to/myscript.js") .done(function() { // ... }) .fail(function() { // ... }); ``` **(4)$.fn.load()** $.fn.load不是jQuery的工具方法,而是定義在jQuery對象實例上的方法,用于獲取服務器端的HTML文件,將其放入當前元素。由于該方法也屬于ajax操作,所以放在這里一起講。 ```javascript $('#newContent').load('/foo.html'); ``` $.fn.load方法還可以指定一個選擇器,將遠程文件中匹配選擇器的部分,放入當前元素,并指定操作完成時的回調函數。 ```javascript $('#newContent').load('/foo.html #myDiv h1:first', function(html) { console.log('內容更新!'); }); ``` 上面代碼只加載foo.html中匹配“#myDiv h1:first”的部分,加載完成后會運行指定的回調函數。 ```javascript $('#main-menu a').click(function(event) { event.preventDefault(); $('#main').load(this.href + ' #main *'); }); ``` 上面的代碼將指定網頁中匹配“#main *”,加載入當前的main元素。星號表示匹配main元素包含的所有子元素,如果不加這個星號,就會加載整個main元素(包括其本身),導致一個main元素中還有另一個main元素。 load方法可以附加一個字符串或對象作為參數,一起向服務器提交。如果是字符串,則采用GET方法提交;如果是對象,則采用POST方法提交。 ```javascript $( "#feeds" ).load( "feeds.php", { limit: 25 }, function() { console.log( "已經載入" ); }); ``` 上面代碼將`{ limit: 25 }`通過POST方法向服務器提交。 load方法的回調函數,可以用來向用戶提示操作已經完成。 ```javascript $('#main-menu a').click(function(event) { event.preventDefault(); $('#main').load(this.href + ' #main *', function(responseText, status) { if (status === 'success') { $('#notification-bar').text('加載成功!'); } else { $('#notification-bar').text('出錯了!'); } }); }); ``` ### Ajax事件 jQuery提供以下一些方法,用于指定特定的AJAX事件的回調函數。 - .ajaxComplete():ajax請求完成。 - .ajaxError():ajax請求出錯。 - .ajaxSend():ajax請求發出之前。 - .ajaxStart():第一個ajax請求開始發出,即沒有還未完成ajax請求。 - .ajaxStop():所有ajax請求完成之后。 - .ajaxSuccess():ajax請求成功之后。 下面是示例。 ```javascript $('#loading_indicator') .ajaxStart(function (){$(this).show();}) .ajaxStop(function (){$(this).hide();}); ``` 下面是處理Ajax請求出錯(返回404或500錯誤)的例子。 ```javascript $(document).ajaxError(function (e, xhr, settings, error) { console.log(error); }); ``` ### 返回值 ajax方法返回的是一個deferred對象,可以用then方法為該對象指定回調函數(詳細解釋參見《deferred對象》一節)。 ```javascript $.ajax({ url: '/data/people.json', dataType: 'json' }).then(function (resp){ console.log(resp.people); }) ``` ### JSONP 由于瀏覽器存在“同域限制”,ajax方法只能向當前網頁所在的域名發出HTTP請求。但是,通過在當前網頁中插入script元素(\<script\>),可以向不同的域名發出GET請求,這種變通方法叫做JSONP(JSON with Padding)。 ajax方法可以發出JSONP請求,方法是在對象參數中指定dataType為JSONP。 ```javascript $.ajax({ url: '/data/search.jsonp', data: {q: 'a'}, dataType: 'jsonp', success: function(resp) { $('#target').html('Results: ' + resp.results.length); } });) ``` JSONP的通常做法是,在所要請求的URL后面加在回調函數的名稱。ajax方法規定,如果所請求的網址以類似“callback=?”的形式結尾,則自動采用JSONP形式。所以,上面的代碼還可以寫成下面這樣。 ```javascript $.getJSON('/data/search.jsonp?q=a&callback=?', function(resp) { $('#target').html('Results: ' + resp.results.length); } ); ``` ### 文件上傳 假定網頁有一個文件控件。 ```html <input type="file" id="test-input"> ``` 下面就是如何使用Ajax上傳文件。 ```javascript var file = $('#test-input')[0].files[0]; var formData = new FormData(); formData.append('file', file); $.ajax('myserver/uploads', { method: 'POST', contentType: false, processData: false, data: formData }); ``` 上面代碼是將文件作為表單數據發送。除此之外,也可以直接發送文件。 ```javascript var file = $('#test-input')[0].files[0]; $.ajax('myserver/uploads', { method: 'POST', contentType: file.type, processData: false, data: file }); ``` <h2 id="11.3">jQuery插件開發</h2> 所謂“插件”,就是用戶自己新增的jQuery實例對象的方法。由于該方法要被所有實例共享,所以只能定義在jQuery構造函數的原型對象(prototype)之上。對于用戶來說,把一些常用的操作封裝成插件(plugin),使用起來會非常方便。 ## 插件的編寫 ### 原理 本質上,jQuery插件是定義在jQuery構造函數的prototype對象上面的一個方法,這樣做就能使得所有jQuery對象的實例都能共享這個方法。因為jQuery構造函數的prototype對象被簡寫成jQuery.fn對象,所以插件采用下面的方法定義。 ```javascript jQuery.fn.myPlugin = function() { // Do your awesome plugin stuff here }; ``` 更好的做法是采用下面的寫法,這樣就能在函數體內自由使用美元符號($)。 ```javascript ;(function ($){ $.fn.myPlugin = function (){ // Do your awesome plugin stuff here }; })(jQuery); ``` 上面代碼的最前面有一個分號,這是為了防止多個腳本文件合并時,其他腳本的結尾語句沒有添加分號,造成運行時錯誤。 有時,還可以把頂層對象(window)作為參數輸入,這樣可以加快代碼的執行速度和執行更有效的最小化操作。 ```javascript ;(function ($, window) { $.fn.myPlugin = function() { // Do your awesome plugin stuff here }; }(jQuery, window)); ``` 需要注意的是,在插件內部,this關鍵字指的是jQuery對象的實例。而在一般的jQuery回調函數之中,this關鍵字指的是DOM對象。 ```javascript (function ($){ $.fn.maxHeight = function (){ var max = 0; // 下面這個this,指的是jQuery對象實例 this.each(function() { // 回調函數內部,this指的是DOM對象 max = Math.max(max, $(this).height()); }); return max; }; })(jQuery); ``` 上面這個maxHeight插件的作用是,返回一系列DOM對象中高度最高的那個對象的高度。 大多數情況下,插件應該返回jQuery對象,這樣可以保持鏈式操作。 ```javascript (function ($){ $.fn.greenify = function (){ this.css("color", "green"); return this; }; })(jQuery); $("a").greenify().addClass("greenified"); ``` 上面代碼返回this對象,即jQuery對象實例,所以接下來可以采用鏈式操作。 對于包含多個jQuery對象的結果集,可以采用each方法,進行處理。 ```javascript $.fn.myNewPlugin = function() { return this.each(function() { // 處理每個對象 }); }; ``` 插件可以接受一個屬性對象參數。 ```javascript (function ($){ $.fn.tooltip = function (options){ var settings = $.extend( { 'location' : 'top', 'background-color' : 'blue' }, options); return this.each(function (){ // 填入插件代碼 }); }; })(jQuery); ``` 上面代碼使用extend方法,為參數對象設置屬性的默認值。 ### 偵測環境 jQuery逐漸從瀏覽器環境,變為也可以用于服務器環境。所以,定義插件的時候,最好首先偵測一下運行環境。 ```javascript if (typeof module === "object" && typeof module.exports === "object") { // CommonJS版本 } else { // 瀏覽器版本 } ``` ## 實例 下面是一個將a元素的href屬性添加到網頁的插件。 ```javascript (function($){ $.fn.showLinkLocation = function() { return this.filter('a').append(function(){ return ' (' + this.href + ')'; }); }; }(jQuery)); // 用法 $('a').showLinkLocation(); ``` 從上面的代碼可以看到,插件的開發和使用都非常簡單。 ## 插件的發布 編寫插件以后,可以將它發布到[jQuery官方網站](http://plugins.jquery.com/)上。 首先,編寫一個插件的信息文件yourPluginName.jquery.json。文件名中的yourPluginName表示你的插件名。 ```javascript { "name": "plugin_name", "title": "plugin_long_title", "description": "...", "keywords": ["jquery", "plugins"], "version": "0.0.1", "author": { "name": "...", "url": "..." }, "maintainers": [ { "name": "...", "url": "..." } ], "licenses": [ { "type": "MIT", "url": "http://www.opensource.org/licenses/mit-license.php" } ], "bugs": "...", // bugs url "homepage": "...", // homepage url "docs": "...", // docs url "download": "...", // download url "dependencies": { "jquery": ">=1.4" } } ``` 上面是一個插件信息文件的實例。 然后,將代碼文件發布到Github,在設置頁面點擊“Service Hooks/WebHook URLs”選項,填入網址http://plugins.jquery.com/postreceive-hook,再點擊“Update Settings”進行保存。 最后,為代碼加上版本,push到github,你的插件就會加入jQuery官方插件庫。 ```javascript git tag 0.1.0 git push origin --tags ``` 以后,你要發布新版本,就做一個新的tag。 <h2 id="11.4">jQuery.Deferred對象</h2> ## 概述 deferred對象代表了將要完成的某種操作,并提供了一些方法,幫助用戶使用。它是jQuery對Promises接口的實現。jQuery的所有Ajax操作函數,默認返回的就是一個deferred對象。 簡單說,Promises是異步操作的通用接口,扮演代理人(proxy)的角色,將異步操作包裝成具有同步操作特性的特殊對象。異步操作的典型例子就是Ajax操作、網頁動畫、web worker等等。 由于JavaScript單線程的特點,如果某個操作耗時很長,其他操作就必需排隊等待。為了避免整個程序失去響應,通常的解決方法是將那些排在后面的操作,寫成“回調函數”(callback)的形式。這樣做雖然可以解決問題,但是有一些顯著缺點: - 回調函數往往寫成函數參數的形式,形成所謂的“持續傳遞風格”(即參數就是下一步操作,Continuation-passing style),導致函數的輸入和輸出非常混亂,整個程序的可閱讀性差; - 回調函數往往只能指定一個,如果有多個操作,就需要改寫回調函數。 - 除了正常的報錯機制,錯誤還可能通過回調函數的形式返回,增加了除錯和調試的難度。 - 正常的函數輸入和輸出可以區分得很清楚,回調函數使得函數的輸出不再重要。 Promises就是為了解決這些問題而提出的,它的主要目的就是取代回調函數,成為非同步操作的解決方案。它的核心思想就是讓非同步操作返回一個對象,其他操作都針對這個對象來完成。比如,假定ajax操作返回一個Promise對象。 ```javascript var promise = get('http://www.example.com'); ``` 然后,Promise對象有一個then方法,可以用來指定回調函數。一旦非同步操作完成,就調用指定的回調函數。 ```javascript promise.then(function (content) { console.log(content) }) ``` 可以將上面兩段代碼合并起來,這樣程序的流程看得更清楚。 ```javascript get('http://www.example.com').then(function (content) { console.log(content) }) ``` 在1.7版之前,jQuery的Ajax操作采用回調函數。 ```javascript $.ajax({ url:"/echo/json/", success: function(response) { console.info(response.name); } }); ``` 1.7版之后,Ajax操作直接返回Promise對象,這意味著可以用then方法指定回調函數。 ```javascript $.ajax({ url: "/echo/json/", }).then(function (response) { console.info(response.name); }); ``` ## deferred對象的方法 ### 基本用法 **(1)生成deferred對象** 第一步是通過$.Deferred()方法,生成一個deferred對象。 ```javascript var deferred = $.Deferred(); ``` **(2)deferred對象的狀態** deferred對象有三種狀態。 - pending:表示操作還沒有完成。 - resolved:表示操作成功。 - rejected:表示操作失敗。 state方法用來返回deferred對象當前狀態。 ```javascript $.Deferred().state() // 'pending' $.Deferred().resolve().state() // 'resolved' $.Deferred().reject().state() // 'rejected' ``` **(3)改變狀態的方法** resolve方法將deferred對象的狀態從pending改為resolved,reject方法則將狀態從pending改為rejected。 ```javascript var deferred = $.Deferred(); deferred.resolve("hello world"); ``` resolve方法的參數,用來傳遞給回調函數。 **(4)綁定回調函數** deferred對象在狀態改變時,會觸發回調函數。 done方法指定狀態變為resolved(操作成功)時的回調函數;fail方法指定狀態變為rejected(操作失敗)時的回調函數;always方法指定,不管狀態變為resolved或rejected,都會觸發的方法。 ```javascript var deferred = $.Deferred(); deferred.done(function(value) { console.log(value); }).resolve('hello world'); // hello world ``` 上述三種方法都返回的原有的deferred對象,因此可以采用鏈式寫法,在后面再鏈接別的方法(包括done和fail在內)。 ```javascript $.Deferred().done(f1).fail(f2).always(f3); ``` ### notify() 和 progress() progress()用來指定一個回調函數,當調用notify()方法時,該回調函數將執行。它的用意是提供一個接口,使得在非同步操作執行過程中,可以執行某些操作,比如定期返回進度條的進度。 ```javascript var userProgress = $.Deferred(); var $profileFields = $("input"); var totalFields = $profileFields.length userProgress.progress(function (filledFields) { var pctComplete = (filledFields/totalFields)*100; $("#progress").html(pctComplete.toFixed(0)); }); userProgress.done(function () { $("#thanks").html("Thanks for completing your profile!").show(); }); $("input").on("change", function () { var filledFields = $profileFields.filter("[value!='']").length; userProgress.notify(filledFields); if (filledFields == totalFields) { userProgress.resolve(); } }); ``` ### then方法 **(1)概述** then方法的作用也是指定回調函數,它可以接受三個參數,也就是三個回調函數。第一個參數是resolve時調用的回調函數(相當于done方法),第二個參數是reject時調用的回調函數(相當于fail方法),第三個參數是progress()方法調用的回調函數。 ```javascript deferred.then( doneFilter [, failFilter ] [, progressFilter ] ) ``` **(2)返回值** 在jQuery 1.8之前,then()只是.done().fail()寫法的語法糖,兩種寫法是等價的。在jQuery 1.8之后,then()返回一個新的promise對象,而done()返回的是原有的deferred對象。如果then()指定的回調函數有返回值,該返回值會作為參數,傳入后面的回調函數。 ```javascript var defer = jQuery.Deferred(); defer.done(function(a,b){ return a * b; }).done(function( result ) { console.log("result = " + result); }).then(function( a, b ) { return a * b; }).done(function( result ) { console.log("result = " + result); }).then(function( a, b ) { return a * b; }).done(function( result ) { console.log("result = " + result); }); defer.resolve( 2, 3 ); ``` 在jQuery 1.8版本之前,上面代碼的結果是: ```javascript result = 2 result = 2 result = 2 ``` 在jQuery 1.8版本之后,返回結果是 ```javascript result = 2 result = 6 result = NaN ``` 這一點需要特別引起注意。 ```javascript $.ajax( url1, { dataType: "json" } ) .then(function( data ) { return $.ajax( url2, { data: { user: data.userId } } ); }).done(function( data ) { // 從url2獲取的數據 }); ``` 上面代碼最后那個done方法,處理的是從url2獲取的數據,而不是從url1獲取的數據。 **(3)對返回值的修改** 利用then()會修改返回值這個特性,我們可以在調用其他回調函數之前,對前一步操作返回的值進行處理。 ```javascript var post = $.post("/echo/json/") .then(function(p){ return p.firstName; }); post.done(function(r){ console.log(r); }); ``` 上面代碼先使用then()方法,從返回的數據中取出所需要的字段(firstName),所以后面的操作就可以只處理這個字段了。 有時,Ajax操作返回json字符串里面有一個error屬性,表示發生錯誤。這個時候,傳統的方法只能是通過done()來判斷是否發生錯誤。通過then()方法,可以讓deferred對象調用fail()方法。 ```javascript var myDeferred = $.post('/echo/json/', {json:JSON.stringify({'error':true})}) .then(function (response) { if (response.error) { return $.Deferred().reject(response); } return response; },function () { return $.Deferred().reject({error:true}); } ); myDeferred.done(function (response) { $("#status").html("Success!"); }).fail(function (response) { $("#status").html("An error occurred"); }); ``` 上面代碼中,不管是通信出錯,或者服務器返回一個錯誤,都會調用reject方法,返回一個新的deferred對象,狀態為rejected,因此就會觸發fail方法指定的回調函數。 關于error的處理,jQuery的deferred對象與其他實現Promises規范的函數庫有一個重大不同。就是說,如果deferred對象執行過程中,拋出一個非Promises對象的錯誤,那么將不會被后繼的then方法指定的rejected回調函數捕獲,而會一直傳播到應用程序層面。為了代碼行為與Promises規范保持一致,建議出錯時,總是使用reject方法返回錯誤。 ```javascript d = $.Deferred() d.then(function(){ throw new Error('err') }).fail(function(){ console.log('fail') }) d.resolve() // Error: err ``` 上面代碼中,then的回調函數拋出一個錯誤,按照Promises規范,應該被fail方法的回調函數捕獲,但是jQuery的部署是上升到應用程序的層面。 **(4)回調函數的返回值** 如果回調函數返回deferred對象,則then方法的返回值將是對應這個返回值的promise對象。 ```javascript var d1 = $.Deferred(); var promise = $.when('Hello').then(function(h){ return $.when(h,d1); }) promise.done(function (s1,s2) { console.log(s1); console.log(s2); }) d1.resolve('World') // Hello // World ``` 上面代碼中,done方法的回調函數,正常情況下只能接受一個參數。但是由于then方法的回調函數,返回一個when方法生成的deferred對象,導致它可以接受兩個參數。 ### pipe方法 pipe方法接受一個函數作為參數,表示在調用then方法、done方法、fail方法、always方法指定的回調函數之前,先運行pipe方法指定的回調函數。它通常用來對服務器返回的數據做初步處理。 ### 與Promise A+規格的差異 Promise事實上的標準是社區提出的Promise A+規格,jQuery的實現并不完全符合Promise A+,主要是對錯誤的處理。 ```javascript var promise2 = promise1.then(function () { throw new Error("boom!"); }); ``` 上面代碼在回調函數中拋出一個錯誤,Promise A+規定此時Promise實例的狀態變為reject,該錯誤被下一個catch方法指定的回調函數捕獲。但是,jQuery的Deferred對象此時不會改變狀態,亦不會觸發回調函數,該錯誤一般情況下會被window.onerror捕獲。換句話說,在Deferred對象中,總是必須使用reject方法來改變狀態。 ## promise對象 **(1)概念** 一般情況下,從外部改變第三方完成的異步操作(比如Ajax)的狀態是毫無意義的。為了防止用戶這樣做,可以在deferred對象的基礎上,返回一個針對它的promise對象。 簡單說,promise對象就是不能改變狀態的deferred對象,也就是deferred的只讀版。或者更通俗地理解成,promise是一個對將要完成的任務的承諾,排除了其他人破壞這個承諾的可能性,只能等待承諾方給出結果。 你可以通過promise對象,為原始的deferred對象添加回調函數,查詢它的狀態,但是無法改變它的狀態,也就是說promise對象不允許你調用resolve和reject方法。 **(2)生成promise對象** deferred對象的promise方法,用來生成對應的promise對象。 ```javascript function getPromise(){ return $.Deferred().promise(); } try{ getPromise().resolve("a"); } catch(err) { console.log(err); } // TypeError ``` 上面代碼對promise對象,調用resolve方法,結果報錯。 jQuery的`ajax()`方法返回的就是一個Promise對象。此外,Animation類操作也可以使用`promise`方法。 ```javascript $('body').toggle('blinds').promise().then( function(){ $('body').toggle('blinds') } ) ``` ## 輔助方法 deferred對象還有一系列輔助方法,使它更方便使用。 ### `$.when()`方法 `$.when()`接受多個deferred對象作為參數,當它們全部運行成功后,才調用resolved狀態的回調函數,但只要其中有一個失敗,就調用rejected狀態的回調函數。它相當于將多個非同步操作,合并成一個。實質上,when方法為多個deferred對象,返回一個單一的promise對象。 ```javascript $.when( $.ajax( "/main.php" ), $.ajax( "/modules.php" ), $.ajax( "/lists.php" ) ).then(successFunc, failureFunc); ``` 上面代碼表示,要等到三個ajax操作都結束以后,才執行then方法指定的回調函數。 when方法里面要執行多少個操作,回調函數就有多少個參數,對應前面每一個操作的返回結果。 ```javascript $.when( $.ajax( "/main.php" ), $.ajax( "/modules.php" ), $.ajax( "/lists.php" ) ).then(function (resp1, resp2, resp3){ console.log(resp1); console.log(resp2); console.log(resp3); }); ``` 上面代碼的回調函數有三個參數,resp1、resp2和resp3,依次對應前面三個ajax操作的返回結果。 如果when方法的參數不是deferred或promise對象,則直接作為回調函數的參數。 ```javascript d = $.Deferred() $.when(d, 'World').done(function (s1, s2){ console.log(s1); console.log(s2); }) d.resolve('Hello') // Hello // World ``` 上面代碼中,when的第二個參數是一個字符串,則直接作為回調函數的第二個參數。 此外,如果when方法的參數都不是deferred或promise對象,那么when方法的回調函數將立即運行。 ## 使用實例 ### wait方法 我們可以用deferred對象寫一個wait方法,表示等待多少毫秒后再執行。 ```javascript $.wait = function(time) { return $.Deferred(function(dfd) { setTimeout(dfd.resolve, time); }); } ``` 使用方法如下。 ```javascript $.wait(5000).then(function() { console.log("Hello from the future!"); }); ``` ### 改寫setTimeout 在上面的wait方法的基礎上,還可以改寫setTimeout方法,讓其返回一個deferred對象。 ```javascript function doSomethingLater(fn, time) { var dfd = $.Deferred(); setTimeout(function() { dfd.resolve(fn()); }, time || 0); return dfd.promise(); } var promise = doSomethingLater(function (){ console.log( '已經延遲執行' ); }, 100); ``` ### 自定義操作使用deferred接口 我們可以利用deferred接口,使得任意操作都可以用done()和fail()指定回調函數。 ```javascript Twitter = { search:function(query) { var dfd = $.Deferred(); $.ajax({ url:"http://search.twitter.com/search.json", data:{q:query}, dataType:'jsonp', success:dfd.resolve }); return dfd.promise(); } } ``` 使用方法如下。 ```javascript Twitter.search('javaScript').then(function(data) { alert(data.results[0].text); }); ``` deferred對象的另一個優勢是可以附加多個回調函數。下面的例子使用了上面所改寫的setTimeout函數。 ```javascript function doSomething(arg) { var dfd = $.Deferred(); setTimeout(function() { dfd.reject("Sorry, something went wrong."); }); return dfd; } doSomething("uh oh").done(function() { console.log("Won't happen, we're erroring here!"); }).fail(function(message) { console.log(message); }); ``` <h2 id="11.5">如何做到 jQuery-free?</h2> ## 概述 jQuery是最流行的JavaScript工具庫。據[統計](http://w3techs.com/technologies/details/js-jquery/all/all),目前全世界57.3%的網站使用它。也就是說,10個網站里面,有6個使用jQuery。如果只考察使用工具庫的網站,這個比例就會上升到驚人的91.7%。 jQuery如此受歡迎,以至于有被濫用的趨勢。許多開發者不管什么樣的項目,都一股腦使用jQuery。但是,jQuery本質只是一個中間層,提供一套統一易用的DOM操作接口,消除瀏覽器之間的差異。多了這一層中間層,操作的性能和效率多多少少會打一些折扣。 2006年,jQuery誕生的時候,主要是為了解決IE6與標準的不兼容問題。如今的[情況](http://en.wikipedia.org/wiki/Usage_share_of_web_browsers)已經發生了很大的變化。IE的市場份額不斷下降,以ECMAScript為基礎的JavaScript標準語法,正得到越來越廣泛的支持,不同瀏覽器對標準的支持越來越好、越來越趨同。開發者直接使用JavaScript標準語法,就能同時在各大瀏覽器運行,不再需要通過jQuery獲取兼容性。 另一方面,jQuery臃腫的[體積](http://mathiasbynens.be/demo/jquery-size)也讓人頭痛不已。jQuery 2.0的原始大小為235KB,優化后為81KB;如果是支持IE6、7、8的jQuery 1.8.3,原始大小為261KB,優化后為91KB。即使有CDN,瀏覽器加載這樣大小的腳本,也會產生不小的開銷。 所以,對于一些不需要支持老式瀏覽器的小型項目來說,不使用jQuery,直接使用DOM原生接口,可能是更好的選擇。開發者有必要了解,jQuery的一些常用操作所對應的DOM寫法。而且,理解jQuery背后的原理,會幫助你更好地使用jQuery。要知道有一種極端的說法是,如果你不理解一樣東西,就不要使用它。 下面就探討如何用JavaScript標準語法,取代jQuery的一些主要功能,做到jQuery-free。 ## 選取DOM元素 jQuery的核心是通過各種選擇器,選中DOM元素,可以用querySelectorAll方法模擬這個功能。 ```javascript var $ = document.querySelectorAll.bind(document); ``` 這里需要注意的是,querySelectorAll方法返回的是NodeList對象,它很像數組(有數字索引和length屬性),但不是數組,不能使用pop、push等數組特有方法。如果有需要,可以考慮將Nodelist對象轉為數組。 ```javascript myList = Array.prototype.slice.call(myNodeList); ``` ## DOM操作 DOM本身就具有很豐富的操作方法,可以取代jQuery提供的操作方法。 獲取父元素。 ```javascript // jQuery寫法 $("#elementID").parent() // DOM寫法 document.getElementById("elementID").parentNode ``` 獲取下一個同級元素。 ```javascript // jQuery寫法 $("#elementID").next() // DOM寫法 document.getElementById("elementID").nextSibling ``` 尾部追加DOM元素。 ```javascript // jQuery寫法 $(parent).append($(child)); // DOM寫法 parent.appendChild(child) ``` 頭部插入DOM元素。 ```javascript // jQuery寫法 $(parent).prepend($(child)); // DOM寫法 parent.insertBefore(child, parent.childNodes[0]) ``` 生成DOM元素。 ```javascript // jQuery寫法 $("<p>") // DOM寫法 document.createElement("p") ``` 刪除DOM元素。 ```javascript // jQuery寫法 $(child).remove() // DOM寫法 child.parentNode.removeChild(child) ``` 清空子元素。 ```javascript // jQuery寫法 $("#elementID").empty() // DOM寫法 var element = document.getElementById("elementID"); while(element.firstChild) element.removeChild(element.firstChild); ``` 檢查是否有子元素。 ```javascript // jQuery寫法 if (!$("#elementID").is(":empty")){} // DOM寫法 if (document.getElementById("elementID").hasChildNodes()){} ``` 克隆元素。 ```javascript // jQuery寫法 $("#elementID").clone() // DOM寫法 document.getElementById("elementID").cloned(true) ``` ## 事件的監聽 jQuery使用on方法,監聽事件和綁定回調函數。 ```javascript $('button').on('click', function(){ ajax( ... ); }); ``` 完全可以自己定義on方法,將它指向addEventListener方法。 ```javascript Element.prototype.on = Element.prototype.addEventListener; ``` 為了使用方便,可以在NodeList對象上也部署這個方法。 ```javascript NodeList.prototype.on = function (event, fn) { []['forEach'].call(this, function (el) { el.on(event, fn); }); return this; }; ``` 取消事件綁定的off方法,也可以自己定義。 ```javascript Element.prototype.off = Element.prototype.removeEventListener; ``` ## 事件的觸發 jQuery的trigger方法則需要單獨部署,相對復雜一些。 ```javascript Element.prototype.trigger = function (type, data) { var event = document.createEvent('HTMLEvents'); event.initEvent(type, true, true); event.data = data || {}; event.eventName = type; event.target = this; this.dispatchEvent(event); return this; }; ``` 在NodeList對象上也部署這個方法。 ```javascript NodeList.prototype.trigger = function (event) { []['forEach'].call(this, function (el) { el['trigger'](event); }); return this; }; ``` ## `$(document).ready` DOM加載完成,會觸發DOMContentLoaded事件,等同于jQuery的`$(document).ready`方法。 ```javascript document.addEventListener("DOMContentLoaded", function() { // ... }); ``` 不過,目前的最佳實踐,是將JavaScript腳本文件都放在頁面底部加載。這樣的話,其實$(document).ready方法(可以簡寫為$(function))已經不必要了,因為等到運行的時候,DOM對象已經生成了。 ## attr方法 jQuery使用attr方法,讀寫網頁元素的屬性。 ```javascript $("#picture").attr("src", "http://url/to/image") ``` DOM提供getAttribute和setAttribute方法讀寫元素屬性。 ```javascript imgElement.setAttribute("src", "http://url/to/image") ``` DOM還允許直接讀取屬性值,寫法要簡潔許多。 ```javascript imgElement.src = "http://url/to/image"; ``` > 需要注意的是,文本框元素(input)的this.value返回的是輸入框中的值,鏈接元素(a標簽)的this.href返回的是絕對URL。如果需要用到這兩個網頁元素的屬性準確值,可以用this.getAttribute('value')和this.getAttibute('href')。 ## addClass方法 jQuery的addClass方法,用于為DOM元素添加一個class。 ```javascript $('body').addClass('hasJS'); ``` DOM元素本身有一個可讀寫的className屬性,可以用來操作class。 ```javascript document.body.className = 'hasJS'; // or document.body.className += ' hasJS'; ``` HTML 5還提供一個classList對象,功能更強大(IE 9不支持)。 ```javascript document.body.classList.add('hasJS'); document.body.classList.remove('hasJS'); document.body.classList.toggle('hasJS'); document.body.classList.contains('hasJS'); ``` ## CSS jQuery的css方法,用來設置網頁元素的樣式。 ```javascript $(node).css( "color", "red" ); ``` DOM元素有一個style屬性,可以直接操作。 ```javascript element.style.color = "red”;; // or element.style.cssText += 'color:red'; ``` ## 數據儲存 jQuery對象可以儲存數據。 ```javascript $("body").data("foo", 52); ``` HTML 5有一個dataset對象,也有類似的功能(IE 10不支持),不過只能保存字符串。 ```javascript element.dataset.user = JSON.stringify(user); element.dataset.score = score; ``` ## Ajax jQuery的ajax方法,用于異步操作。 ```javascript $.ajax({ type: "POST", url: "some.php", data: { name: "John", location: "Boston" } }).done(function( msg ) { alert( "Data Saved: " + msg ); }); ``` 我們自定義一個ajax函數,簡單模擬jQuery的ajax方法。 ```javascript function ajax(url, opts){ var xhr = new XMLHttpRequest(); xhr.onreadystatechange = function(){ var completed = 4; if(xhr.readyState === completed){ if(xhr.status === 200){ opts.success(xhr.responseText, xhr); }else{ opts.error(xhr.responseText, xhr); } } }; xhr.open(opts.method, url, true); xhr.send(opts.data); } ``` 使用的時候,除了網址,還需要傳入一個自己構造的option對象。 ```javascript ajax('/foo', { method: 'GET', success: function(response){ console.log(response); }, error: function(response){ console.log(response); } }); ``` ## 動畫 jQuery的animate方法,用于生成動畫效果。 ```javascript $foo.animate('slow', { x: '+=10px' }) ``` jQuery的動畫效果,很大部分基于DOM。但是目前,CSS 3的動畫遠比DOM強大,所以可以把動畫效果寫進CSS,然后通過操作DOM元素的class,來展示動畫。 ```javascript foo.classList.add('animate') ``` 如果需要對動畫使用回調函數,CSS 3也定義了相應的事件。 ```javascript el.addEventListener("webkitTransitionEnd", transitionEnded); el.addEventListener("transitionend", transitionEnded); ``` ## 替代方案 由于jQuery體積過大,替代方案層出不窮。 其中,最有名的是[zepto.js](http://zeptojs.com/)。它的設計目標是以最小的體積,做到最大兼容jQuery的API。它的1.0版的原始大小是55KB,優化后是29KB,gzip壓縮后為10KB。 如果不求最大兼容,只希望模擬jQuery的基本功能。那么,[min.js](https://github.com/remy/min.js)優化后只有200字節,而[dolla](https://github.com/lelandrichardson/dolla)優化后是1.7KB。 此外,jQuery本身也采用模塊設計,可以只選擇使用自己需要的模塊。具體做法參見jQuery的[github網站](https://github.com/jquery/jquery),或者使用專用的[Web界面](http://projects.jga.me/jquery-builder/)。
                  <ruby id="bdb3f"></ruby>

                  <p id="bdb3f"><cite id="bdb3f"></cite></p>

                    <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
                      <p id="bdb3f"><cite id="bdb3f"></cite></p>

                        <pre id="bdb3f"></pre>
                        <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

                        <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
                        <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

                        <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                              <ruby id="bdb3f"></ruby>

                              哎呀哎呀视频在线观看