久久精品中文字幕av,av.涩涩涩涩涩涩涩涩,亚洲国产日韩欧美精品成人久久久,成人福利电影免费在线观看,日产国产原创av,日韩av午夜激情,bt天堂中文字幕,超级碰人妻在线视频,欧美老熟妇久久一区二区

??一站式輕松地調(diào)用各大LLM模型接口,支持GPT4、智譜、豆包、星火、月之暗面及文生圖、文生視頻 廣告
<h2 id="11.1">概述</h2> jQuery是目前使用最廣泛的JavaScript函數(shù)庫(kù)。據(jù)[統(tǒng)計(jì)](http://w3techs.com/technologies/details/js-jquery/all/all),全世界57.5%的網(wǎng)站使用jQuery,在使用JavaScript函數(shù)庫(kù)的網(wǎng)站中,93.0%使用jQuery。它已經(jīng)成了開發(fā)者必須學(xué)會(huì)的技能。 jQuery的最大優(yōu)勢(shì)有兩個(gè)。首先,它基本是一個(gè)DOM操作工具,可以使操作DOM對(duì)象變得異常容易。其次,它統(tǒng)一了不同瀏覽器的API接口,使得代碼在所有現(xiàn)代瀏覽器均能運(yùn)行,開發(fā)者不用擔(dān)心瀏覽器之間的差異。 ## jQuery的加載 一般采用下面的寫法,在網(wǎng)頁(yè)中加載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> ``` 上面代碼有兩點(diǎn)需要注意。一是采用[CDN](http://jquery.com/download/#using-jquery-with-a-cdn)加載。如果CDN加載失敗,則退回到本地加載。二是采用協(xié)議無(wú)關(guān)的加載網(wǎng)址(使用雙斜線表示),同時(shí)支持http協(xié)議和https協(xié)議。 目前常用的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) 上面這段代碼最好放到網(wǎng)頁(yè)尾部。如果需要支持IE 6/7/8,就使用jQuery 1.x版,否則使用最新版。 ## jQuery基礎(chǔ) ### jQuery對(duì)象 jQuery最重要的概念,就是`jQuery`對(duì)象。它是一個(gè)全局對(duì)象,可以簡(jiǎn)寫為美元符號(hào)`$`。也就是說,`jQuery`和`$`兩者是等價(jià)的。 在網(wǎng)頁(yè)中加載jQuery函數(shù)庫(kù)以后,就可以使用jQuery對(duì)象了。jQuery的全部方法,都定義在這個(gè)對(duì)象上面。 ```javascript var listItems = jQuery('li'); // or var listItems = $('li'); ``` 上面兩行代碼是等價(jià)的,表示選中網(wǎng)頁(yè)中所有的`li`元素。 ### jQuery構(gòu)造函數(shù) `jQuery`對(duì)象本質(zhì)上是一個(gè)構(gòu)造函數(shù),主要作用是返回`jQuery`對(duì)象的實(shí)例。比如,上面代碼表面上是選中`li`元素,實(shí)際上是返回對(duì)應(yīng)于`li`元素的`jQuery`實(shí)例。因?yàn)橹挥羞@樣,才能在DOM對(duì)象上使用jQuery提供的各種方法。 ```javascript $('body').nodeType // undefined $('body') instanceof jQuery // true ``` 上面代碼表示,由于jQuery返回的不是DOM對(duì)象,所以沒有DOM屬性`nodeType`。它返回的是jQuery對(duì)象的實(shí)例。 jQuery構(gòu)造函數(shù)可以多種參數(shù),返回不同的值。 **(1)CSS選擇器作為參數(shù)** jQuery構(gòu)造函數(shù)的參數(shù),主要是CSS選擇器,就像上面的那個(gè)例子。下面是另外一些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選擇器,請(qǐng)讀者參考有關(guān)書籍和jQuery文檔。 除了CSS選擇器,jQuery還定義了一些自有的選擇器,比如`contains`選擇器用來(lái)選擇包含特定文本的元素。下面是一個(gè)例子。 ```javascript var search = $('#search').val(); $('div:not(:contains("' + search + '"))').hide(); ``` 上面代碼用來(lái)選中包含搜索框輸入文本的元素。 **(2)DOM對(duì)象作為參數(shù)** jQuery構(gòu)造函數(shù)的參數(shù),還可以是DOM對(duì)象。它也會(huì)被轉(zhuǎn)為jQuery對(duì)象的實(shí)例。 ```javascript $(document.body) instanceof jQuery // true ``` 上面代碼中,jQuery的參數(shù)不是CSS選擇器,而是一個(gè)DOM對(duì)象,返回的依然是jQuery對(duì)象的實(shí)例。 如果有多個(gè)DOM元素要轉(zhuǎn)為jQuery對(duì)象的實(shí)例,可以把DOM元素放在一個(gè)數(shù)組里,輸入jQuery構(gòu)造函數(shù)。 ```javascript $([document.body, document.head]) ``` **(3)HTML字符串作為參數(shù)** 如果直接在jQuery構(gòu)造函數(shù)中輸入HTML字符串,返回的也是jQuery實(shí)例。 ```javascript $('<li class="greet">test</li>') ``` 上面代碼從HTML代碼生成了一個(gè)jQuery實(shí)例,它與從CSS選擇器生成的jQuery實(shí)例完全一樣。唯一的區(qū)別就是,它對(duì)應(yīng)的DOM結(jié)構(gòu)不屬于當(dāng)前文檔。 上面代碼也可以寫成下面這樣。 ```javascript $( '<li>', { html: 'test', 'class': 'greet' }); ``` 上面代碼中,由于`class`是javaScript的保留字,所以只能放在引號(hào)中。 通常來(lái)說,上面第二種寫法是更好的寫法。 ```javascript $('<input class="form-control" type="hidden" name="foo" value="bar" />') // 相當(dāng)于 $('<input/>', { 'class': 'form-control', type: 'hidden', name: 'foo', value: 'bar' }) // 或者 $('<input/>') .addClass('form-control') .attr('type', 'hidden') .attr('name', 'foo') .val('bar') ``` 由于新增的DOM節(jié)點(diǎn)不屬于當(dāng)前文檔,所以可以用這種寫法預(yù)加載圖片。 ```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)第二個(gè)參數(shù)** 默認(rèn)情況下,jQuery將文檔的根元素(`html`)作為尋找匹配對(duì)象的起點(diǎn)。如果要指定某個(gè)網(wǎng)頁(yè)元素(比如某個(gè)`div`元素)作為尋找的起點(diǎn),可以將它放在jQuery函數(shù)的第二個(gè)參數(shù)。 ```javascript $('li', someElement); ``` 上面代碼表示,只尋找屬于someElement對(duì)象下屬的li元素。someElement可以是jQuery對(duì)象的實(shí)例,也可以是DOM對(duì)象。 ### jQuery構(gòu)造函數(shù)返回的結(jié)果集 jQuery的核心思想是“先選中某些網(wǎng)頁(yè)元素,然后對(duì)其進(jìn)行某種處理”(find something, do something),也就是說,先選擇后處理,這是jQuery的基本操作模式。所以,絕大多數(shù)jQuery操作都是從選擇器開始的,返回一個(gè)選中的結(jié)果集。 **(1)length屬性** jQuery對(duì)象返回的結(jié)果集是一個(gè)類似數(shù)組的對(duì)象,包含了所有被選中的網(wǎng)頁(yè)元素。查看該對(duì)象的length屬性,可以知道到底選中了多少個(gè)結(jié)果。 ```javascript if ( $('li').length === 0 ) { console.log('不含li元素'); } ``` 上面代碼表示,如果網(wǎng)頁(yè)沒有l(wèi)i元素,則返回對(duì)象的length屬性等于0。這就是測(cè)試有沒有選中的標(biāo)準(zhǔn)方法。 所以,如果想知道jQuery有沒有選中相應(yīng)的元素,不能寫成下面這樣。 ```javascript if ($('div.foo')) { ... } ``` 因?yàn)椴还苡袥]有選中,jQuery構(gòu)造函數(shù)總是返回一個(gè)實(shí)例對(duì)象,而對(duì)象的布爾值永遠(yuǎn)是true。使用length屬性才是判斷有沒有選中的正確方法。 ```javascript if ($('div.foo').length) { ... } ``` **(2)下標(biāo)運(yùn)算符** jQuery選擇器返回的是一個(gè)類似數(shù)組的對(duì)象。但是,使用下標(biāo)運(yùn)算符取出的單個(gè)對(duì)象,并不是jQuery對(duì)象的實(shí)例,而是一個(gè)DOM對(duì)象。 ```javascript $('li')[0] instanceof jQuery // false $('li')[0] instanceof Element // true ``` 上面代碼表示,下標(biāo)運(yùn)算符取出的是Element節(jié)點(diǎn)的實(shí)例。所以,通常使用下標(biāo)運(yùn)算符將jQuery實(shí)例轉(zhuǎn)回DOM對(duì)象。 **(3)is方法** is方法返回一個(gè)布爾值,表示選中的結(jié)果是否符合某個(gè)條件。這個(gè)用來(lái)驗(yàn)證的條件,可以是CSS選擇器,也可以是一個(gè)函數(shù),或者DOM元素和jQuery實(shí)例。 ```javascript $('li').is('li') // true $('li').is($('.item')) $('li').is(document.querySelector('li')) $('li').is(function() { return $("strong", this).length === 0; }); ``` **(4)get方法** jQuery實(shí)例的get方法是下標(biāo)運(yùn)算符的另一種寫法。 ```javascript $('li').get(0) instanceof Element // true ``` **(5)eq方法** 如果想要在結(jié)果集取出一個(gè)jQuery對(duì)象的實(shí)例,不需要取出DOM對(duì)象,則使用eq方法,它的參數(shù)是實(shí)例在結(jié)果集中的位置(從0開始)。 ```javascript $('li').eq(0) instanceof jQuery // true ``` 由于eq方法返回的是jQuery的實(shí)例,所以可以在返回結(jié)果上使用jQuery實(shí)例對(duì)象的方法。 **(6)each方法,map方法** 這兩個(gè)方法用于遍歷結(jié)果集,對(duì)每一個(gè)成員進(jìn)行某種操作。 each方法接受一個(gè)函數(shù)作為參數(shù),依次處理集合中的每一個(gè)元素。 ```javascript $('li').each(function( index, element) { $(element).prepend( '<em>' + index + ': </em>' ); }); // <li>Hello</li> // <li>World</li> // 變?yōu)?// <li><em>0: </em>Hello</li> // <li><em>1: </em>World</li> ``` 從上面代碼可以看出,作為each方法參數(shù)的函數(shù),本身有兩個(gè)參數(shù),第一個(gè)是當(dāng)前元素在集合中的位置,第二個(gè)是當(dāng)前元素對(duì)應(yīng)的DOM對(duì)象。 map方法的用法與each方法完全一樣,區(qū)別在于each方法沒有返回值,只是對(duì)每一個(gè)元素執(zhí)行某種操作,而map方法返回一個(gè)新的jQuery對(duì)象。 ```javascript $("input").map(function (index, element){ return $(this).val(); }) .get() .join(", ") ``` 上面代碼表示,將所有input元素依次取出值,然后通過get方法得到一個(gè)包含這些值的數(shù)組,最后通過數(shù)組的join方法返回一個(gè)逗號(hào)分割的字符串。 **(8)內(nèi)置循環(huán)** jQuery默認(rèn)對(duì)當(dāng)前結(jié)果集進(jìn)行循環(huán)處理,所以如果直接使用jQuery內(nèi)置的某種方法,each和map方法是不必要的。 ```javascript $(".class").addClass("highlight"); ``` 上面代碼會(huì)執(zhí)行一個(gè)內(nèi)部循環(huán),對(duì)每一個(gè)選中的元素進(jìn)行addClass操作。由于這個(gè)原因,對(duì)上面操作加上each方法是不必要的。 ```javascript $(".class").each(function(index,element){ $(element).addClass("highlight"); }); // 或者 $(".class").each(function(){ $(this).addClass("highlight"); }); ``` 上面代碼的each方法,都是沒必要使用的。 由于內(nèi)置循環(huán)的存在,從性能考慮,應(yīng)該盡量減少不必要的操作步驟。 ```javascript $(".class").css("color", "green").css("font-size", "16px"); // 應(yīng)該寫成 $(".class").css({ "color": "green", "font-size": "16px" }); ``` ### 鏈?zhǔn)讲僮? jQuery最方便的一點(diǎn)就是,它的大部分方法返回的都是jQuery對(duì)象,因此可以鏈?zhǔn)讲僮?。也就是說,后一個(gè)方法可以緊跟著寫在前一個(gè)方法后面。 ```javascript $('li').click(function (){ $(this).addClass('clicked'); }) .find('span') .attr( 'title', 'Hover over me' ); ``` ### $(document).ready() $(document).ready方法接受一個(gè)函數(shù)作為參數(shù),將該參數(shù)作為document對(duì)象的DOMContentLoaded事件的回調(diào)函數(shù)。也就是說,當(dāng)頁(yè)面解析完成(即下載完&lt;/html&gt;標(biāo)簽)以后,在所有外部資源(圖片、腳本等)完成加載之前,該函數(shù)就會(huì)立刻運(yùn)行。 ```javascript $( document ).ready(function() { console.log( 'ready!' ); }); ``` 上面代碼表示,一旦頁(yè)面完成解析,就會(huì)運(yùn)行ready方法指定的函數(shù),在控制臺(tái)顯示“ready!”。 該方法通常作為網(wǎng)頁(yè)初始化手段使用,jQuery提供了一種簡(jiǎn)寫法,就是直接把回調(diào)函數(shù)放在jQuery對(duì)象中。 ```javascript $(function() { console.log( 'ready!' ); }); ``` 上面代碼與前一段代碼是等價(jià)的。 ### $.noConflict方法 jQuery使用美元符號(hào)($)指代jQuery對(duì)象。某些情況下,其他函數(shù)庫(kù)也會(huì)用到美元符號(hào),為了避免沖突,$.noConflict方法允許將美元符號(hào)與jQuery脫鉤。 ```html <script src="other_lib.js"></script> <script src="jquery.js"></script> <script>$.noConflict();</script> ``` 上面代碼就是$.noConflict方法的一般用法。在加載jQuery之后,立即調(diào)用該方法,會(huì)使得美元符號(hào)還給前面一個(gè)函數(shù)庫(kù)。這意味著,其后再調(diào)用jQuery,只能寫成jQuery.methond的形式,而不能用$.method了。 為了避免沖突,可以考慮從一開始就只使用jQuery代替美元符號(hào)。 ## jQuery實(shí)例對(duì)象的方法 除了上一節(jié)提到的is、get、eq方法,jQuery實(shí)例還有許多其他方法。 ### 結(jié)果集的過濾方法 選擇器選出一組符合條件的網(wǎng)頁(yè)元素以后,jQuery提供了許多方法,可以過濾結(jié)果集,返回更準(zhǔn)確的目標(biāo)。 **(1)first方法,last方法** first方法返回結(jié)果集的第一個(gè)成員,last方法返回結(jié)果集的最后一個(gè)成員。 ```javascri $("li").first() $("li").last() ``` **(2)next方法,prev方法** next方法返回緊鄰的下一個(gè)同級(jí)元素,prev方法返回緊鄰的上一個(gè)同級(jí)元素。 ```javascript $("li").first().next() $("li").last().prev() $("li").first().next('.item') $("li").last().prev('.item') ``` 如果`next`方法和`prev`方法帶有參數(shù),表示選擇符合該參數(shù)的同級(jí)元素。 **(3)parent方法,parents方法,children方法** parent方法返回當(dāng)前元素的父元素,parents方法返回當(dāng)前元素的所有上級(jí)元素(直到html元素)。 ```javascript $("p").parent() $("p").parent(".selected") $("p").parents() $("p").parents("div") ``` children方法返回選中元素的所有子元素。 ```javascript $("div").children() $("div").children(".selected") // 下面的寫法結(jié)果相同,但是效率較低 $('div > *') $('div > .selected') ``` 上面這三個(gè)方法都接受一個(gè)選擇器作為參數(shù)。 **(4)siblings方法,nextAll方法,prevAll方法** siblings方法返回當(dāng)前元素的所有同級(jí)元素。 ```javascript $('li').first().siblings() $('li').first().siblings('.item') ``` nextAll方法返回當(dāng)前元素其后的所有同級(jí)元素,prevAll方法返回當(dāng)前元素前面的所有同級(jí)元素。 ```javascript $('li').first().nextAll() $('li').last().prevAll() ``` **(5)closest方法,find方法** closest方法返回當(dāng)前元素,以及當(dāng)前元素的所有上級(jí)元素之中,第一個(gè)符合條件的元素。find方法返回當(dāng)前元素的所有符合條件的下級(jí)元素。 ```javascript $('li').closest('div') $('div').find('li') ``` 上面代碼中的find方法,選中所有div元素下面的li元素,等同于$('li', 'div')。由于這樣寫縮小了搜索范圍,所以要優(yōu)于$('div li')的寫法。 **(6)find方法,add方法,addBack方法,end方法** add方法用于為結(jié)果集添加元素。 ```javascript $('li').add('p') ``` addBack方法將當(dāng)前元素加回原始的結(jié)果集。 ```javascript $('li').parent().addBack() ``` end方法用于返回原始的結(jié)果集。 ```javascrip $('li').first().end() ``` **(7)filter方法,not方法,has方法** filter方法用于過濾結(jié)果集,它可以接受多種類型的參數(shù),只返回與參數(shù)一致的結(jié)果。 ```javascript // 返回符合CSS選擇器的結(jié)果 $('li').filter('.item') // 返回函數(shù)返回值為true的結(jié)果 $("li").filter(function(index) { return index % 2 === 1; }) // 返回符合特定DOM對(duì)象的結(jié)果 $("li").filter(document.getElementById("unique")) // 返回符合特定jQuery實(shí)例的結(jié)果 $("li").filter($("#unique")) ``` `not`方法的用法與`filter`方法完全一致,但是返回相反的結(jié)果,即過濾掉匹配項(xiàng)。 ```javascript $('li').not('.item') ``` has方法與filter方法作用相同,但是只過濾出子元素符合條件的元素。 ```javascript $("li").has("ul") ``` 上面代碼返回具有ul子元素的li元素。 ### DOM相關(guān)方法 許多方法可以對(duì)DOM元素進(jìn)行處理。 **(1)html方法和text方法** html方法返回該元素包含的HTML代碼,text方法返回該元素包含的文本。 假定網(wǎng)頁(yè)只含有一個(gè)p元素。 ```html <p><em>Hello World!</em></p> ``` html方法和text方法的返回結(jié)果分別如下。 ```javascript $('p').html() // <em>Hello World!</em> $('p').text() // Hello World! ``` jQuery的許多方法都是取值器(getter)與賦值器(setter)的合一,即取值和賦值都是同一個(gè)方法,不使用參數(shù)的時(shí)候?yàn)槿≈灯?,使用參?shù)的時(shí)候?yàn)橘x值器。 上面代碼的html方法和text方法都沒有參數(shù),就會(huì)當(dāng)作取值器使用,取回結(jié)果集的第一個(gè)元素所包含的內(nèi)容。如果對(duì)這兩個(gè)方法提供參數(shù),就是當(dāng)作賦值器使用,修改結(jié)果集所有成員的內(nèi)容,并返回原來(lái)的結(jié)果集,以便進(jìn)行鏈?zhǔn)讲僮鳌? ```javascript $('p').html('<strong>你好</strong>') // 網(wǎng)頁(yè)代碼變?yōu)?lt;p><strong>你好</strong></p> $('p').text('你好') // 網(wǎng)頁(yè)代碼變?yōu)?lt;p>你好</p> ``` 下面要講到的jQuery其他許多方法,都采用這種同一個(gè)方法既是取值器又是賦值器的模式。 html方法和text方法還可以接受一個(gè)函數(shù)作為參數(shù),函數(shù)的返回值就是網(wǎng)頁(yè)元素所要包含的新的代碼和文本。這個(gè)函數(shù)接受兩個(gè)參數(shù),第一個(gè)是網(wǎng)頁(yè)元素在集合中的位置,第二個(gè)參數(shù)是網(wǎng)頁(yè)元素原來(lái)的代碼或文本。 ```javascript $('li').html(function (i, v){ return (i + ': ' + v); }) // <li>Hello</li> // <li>World</li> // 變?yōu)?// <li>0: Hello</li> // <li>1: World</li> ``` **(2)addClass方法,removeClass方法,toggleClass方法** addClass方法用于添加一個(gè)類,removeClass方法用于移除一個(gè)類,toggleClass方法用于折疊一個(gè)類(如果無(wú)就添加,如果有就移除)。 ```javascript $('li').addClass('special') $('li').removeClass('special') $('li').toggleClass('special') ``` **(3)css方法** css方法用于改變CSS設(shè)置。 該方法可以作為取值器使用。 ```javascript $('h1').css('fontSize'); ``` css方法的參數(shù)是css屬性名。這里需要注意,CSS屬性名的CSS寫法和DOM寫法,兩者都可以接受,比如font-size和fontSize都行。 css方法也可以作為賦值器使用。 ```javascript $('li').css('padding-left', '20px') // 或者 $('li').css({ 'padding-left': '20px' }); ``` 上面兩種形式都可以用于賦值,jQuery賦值器基本上都是如此。 **(4)val方法** val方法返回結(jié)果集第一個(gè)元素的值,或者設(shè)置當(dāng)前結(jié)果集所有元素的值。 ```javascript $('input[type="text"]').val() $('input[type="text"]').val('new value') ``` **(5)prop方法,attr方法** 首先,這里要區(qū)分兩種屬性。 一種是網(wǎng)頁(yè)元素的屬性,比如`a`元素的`href`屬性、`img`元素的`src`屬性。這要使用`attr`方法讀寫。 ```javascript // 讀取屬性值 $('textarea').attr(name) //寫入屬性值 $('textarea').attr(name, val) ``` 下面是通過設(shè)置`a`元素的`target`屬性,使得網(wǎng)頁(yè)上的外部鏈接在新窗口打開的例子。 ```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`方法針對(duì)的是不同的屬性。在英語(yǔ)中,`attr`是attribute的縮寫,`prop`是property的縮寫,中文很難表達(dá)出這種差異。有時(shí),`attr`方法和`prop`方法對(duì)同一個(gè)屬性會(huì)讀到不一樣的值。比如,網(wǎng)頁(yè)上有一個(gè)單選框。 ```html <input type="checkbox" checked="checked" /> ``` 對(duì)于checked屬性,attr方法讀到的是checked,prop方法讀到的是true。 ```javascript $(input[type=checkbox]).attr("checked") // "checked" $(input[type=checkbox]).prop("checked") // true ``` 可以看到,attr方法讀取的是網(wǎng)頁(yè)上該屬性的值,而prop方法讀取的是DOM元素的該屬性的值,根據(jù)規(guī)范,element.checked應(yīng)該返回一個(gè)布爾值。所以,判斷單選框是否選中,要使用prop方法。事實(shí)上,不管這個(gè)單選框是否選中,attr("checked")的返回值都是checked。 ```javascript if ($(elem).prop("checked")) { /*... */ }; // 下面兩種方法亦可 if ( elem.checked ) { /*...*/ }; if ( $(elem).is(":checked") ) { /*...*/ }; ``` **(6)removeProp方法,removeAttr方法** removeProp方法移除某個(gè)DOM屬性,removeAttr方法移除某個(gè)HTML屬性。 ```javascript $("a").prop("oldValue",1234).removeProp('oldValue') $('a').removeAttr("title") ``` **(7)data方法** data方法用于在一個(gè)DOM對(duì)象上儲(chǔ)存數(shù)據(jù)。 ```javascript // 儲(chǔ)存數(shù)據(jù) $("body").data("foo", 52); // 讀取數(shù)據(jù) $("body").data("foo"); ``` 該方法可以在DOM節(jié)點(diǎn)上儲(chǔ)存各種類型的數(shù)據(jù)。 ### 添加、復(fù)制和移動(dòng)網(wǎng)頁(yè)元素的方法 jQuery方法提供一系列方法,可以改變?cè)卦谖臋n中的位置。 **(1)append方法,appendTo方法** append方法將參數(shù)中的元素插入當(dāng)前元素的尾部。 ```javascript $("div").append("<p>World</p>") // <div>Hello </div> // 變?yōu)?// <div>Hello <p>World</p></div> ``` appendTo方法將當(dāng)前元素插入?yún)?shù)中的元素尾部。 ```javascript $("<p>World</p>").appendTo("div") ``` 上面代碼返回與前一個(gè)例子一樣的結(jié)果。 **(2)prepend方法,prependTo方法** prepend方法將參數(shù)中的元素,變?yōu)楫?dāng)前元素的第一個(gè)子元素。 ```javascript $("p").prepend("Hello ") // <p>World</p> // 變?yōu)?// <p>Hello World</p> ``` 如果prepend方法的參數(shù)不是新生成的元素,而是當(dāng)前頁(yè)面已存在的元素,則會(huì)產(chǎn)生移動(dòng)元素的效果。 ```javascript $("p").prepend("strong") // <strong>Hello </strong><p>World</p> // 變?yōu)?// <p><strong>Hello </strong>World</p> ``` 上面代碼運(yùn)行后,strong元素的位置將發(fā)生移動(dòng),而不是克隆一個(gè)新的strong元素。不過,如果當(dāng)前結(jié)果集包含多個(gè)元素,則除了第一個(gè)以后,后面的p元素都將插入一個(gè)克隆的strong子元素。 prependTo方法將當(dāng)前元素變?yōu)閰?shù)中的元素的第一個(gè)子元素。 ```javascript $("<p></p>").prependTo("div") // <div></div> // 變?yōu)?// <div><p></p></div> ``` **(3)after方法,insertAfter方法** after方法將參數(shù)中的元素插在當(dāng)前元素后面。 ```javascript $("div").after("<p></p>") // <div></div> // 變?yōu)?// <div></div><p></p> ``` insertAfter方法將當(dāng)前元素插在參數(shù)中的元素后面。 ```javascript $("<p></p>").insertAfter("div") ``` 上面代碼返回與前一個(gè)例子一樣的結(jié)果。 **(4)before方法,insertBefore方法** before方法將參數(shù)中的元素插在當(dāng)前元素的前面。 ```javascript $("div").before("<p></p>") // <div></div> // 變?yōu)?// <p></p><div></div> ``` insertBefore方法將當(dāng)前元素插在參數(shù)中的元素的前面。 ```javascript $("<p></p>").insertBefore("div") ``` 上面代碼返回與前一個(gè)例子一樣的結(jié)果。 **(5)wrap方法,wrapAll方法,unwrap方法,wrapInner方法** wrap方法將參數(shù)中的元素變成當(dāng)前元素的父元素。 ```javascript $("p").wrap("<div></div>") // <p></p> // 變?yōu)?// <div><p></p></div> ``` wrap方法的參數(shù)還可以是一個(gè)函數(shù)。 ```javascript $("p").wrap(function() { return "<div></div>"; }) ``` 上面代碼返回與前一個(gè)例子一樣的結(jié)果。 wrapAll方法為結(jié)果集的所有元素,添加一個(gè)共同的父元素。 ```javascript $("p").wrapAll("<div></div>") // <p></p><p></p> // 變?yōu)?// <div><p></p><p></p></div> ``` unwrap方法移除當(dāng)前元素的父元素。 ```javascript $("p").unwrap() // <div><p></p></div> // 變?yōu)?// <p></p> ``` wrapInner方法為當(dāng)前元素的所有子元素,添加一個(gè)父元素。 ```javascript $("p").wrapInner('<strong></strong>') // <p>Hello</p> // 變?yōu)?// <p><strong>Hello</strong></p> ``` **(6)clone方法** clone方法克隆當(dāng)前元素。 ```javascript var clones = $('li').clone(); ``` 對(duì)于那些有id屬性的節(jié)點(diǎn),clone方法會(huì)連id屬性一起克隆。所以,要把克隆的節(jié)點(diǎn)插入文檔的時(shí)候,務(wù)必要修改或移除id屬性。 **(7)remove方法,detach方法,replaceWith方法** remove方法移除并返回一個(gè)元素,取消該元素上所有事件的綁定。detach方法也是移除并返回一個(gè)元素,但是不取消該元素上所有事件的綁定。 ```javascript $('p').remove() $('p').detach() ``` replaceWith方法用參數(shù)中的元素,替換并返回當(dāng)前元素,取消當(dāng)前元素的所有事件的綁定。 ```javascript $('p').replaceWith('<div></div>') ``` ### 動(dòng)畫效果方法 jQuery提供一些方法,可以很容易地顯示網(wǎng)頁(yè)動(dòng)畫效果。但是,總體上來(lái)說,它們不如CSS動(dòng)畫強(qiáng)大和節(jié)省資源,所以應(yīng)該優(yōu)先考慮使用CSS動(dòng)畫。 如果將jQuery.fx.off設(shè)為true,就可以將所有動(dòng)畫效果關(guān)閉,使得網(wǎng)頁(yè)元素的各種變化一步到位,沒有中間過渡的動(dòng)畫效果。 **(1)動(dòng)畫效果的簡(jiǎn)便方法** jQuery提供以下一些動(dòng)畫效果方法。 - show:顯示當(dāng)前元素。 - hide:隱藏當(dāng)前元素。 - toggle:顯示或隱藏當(dāng)前元素。 - fadeIn:將當(dāng)前元素的不透明度(opacity)逐步提升到100%。 - fadeOut:將當(dāng)前元素的不透明度逐步降為0%。 - fadeToggle:以逐漸透明或逐漸不透明的方式,折疊顯示當(dāng)前元素。 - slideDown:以從上向下滑入的方式顯示當(dāng)前元素。 - slideUp:以從下向上滑出的方式隱藏當(dāng)前元素。 - slideToggle:以垂直滑入或滑出的方式,折疊顯示當(dāng)前元素。 上面這些方法可以不帶參數(shù)調(diào)用,也可以接受毫秒或預(yù)定義的關(guān)鍵字作為參數(shù)。 ```javascript $('.hidden').show(); $('.hidden').show(300); $('.hidden').show('slow'); ``` 上面三行代碼分別表示,以默認(rèn)速度、300毫秒、較慢的速度隱藏一個(gè)元素。 jQuery預(yù)定義的關(guān)鍵字是在`jQuery.fx.speeds`對(duì)象上面,可以自行改動(dòng)這些值,或者創(chuàng)造新的值。 ```javascript jQuery.fx.speeds.fast = 50; jQuery.fx.speeds.slow = 3000; jQuery.fx.speeds.normal = 1000; ``` 上面三行代碼重新定義fast、normal、slow關(guān)鍵字對(duì)應(yīng)的毫秒數(shù)。 你還可以定義自己的關(guān)鍵字。 ```javascript jQuery.fx.speeds.blazing = 30; // 調(diào)用 $('.hidden').show('blazing'); ``` 這些方法還可以接受一個(gè)函數(shù),作為第二個(gè)參數(shù),表示動(dòng)畫結(jié)束后的回調(diào)函數(shù)。 ```javascript $('p').fadeOut(300, function() { $(this).remove(); }); ``` 上面代碼表示,`p`元素以300毫秒的速度淡出,然后調(diào)用回調(diào)函數(shù),將其從DOM中移除。 使用按鈕控制某個(gè)元素折疊顯示的代碼如下。 ```javascript // Fade $('.btn').click(function () { $('.element').fadeToggle('slow'); }); // Toggle $('.btn').click(function () { $('.element').slideToggle('slow'); }); ``` **(2)animate方法** 上面這些動(dòng)畫效果方法,實(shí)際上都是animate方法的簡(jiǎn)便寫法。在幕后,jQuery都是統(tǒng)一使用animate方法生成各種動(dòng)畫效果。 ```javascript $('a.top').click(function (e) { e.preventDefault(); $('html, body').animate({scrollTop: 0}, 800); }); ``` 上面代碼是點(diǎn)擊鏈接,回到頁(yè)面頭部的寫法。其中,`animate`方法接受兩個(gè)參數(shù),第一個(gè)參數(shù)是一個(gè)對(duì)象,表示動(dòng)畫結(jié)束時(shí)相關(guān)CSS屬性的值,第二個(gè)參數(shù)是動(dòng)畫持續(xù)的毫秒數(shù)。需要注意的是,第一個(gè)參數(shù)對(duì)象的成員名稱,必須與CSS屬性名稱一致,如果CSS屬性名稱帶有連字號(hào),則需要用“駱駝拼寫法”改寫。 animate方法還可以接受第三個(gè)參數(shù),表示動(dòng)畫結(jié)束時(shí)的回調(diào)函數(shù)。 ```javascript $('div').animate({ left: '+=50', // 增加50 opacity: 0.25, fontSize: '12px' }, 300, // 持續(xù)時(shí)間 function() { // 回調(diào)函數(shù) console.log('done!'); } ); ``` 上面代碼表示,動(dòng)畫結(jié)束時(shí),在控制臺(tái)輸出“done!”。 **(3)stop方法,delay方法** stop方法表示立即停止執(zhí)行當(dāng)前的動(dòng)畫。 ```javascript $("#stop").click(function() { $(".block").stop(); }); ``` 上面代碼表示,點(diǎn)擊按鈕后,block元素的動(dòng)畫效果停止。 delay方法接受一個(gè)時(shí)間參數(shù),表示暫停多少毫秒后繼續(xù)執(zhí)行。 ```javascript $("#foo").slideUp(300).delay(800).fadeIn(400) ``` 上面代碼表示,slideUp動(dòng)畫之后,暫停800毫秒,然后繼續(xù)執(zhí)行fadeIn動(dòng)畫。 ### 其他方法 jQuery還提供一些供特定元素使用的方法。 serialize方法用于將表單元素的值,轉(zhuǎn)為url使用的查詢字符串。 ```javascript $( "form" ).on( "submit", function( event ) { event.preventDefault(); console.log( $( this ).serialize() ); }); // single=Single&multiple=Multiple&check=check2&radio=radio1 ``` serializeArray方法用于將表單元素的值轉(zhuǎn)為數(shù)組。 ```javascript $("form").submit(function (event){ console.log($(this).serializeArray()); event.preventDefault(); }); // [ // {name : 'field1', value : 123}, // {name : 'field2', value : 'hello world'} // ] ``` ## 事件處理 ### 事件綁定的簡(jiǎn)便方法 jQuery提供一系列方法,允許直接為常見事件綁定回調(diào)函數(shù)。比如,click方法可以為一個(gè)元素綁定click事件的回調(diào)函數(shù)。 ```javascript $('li').click(function (e){ console.log($(this).text()); }); ``` 上面代碼為li元素綁定click事件的回調(diào)函數(shù),點(diǎn)擊后在控制臺(tái)顯示li元素包含的文本。 這樣綁定事件的簡(jiǎn)便方法有如下一些: - click - keydown - keypress - keyup - mouseover - mouseout - mouseenter - mouseleave - scroll - focus - blur - resize - hover 如果不帶參數(shù)調(diào)用這些方法,就是觸發(fā)相應(yīng)的事件,從而引發(fā)回調(diào)函數(shù)的運(yùn)行。 ```javascript $('li').click() ``` 上面代碼將觸發(fā)click事件的回調(diào)函數(shù)。 需要注意的是,通過這種方法觸發(fā)回調(diào)函數(shù),將不會(huì)引發(fā)瀏覽器對(duì)該事件的默認(rèn)行為。比如,對(duì)a元素調(diào)用click方法,將只觸發(fā)事先綁定的回調(diào)函數(shù),而不會(huì)導(dǎo)致瀏覽器將頁(yè)面導(dǎo)向href屬性指定的網(wǎng)址。 下面是一個(gè)捕捉用戶按下escape鍵的函數(shù)。 ```javascript $(document).keyup(function(e) { if (e.keyCode == 27) { $('body').toggleClass('show-nav'); // $('body').removeClass('show-nav'); } }); ``` 上面代碼中,用戶按下escape鍵,jQuery就會(huì)為body元素添加/去除名為show-nav的class。 `hover`方法需要特別說明。它接受兩個(gè)回調(diào)函數(shù)作為參數(shù),分別代表`mouseenter`和`mouseleave`事件的回調(diào)函數(shù)。 ```javascript $(selector).hover(handlerIn, handlerOut) // 等同于 $(selector).mouseenter(handlerIn).mouseleave(handlerOut) ``` 下面是一個(gè)例子,當(dāng)按鈕發(fā)生`hover`事件,添加一個(gè)class樣式,當(dāng)`hover`事件結(jié)束時(shí),再取消這個(gè)class。 ```javascript $('.btn').hover(function () { $(this).addClass('hover'); }, function () { $(this).removeClass('hover'); }); ``` 使用`toggleClass`可以簡(jiǎn)化上面的代碼。 ```javascript $('.btn').hover(function () { $(this).toggleClass('hover'); }); ``` ### on方法,trigger方法,off方法 除了簡(jiǎn)便方法,jQuery還提供事件處理的通用方法。 **(1)on方法** `on`方法是jQuery事件綁定的統(tǒng)一接口。事件綁定的那些簡(jiǎn)便方法,其實(shí)都是`on`方法的簡(jiǎn)寫形式。 `on`方法接受兩個(gè)參數(shù),第一個(gè)是事件名稱,第二個(gè)是回調(diào)函數(shù)。 ```javascript $('li').on('click', function (e){ console.log($(this).text()); }); ``` 上面代碼為`li`元素綁定`click`事件的回調(diào)函數(shù)。 > 注意,在回調(diào)函數(shù)內(nèi)部,`this`關(guān)鍵字指的是發(fā)生該事件的DOM對(duì)象。為了使用jQuery提供的方法,必須將DOM對(duì)象轉(zhuǎn)為jQuery對(duì)象,因此寫成`$(this)`。 `on`方法允許一次為多個(gè)事件指定同樣的回調(diào)函數(shù)。 ```javascript $('input[type="text"]').on('focus blur', function (){ console.log('focus or blur'); }); ``` 上面代碼為文本框的`focus`和`blur`事件綁定同一個(gè)回調(diào)函數(shù)。 下面是一個(gè)例子,當(dāng)圖片加載失敗,使用`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`方法還可以為當(dāng)前元素的某一個(gè)子元素,添加回調(diào)函數(shù)。 ```javascript $('ul').on('click', 'li', function (e){ console.log(this); }); ``` 上面代碼為`ul`的子元素`li`綁定click事件的回調(diào)函數(shù)。采用這種寫法時(shí),on方法接受三個(gè)參數(shù),子元素選擇器作為第二個(gè)參數(shù),夾在事件名稱和回調(diào)函數(shù)之間。 這種寫法有兩個(gè)好處。首先,click事件還是在ul元素上觸發(fā)回調(diào)函數(shù),但是會(huì)檢查event對(duì)象的target屬性是否為li子元素,如果為true,再調(diào)用回調(diào)函數(shù)。這樣就比為li元素一一綁定回調(diào)函數(shù),節(jié)省了內(nèi)存空間。其次,這種綁定的回調(diào)函數(shù),對(duì)于在綁定后生成的li元素依然有效。 on方法還允許向回調(diào)函數(shù)傳入數(shù)據(jù)。 ```javascript $("ul" ).on("click", {name: "張三"}, function (event){ console.log(event.data.name); }); ``` 上面代碼在發(fā)生click事件之后,會(huì)通過event對(duì)象的data屬性,在控制臺(tái)打印出所傳入的數(shù)據(jù)(即“張三”)。 **(2)trigger方法** trigger方法用于觸發(fā)回調(diào)函數(shù),它的參數(shù)就是事件的名稱。 ```javascript $('li').trigger('click') ``` 上面代碼觸發(fā)li元素的click事件回調(diào)函數(shù)。與那些簡(jiǎn)便方法一樣,trigger方法只觸發(fā)回調(diào)函數(shù),而不會(huì)引發(fā)瀏覽器的默認(rèn)行為。 **(3)off方法** off方法用于移除事件的回調(diào)函數(shù)。 ```javascript $('li').off('click') ``` 上面代碼移除li元素所有的click事件回調(diào)函數(shù)。 **(4)事件的名稱空間** 同一個(gè)事件有時(shí)綁定了多個(gè)回調(diào)函數(shù),這時(shí)如果想移除其中的一個(gè)回調(diào)函數(shù),可以采用“名稱空間”的方式,即為每一個(gè)回調(diào)函數(shù)指定一個(gè)二級(jí)事件名,然后再用off方法移除這個(gè)二級(jí)事件的回調(diào)函數(shù)。 ```javascript $('li').on('click.logging', function (){ console.log('click.logging callback removed'); }); $('li').off('click.logging'); ``` 上面代碼為li元素定義了二級(jí)事件click.logging的回調(diào)函數(shù),click.logging屬于click名稱空間,當(dāng)發(fā)生click事件時(shí)會(huì)觸發(fā)該回調(diào)函數(shù)。將click.logging作為off方法的參數(shù),就會(huì)移除這個(gè)回調(diào)函數(shù),但是對(duì)其他click事件的回調(diào)函數(shù)沒有影響。 trigger方法也適用帶名稱空間的事件。 ```javascript $('li').trigger('click.logging') ``` ### event對(duì)象 當(dāng)回調(diào)函數(shù)被觸發(fā)后,它們的參數(shù)通常是一個(gè)事件對(duì)象event。 ```javascript $(document).on('click', function (e){ // ... }); ``` 上面代碼的回調(diào)函數(shù)的參數(shù)e,就代表事件對(duì)象event。 event對(duì)象有以下屬性: - type:事件類型,比如click。 - which:觸發(fā)該事件的鼠標(biāo)按鈕或鍵盤的鍵。 - target:事件發(fā)生的初始對(duì)象。 - data:傳入事件對(duì)象的數(shù)據(jù)。 - pageX:事件發(fā)生時(shí),鼠標(biāo)位置的水平坐標(biāo)(相對(duì)于頁(yè)面左上角)。 - pageY:事件發(fā)生時(shí),鼠標(biāo)位置的垂直坐標(biāo)(相對(duì)于頁(yè)面左上角)。 event對(duì)象有以下方法: - preventDefault:取消瀏覽器默認(rèn)行為。 - stopPropagation:阻止事件向上層元素傳播。 ### 一次性事件 one方法指定一次性的回調(diào)函數(shù),即這個(gè)函數(shù)只能運(yùn)行一次。這對(duì)提交表單很有用。 ```javascript $("#button").one( "click", function() { return false; } ); ``` one方法本質(zhì)上是回調(diào)函數(shù)運(yùn)行一次,即解除對(duì)事件的監(jiān)聽。 ```javascript document.getElementById("#button").addEventListener("click", handler); function handler(e) { e.target.removeEventListener(e.type, arguments.callee); return false; } ``` 上面的代碼在點(diǎn)擊一次以后,取消了對(duì)click事件的監(jiān)聽。如果有特殊需要,可以設(shè)定點(diǎn)擊2次或3次之后取消監(jiān)聽,這都是可以的。 <h2 id="11.2">jQuery工具方法</h2> jQuery函數(shù)庫(kù)提供了一個(gè)jQuery對(duì)象(簡(jiǎn)寫為$),這個(gè)對(duì)象本身是一個(gè)構(gòu)造函數(shù),可以用來(lái)生成jQuery對(duì)象的實(shí)例。有了實(shí)例以后,就可以調(diào)用許多針對(duì)實(shí)例的方法,它們定義jQuery.prototype對(duì)象上面(簡(jiǎn)寫為$.fn)。 除了實(shí)例對(duì)象的方法以外,jQuery對(duì)象本身還提供一些方法(即直接定義jQuery對(duì)象上面),不需要生成實(shí)例就能使用。由于這些方法類似“通用工具”的性質(zhì),所以我們把它們稱為“工具方法”(utilities)。 ## 常用工具方法 **(1)$.trim** $.trim方法用于移除字符串頭部和尾部多余的空格。 ```javascript $.trim(' Hello ') // Hello ``` **(2)$.contains** $.contains方法返回一個(gè)布爾值,表示某個(gè)DOM元素(第二個(gè)參數(shù))是否為另一個(gè)DOM元素(第一個(gè)參數(shù))的下級(jí)元素。 ```javascript $.contains(document.documentElement, document.body); // true $.contains(document.body, document.documentElement); // false ``` **(3)$.each,$.map** $.each方法用于遍歷數(shù)組和對(duì)象,然后返回原始對(duì)象。它接受兩個(gè)參數(shù),分別是數(shù)據(jù)集合和回調(diào)函數(shù)。 ```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對(duì)象實(shí)例也有一個(gè)each方法($.fn.each),兩者的作用差不多。 $.map方法也是用來(lái)遍歷數(shù)組和對(duì)象,但是會(huì)返回一個(gè)新對(duì)象。 ```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方法返回一個(gè)值在數(shù)組中的位置(從0開始)。如果該值不在數(shù)組中,則返回-1。 ```javascript var a = [1,2,3,4]; $.inArray(4,a) // 3 ``` **(5)$.extend** $.extend方法用于將多個(gè)對(duì)象合并進(jìn)第一個(gè)對(duì)象。 ```javascript var o1 = {p1:'a',p2:'b'}; var o2 = {p1:'c'}; $.extend(o1,o2); o1.p1 // "c" ``` $.extend的另一種用法是生成一個(gè)新對(duì)象,用來(lái)繼承原有對(duì)象。這時(shí),它的第一個(gè)參數(shù)應(yīng)該是一個(gè)空對(duì)象。 ```javascript var o1 = {p1:'a',p2:'b'}; var o2 = {p1:'c'}; var o = $.extend({},o1,o2); o // Object {p1: "c", p2: "b"} ``` 默認(rèn)情況下,extend方法生成的對(duì)象是“淺拷貝”,也就是說,如果某個(gè)屬性是對(duì)象或數(shù)組,那么只會(huì)生成指向這個(gè)對(duì)象或數(shù)組的指針,而不會(huì)復(fù)制值。如果想要“深拷貝”,可以在extend方法的第一個(gè)參數(shù)傳入布爾值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是深拷貝。結(jié)果,改變?cè)紨?shù)組的屬性,o2會(huì)跟著一起變,而o3不會(huì)。 **(6)$.proxy** $.proxy方法類似于ECMAScript 5的bind方法,可以綁定函數(shù)的上下文(也就是this對(duì)象)和參數(shù),返回一個(gè)新函數(shù)。 jQuery.proxy()的主要用處是為回調(diào)函數(shù)綁定上下文對(duì)象。 ```javascript var o = { type: "object", test: function(event) { console.log(this.type); } }; $("#button") .on("click", o.test) // 無(wú)輸出 .on("click", $.proxy(o.test, o)) // object ``` 上面的代碼中,第一個(gè)回調(diào)函數(shù)沒有綁定上下文,所以結(jié)果為空,沒有任何輸出;第二個(gè)回調(diào)函數(shù)將上下文綁定為對(duì)象o,結(jié)果就為object。 這個(gè)例子的另一種等價(jià)的寫法是: ```javascript $("#button").on( "click", $.proxy(o, test)) ``` 上面代碼的$.proxy(o, test)的意思是,將o的方法test與o綁定。 這個(gè)例子表明,proxy方法的寫法主要有兩種。 ```javascript jQuery.proxy(function, context) // or jQuery.proxy(context, name) ``` 第一種寫法是為函數(shù)(function)指定上下文對(duì)象(context),第二種寫法是指定上下文對(duì)象(context)和它的某個(gè)方法名(name)。 再看一個(gè)例子。正常情況下,下面代碼中的this對(duì)象指向發(fā)生click事件的DOM對(duì)象。 ```javascript $('#myElement').click(function() { $(this).addClass('aNewClass'); }); ``` 如果我們想讓回調(diào)函數(shù)延遲運(yùn)行,使用setTimeout方法,代碼就會(huì)出錯(cuò),因?yàn)閟etTimeout使得回調(diào)函數(shù)在全局環(huán)境運(yùn)行,this將指向全局對(duì)象。 ```javascript $('#myElement').click(function() { setTimeout(function() { $(this).addClass('aNewClass'); }, 1000); }); ``` 上面代碼中的this,將指向全局對(duì)象window,導(dǎo)致出錯(cuò)。 這時(shí),就可以用proxy方法,將this對(duì)象綁定到myElement對(duì)象。 ```javascript $('#myElement').click(function() { setTimeout($.proxy(function() { $(this).addClass('aNewClass'); }, this), 1000); }); ``` **(7)$.data,$.removeData** $.data方法可以用來(lái)在DOM節(jié)點(diǎn)上儲(chǔ)存數(shù)據(jù)。 ```javascript // 存入數(shù)據(jù) $.data(document.body, "foo", 52 ); // 讀取數(shù)據(jù) $.data(document.body, "foo"); // 讀取所有數(shù)據(jù) $.data(document.body); ``` 上面代碼在網(wǎng)頁(yè)元素body上儲(chǔ)存了一個(gè)鍵值對(duì),鍵名為“foo”,鍵值為52。 $.removeData方法用于移除$.data方法所儲(chǔ)存的數(shù)據(jù)。 ```javascript $.data(div, "test1", "VALUE-1"); $.removeData(div, "test1"); ``` **(8)$.parseHTML,$.parseJSON,$.parseXML** $.parseHTML方法用于將字符串解析為DOM對(duì)象。 $.parseJSON方法用于將JSON字符串解析為JavaScript對(duì)象,作用與原生的JSON.parse()類似。但是,jQuery沒有提供類似JSON.stringify()的方法,即不提供將JavaScript對(duì)象轉(zhuǎn)為JSON對(duì)象的方法。 $.parseXML方法用于將字符串解析為XML對(duì)象。 ```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方法將一個(gè)類似數(shù)組的對(duì)象,轉(zhuǎn)化為真正的數(shù)組。 ```javascript var a = $.makeArray(document.getElementsByTagName("div")); ``` **(10)$.merge** $.merge方法用于將一個(gè)數(shù)組(第二個(gè)參數(shù))合并到另一個(gè)數(shù)組(第一個(gè)參數(shù))之中。 ```javascript var a1 = [0,1,2]; var a2 = [2,3,4]; $.merge(a1, a2); a1 // [0, 1, 2, 2, 3, 4] ``` **(11)$.now** $.now方法返回當(dāng)前時(shí)間距離1970年1月1日00:00:00 UTC對(duì)應(yīng)的毫秒數(shù),等同于(new Date).getTime()。 ```javascript $.now() // 1388212221489 ``` ## 判斷數(shù)據(jù)類型的方法 jQuery提供一系列工具方法,用來(lái)判斷數(shù)據(jù)類型,以彌補(bǔ)JavaScript原生的typeof運(yùn)算符的不足。以下方法對(duì)參數(shù)進(jìn)行判斷,返回一個(gè)布爾值。 - jQuery.isArray():是否為數(shù)組。 - jQuery.isEmptyObject():是否為空對(duì)象(不含可枚舉的屬性)。 - jQuery.isFunction():是否為函數(shù)。 - jQuery.isNumeric():是否為數(shù)值(整數(shù)或浮點(diǎn)數(shù))。 - jQuery.isPlainObject():是否為使用“{}”或“new Object”生成的對(duì)象,而不是瀏覽器原生提供的對(duì)象。 - jQuery.isWindow():是否為window對(duì)象。 - jQuery.isXMLDoc():判斷一個(gè)DOM節(jié)點(diǎn)是否處于XML文檔之中。 下面是一些例子。 ```javascript $.isEmptyObject({}) // true $.isPlainObject(document.location) // false $.isWindow(window) // true $.isXMLDoc(document.body) // false ``` 除了上面這些方法以外,還有一個(gè)$.type方法,可以返回一個(gè)變量的數(shù)據(jù)類型。它的實(shí)質(zhì)是用Object.prototype.toString方法讀取對(duì)象內(nèi)部的[[Class]]屬性(參見《標(biāo)準(zhǔn)庫(kù)》的Object對(duì)象一節(jié))。 ```javascript $.type(/test/) // "regexp" ``` ## Ajax操作 ### $.ajax jQuery對(duì)象上面還定義了Ajax方法($.ajax()),用來(lái)處理Ajax操作。調(diào)用該方法后,瀏覽器就會(huì)向服務(wù)器發(fā)出一個(gè)HTTP請(qǐng)求。 $.ajax()的用法主要有兩種。 ```javascript $.ajax(url[, options]) $.ajax([options]) ``` 上面代碼中的url,指的是服務(wù)器網(wǎng)址,options則是一個(gè)對(duì)象參數(shù),設(shè)置Ajax請(qǐng)求的具體參數(shù)。 ```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請(qǐng)求已結(jié)束。'); } ``` 上面代碼的對(duì)象參數(shù)有多個(gè)屬性,含義如下: - accepts:將本機(jī)所能處理的數(shù)據(jù)類型,告訴服務(wù)器。 - async:該項(xiàng)默認(rèn)為true,如果設(shè)為false,則表示發(fā)出的是同步請(qǐng)求。 - beforeSend:指定發(fā)出請(qǐng)求前,所要調(diào)用的函數(shù),通常用來(lái)對(duì)發(fā)出的數(shù)據(jù)進(jìn)行修改。 - cache:該項(xiàng)默認(rèn)為true,如果設(shè)為false,則瀏覽器不緩存返回服務(wù)器返回的數(shù)據(jù)。注意,瀏覽器本身就不會(huì)緩存POST請(qǐng)求返回的數(shù)據(jù),所以即使設(shè)為false,也只對(duì)HEAD和GET請(qǐng)求有效。 - complete:指定當(dāng)HTTP請(qǐng)求結(jié)束時(shí)(請(qǐng)求成功或請(qǐng)求失敗的回調(diào)函數(shù),此時(shí)已經(jīng)運(yùn)行完畢)的回調(diào)函數(shù)。不管請(qǐng)求成功或失敗,該回調(diào)函數(shù)都會(huì)執(zhí)行。它的參數(shù)為發(fā)出請(qǐng)求的原始對(duì)象以及返回的狀態(tài)信息。 - contentType:發(fā)送到服務(wù)器的數(shù)據(jù)類型。 - context:指定一個(gè)對(duì)象,作為所有Ajax相關(guān)的回調(diào)函數(shù)的this對(duì)象。 - crossDomain:該屬性設(shè)為true,將強(qiáng)制向相同域名發(fā)送一個(gè)跨域請(qǐng)求(比如JSONP)。 - data:向服務(wù)器發(fā)送的數(shù)據(jù),如果使用GET方法,此項(xiàng)將轉(zhuǎn)為查詢字符串,附在網(wǎng)址的最后。 - dataType:向服務(wù)器請(qǐng)求的數(shù)據(jù)類型,可以設(shè)為text、html、script、json、jsonp和xml。 - error:請(qǐng)求失敗時(shí)的回調(diào)函數(shù),函數(shù)參數(shù)為發(fā)出請(qǐng)求的原始對(duì)象以及返回的狀態(tài)信息。 - headers:指定HTTP請(qǐng)求的頭信息。 - ifModified:如果該屬性設(shè)為true,則只有當(dāng)服務(wù)器端的內(nèi)容與上次請(qǐng)求不一樣時(shí),才會(huì)發(fā)出本次請(qǐng)求。 - jsonp:指定JSONP請(qǐng)求“callback=?”中的callback的名稱。 - jsonpCallback: 指定JSONP請(qǐng)求中回調(diào)函數(shù)的名稱。 - mimeType:指定HTTP請(qǐng)求的mime type。 - password:指定HTTP認(rèn)證所需要的密碼。 - statusCode:值為一個(gè)對(duì)象,為服務(wù)器返回的狀態(tài)碼,指定特別的回調(diào)函數(shù)。 - success:請(qǐng)求成功時(shí)的回調(diào)函數(shù),函數(shù)參數(shù)為服務(wù)器傳回的數(shù)據(jù)、狀態(tài)信息、發(fā)出請(qǐng)求的原始對(duì)象。 - timeout: 等待的最長(zhǎng)毫秒數(shù)。如果過了這個(gè)時(shí)間,請(qǐng)求還沒有返回,則自動(dòng)將請(qǐng)求狀態(tài)改為失敗。 - type:向服務(wù)器發(fā)送信息所使用的HTTP動(dòng)詞,默認(rèn)為GET,其他動(dòng)詞有POST、PUT、DELETE。 - url:服務(wù)器端網(wǎng)址。這是唯一必需的一個(gè)屬性,其他屬性都可以省略。 - username:指定HTTP認(rèn)證的用戶名。 - xhr:指定生成XMLHttpRequest對(duì)象時(shí)的回調(diào)函數(shù)。 這些參數(shù)之中,url可以獨(dú)立出來(lái),作為ajax方法的第一個(gè)參數(shù)。也就是說,上面代碼還可以寫成下面這樣。 ```javascript $.ajax('/url/to/json',{ type: 'GET', dataType: 'json', success: successCallback, error: errorCallback }) ``` 作為向服務(wù)器發(fā)送的數(shù)據(jù),data屬性也可以寫成一個(gè)對(duì)象。 ```javascript $.ajax({ url: '/remote/url', data: { param1: 'value1', param2: 'value2', ... } }); // 相當(dāng)于 $.ajax({ url: '/remote/url?param1=value1&param2=value2...' }}); ``` ### 簡(jiǎn)便寫法 ajax方法還有一些簡(jiǎn)便寫法。 - $.get():發(fā)出GET請(qǐng)求。 - $.getScript():讀取一個(gè)JavaScript腳本文件并執(zhí)行。 - $.getJSON():發(fā)出GET請(qǐng)求,讀取一個(gè)JSON文件。 - $.post():發(fā)出POST請(qǐng)求。 - $.fn.load():讀取一個(gè)html文件,并將其放入當(dāng)前元素之中。 一般來(lái)說,這些簡(jiǎn)便方法依次接受三個(gè)參數(shù):url、數(shù)據(jù)、成功時(shí)的回調(diào)函數(shù)。 **(1)$.get(),$.post()** 這兩個(gè)方法分別對(duì)應(yīng)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方法的參數(shù)相同,第一個(gè)參數(shù)是服務(wù)器網(wǎng)址,該參數(shù)是必需的,其他參數(shù)都是可選的。第二個(gè)參數(shù)是發(fā)送給服務(wù)器的數(shù)據(jù),第三個(gè)參數(shù)是操作成功后的回調(diào)函數(shù)。 上面的post方法對(duì)應(yīng)的ajax寫法如下。 ```javascript $.ajax({ type: 'POST', url: '/data/save', data: {name: 'Rebecca'}, dataType: 'json', success: function (resp){ console.log(JSON.parse(resp)); } }); ``` **(2)$.getJSON()** ajax方法的另一個(gè)簡(jiǎn)便寫法是getJSON方法。當(dāng)服務(wù)器端返回JSON格式的數(shù)據(jù),可以用這個(gè)方法代替$.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方法用于從服務(wù)器端加載一個(gè)腳本文件。 ```javascript $.getScript('/static/js/myScript.js', function() { functionFromMyScript(); }); ``` 上面代碼先從服務(wù)器加載myScript.js腳本,然后在回調(diào)函數(shù)中執(zhí)行該腳本提供的函數(shù)。 getScript的回調(diào)函數(shù)接受三個(gè)參數(shù),分別是腳本文件的內(nèi)容,HTTP響應(yīng)的狀態(tài)信息和ajax對(duì)象實(shí)例。 ```javascript $.getScript( "ajax/test.js", function (data, textStatus, jqxhr){ console.log( data ); // test.js的內(nèi)容 console.log( textStatus ); // Success console.log( jqxhr.status ); // 200 }); ``` getScript是ajax方法的簡(jiǎn)便寫法,因此返回的是一個(gè)deferred對(duì)象,可以使用deferred接口。 ```javascript jQuery.getScript("/path/to/myscript.js") .done(function() { // ... }) .fail(function() { // ... }); ``` **(4)$.fn.load()** $.fn.load不是jQuery的工具方法,而是定義在jQuery對(duì)象實(shí)例上的方法,用于獲取服務(wù)器端的HTML文件,將其放入當(dāng)前元素。由于該方法也屬于ajax操作,所以放在這里一起講。 ```javascript $('#newContent').load('/foo.html'); ``` $.fn.load方法還可以指定一個(gè)選擇器,將遠(yuǎn)程文件中匹配選擇器的部分,放入當(dāng)前元素,并指定操作完成時(shí)的回調(diào)函數(shù)。 ```javascript $('#newContent').load('/foo.html #myDiv h1:first', function(html) { console.log('內(nèi)容更新!'); }); ``` 上面代碼只加載foo.html中匹配“#myDiv h1:first”的部分,加載完成后會(huì)運(yùn)行指定的回調(diào)函數(shù)。 ```javascript $('#main-menu a').click(function(event) { event.preventDefault(); $('#main').load(this.href + ' #main *'); }); ``` 上面的代碼將指定網(wǎng)頁(yè)中匹配“#main *”,加載入當(dāng)前的main元素。星號(hào)表示匹配main元素包含的所有子元素,如果不加這個(gè)星號(hào),就會(huì)加載整個(gè)main元素(包括其本身),導(dǎo)致一個(gè)main元素中還有另一個(gè)main元素。 load方法可以附加一個(gè)字符串或?qū)ο笞鳛閰?shù),一起向服務(wù)器提交。如果是字符串,則采用GET方法提交;如果是對(duì)象,則采用POST方法提交。 ```javascript $( "#feeds" ).load( "feeds.php", { limit: 25 }, function() { console.log( "已經(jīng)載入" ); }); ``` 上面代碼將`{ limit: 25 }`通過POST方法向服務(wù)器提交。 load方法的回調(diào)函數(shù),可以用來(lái)向用戶提示操作已經(jīng)完成。 ```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('出錯(cuò)了!'); } }); }); ``` ### Ajax事件 jQuery提供以下一些方法,用于指定特定的AJAX事件的回調(diào)函數(shù)。 - .ajaxComplete():ajax請(qǐng)求完成。 - .ajaxError():ajax請(qǐng)求出錯(cuò)。 - .ajaxSend():ajax請(qǐng)求發(fā)出之前。 - .ajaxStart():第一個(gè)ajax請(qǐng)求開始發(fā)出,即沒有還未完成ajax請(qǐng)求。 - .ajaxStop():所有ajax請(qǐng)求完成之后。 - .ajaxSuccess():ajax請(qǐng)求成功之后。 下面是示例。 ```javascript $('#loading_indicator') .ajaxStart(function (){$(this).show();}) .ajaxStop(function (){$(this).hide();}); ``` 下面是處理Ajax請(qǐng)求出錯(cuò)(返回404或500錯(cuò)誤)的例子。 ```javascript $(document).ajaxError(function (e, xhr, settings, error) { console.log(error); }); ``` ### 返回值 ajax方法返回的是一個(gè)deferred對(duì)象,可以用then方法為該對(duì)象指定回調(diào)函數(shù)(詳細(xì)解釋參見《deferred對(duì)象》一節(jié))。 ```javascript $.ajax({ url: '/data/people.json', dataType: 'json' }).then(function (resp){ console.log(resp.people); }) ``` ### JSONP 由于瀏覽器存在“同域限制”,ajax方法只能向當(dāng)前網(wǎng)頁(yè)所在的域名發(fā)出HTTP請(qǐng)求。但是,通過在當(dāng)前網(wǎng)頁(yè)中插入script元素(\<script\>),可以向不同的域名發(fā)出GET請(qǐng)求,這種變通方法叫做JSONP(JSON with Padding)。 ajax方法可以發(fā)出JSONP請(qǐng)求,方法是在對(duì)象參數(shù)中指定dataType為JSONP。 ```javascript $.ajax({ url: '/data/search.jsonp', data: {q: 'a'}, dataType: 'jsonp', success: function(resp) { $('#target').html('Results: ' + resp.results.length); } });) ``` JSONP的通常做法是,在所要請(qǐng)求的URL后面加在回調(diào)函數(shù)的名稱。ajax方法規(guī)定,如果所請(qǐng)求的網(wǎng)址以類似“callback=?”的形式結(jié)尾,則自動(dòng)采用JSONP形式。所以,上面的代碼還可以寫成下面這樣。 ```javascript $.getJSON('/data/search.jsonp?q=a&callback=?', function(resp) { $('#target').html('Results: ' + resp.results.length); } ); ``` ### 文件上傳 假定網(wǎng)頁(yè)有一個(gè)文件控件。 ```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 }); ``` 上面代碼是將文件作為表單數(shù)據(jù)發(fā)送。除此之外,也可以直接發(fā)送文件。 ```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插件開發(fā)</h2> 所謂“插件”,就是用戶自己新增的jQuery實(shí)例對(duì)象的方法。由于該方法要被所有實(shí)例共享,所以只能定義在jQuery構(gòu)造函數(shù)的原型對(duì)象(prototype)之上。對(duì)于用戶來(lái)說,把一些常用的操作封裝成插件(plugin),使用起來(lái)會(huì)非常方便。 ## 插件的編寫 ### 原理 本質(zhì)上,jQuery插件是定義在jQuery構(gòu)造函數(shù)的prototype對(duì)象上面的一個(gè)方法,這樣做就能使得所有jQuery對(duì)象的實(shí)例都能共享這個(gè)方法。因?yàn)閖Query構(gòu)造函數(shù)的prototype對(duì)象被簡(jiǎn)寫成jQuery.fn對(duì)象,所以插件采用下面的方法定義。 ```javascript jQuery.fn.myPlugin = function() { // Do your awesome plugin stuff here }; ``` 更好的做法是采用下面的寫法,這樣就能在函數(shù)體內(nèi)自由使用美元符號(hào)($)。 ```javascript ;(function ($){ $.fn.myPlugin = function (){ // Do your awesome plugin stuff here }; })(jQuery); ``` 上面代碼的最前面有一個(gè)分號(hào),這是為了防止多個(gè)腳本文件合并時(shí),其他腳本的結(jié)尾語(yǔ)句沒有添加分號(hào),造成運(yùn)行時(shí)錯(cuò)誤。 有時(shí),還可以把頂層對(duì)象(window)作為參數(shù)輸入,這樣可以加快代碼的執(zhí)行速度和執(zhí)行更有效的最小化操作。 ```javascript ;(function ($, window) { $.fn.myPlugin = function() { // Do your awesome plugin stuff here }; }(jQuery, window)); ``` 需要注意的是,在插件內(nèi)部,this關(guān)鍵字指的是jQuery對(duì)象的實(shí)例。而在一般的jQuery回調(diào)函數(shù)之中,this關(guān)鍵字指的是DOM對(duì)象。 ```javascript (function ($){ $.fn.maxHeight = function (){ var max = 0; // 下面這個(gè)this,指的是jQuery對(duì)象實(shí)例 this.each(function() { // 回調(diào)函數(shù)內(nèi)部,this指的是DOM對(duì)象 max = Math.max(max, $(this).height()); }); return max; }; })(jQuery); ``` 上面這個(gè)maxHeight插件的作用是,返回一系列DOM對(duì)象中高度最高的那個(gè)對(duì)象的高度。 大多數(shù)情況下,插件應(yīng)該返回jQuery對(duì)象,這樣可以保持鏈?zhǔn)讲僮鳌? ```javascript (function ($){ $.fn.greenify = function (){ this.css("color", "green"); return this; }; })(jQuery); $("a").greenify().addClass("greenified"); ``` 上面代碼返回this對(duì)象,即jQuery對(duì)象實(shí)例,所以接下來(lái)可以采用鏈?zhǔn)讲僮鳌? 對(duì)于包含多個(gè)jQuery對(duì)象的結(jié)果集,可以采用each方法,進(jìn)行處理。 ```javascript $.fn.myNewPlugin = function() { return this.each(function() { // 處理每個(gè)對(duì)象 }); }; ``` 插件可以接受一個(gè)屬性對(duì)象參數(shù)。 ```javascript (function ($){ $.fn.tooltip = function (options){ var settings = $.extend( { 'location' : 'top', 'background-color' : 'blue' }, options); return this.each(function (){ // 填入插件代碼 }); }; })(jQuery); ``` 上面代碼使用extend方法,為參數(shù)對(duì)象設(shè)置屬性的默認(rèn)值。 ### 偵測(cè)環(huán)境 jQuery逐漸從瀏覽器環(huán)境,變?yōu)橐部梢杂糜诜?wù)器環(huán)境。所以,定義插件的時(shí)候,最好首先偵測(cè)一下運(yùn)行環(huán)境。 ```javascript if (typeof module === "object" && typeof module.exports === "object") { // CommonJS版本 } else { // 瀏覽器版本 } ``` ## 實(shí)例 下面是一個(gè)將a元素的href屬性添加到網(wǎng)頁(yè)的插件。 ```javascript (function($){ $.fn.showLinkLocation = function() { return this.filter('a').append(function(){ return ' (' + this.href + ')'; }); }; }(jQuery)); // 用法 $('a').showLinkLocation(); ``` 從上面的代碼可以看到,插件的開發(fā)和使用都非常簡(jiǎn)單。 ## 插件的發(fā)布 編寫插件以后,可以將它發(fā)布到[jQuery官方網(wǎng)站](http://plugins.jquery.com/)上。 首先,編寫一個(gè)插件的信息文件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" } } ``` 上面是一個(gè)插件信息文件的實(shí)例。 然后,將代碼文件發(fā)布到Github,在設(shè)置頁(yè)面點(diǎn)擊“Service Hooks/WebHook URLs”選項(xiàng),填入網(wǎng)址http://plugins.jquery.com/postreceive-hook,再點(diǎn)擊“Update Settings”進(jìn)行保存。 最后,為代碼加上版本,push到github,你的插件就會(huì)加入jQuery官方插件庫(kù)。 ```javascript git tag 0.1.0 git push origin --tags ``` 以后,你要發(fā)布新版本,就做一個(gè)新的tag。 <h2 id="11.4">jQuery.Deferred對(duì)象</h2> ## 概述 deferred對(duì)象代表了將要完成的某種操作,并提供了一些方法,幫助用戶使用。它是jQuery對(duì)Promises接口的實(shí)現(xiàn)。jQuery的所有Ajax操作函數(shù),默認(rèn)返回的就是一個(gè)deferred對(duì)象。 簡(jiǎn)單說,Promises是異步操作的通用接口,扮演代理人(proxy)的角色,將異步操作包裝成具有同步操作特性的特殊對(duì)象。異步操作的典型例子就是Ajax操作、網(wǎng)頁(yè)動(dòng)畫、web worker等等。 由于JavaScript單線程的特點(diǎn),如果某個(gè)操作耗時(shí)很長(zhǎng),其他操作就必需排隊(duì)等待。為了避免整個(gè)程序失去響應(yīng),通常的解決方法是將那些排在后面的操作,寫成“回調(diào)函數(shù)”(callback)的形式。這樣做雖然可以解決問題,但是有一些顯著缺點(diǎn): - 回調(diào)函數(shù)往往寫成函數(shù)參數(shù)的形式,形成所謂的“持續(xù)傳遞風(fēng)格”(即參數(shù)就是下一步操作,Continuation-passing style),導(dǎo)致函數(shù)的輸入和輸出非常混亂,整個(gè)程序的可閱讀性差; - 回調(diào)函數(shù)往往只能指定一個(gè),如果有多個(gè)操作,就需要改寫回調(diào)函數(shù)。 - 除了正常的報(bào)錯(cuò)機(jī)制,錯(cuò)誤還可能通過回調(diào)函數(shù)的形式返回,增加了除錯(cuò)和調(diào)試的難度。 - 正常的函數(shù)輸入和輸出可以區(qū)分得很清楚,回調(diào)函數(shù)使得函數(shù)的輸出不再重要。 Promises就是為了解決這些問題而提出的,它的主要目的就是取代回調(diào)函數(shù),成為非同步操作的解決方案。它的核心思想就是讓非同步操作返回一個(gè)對(duì)象,其他操作都針對(duì)這個(gè)對(duì)象來(lái)完成。比如,假定ajax操作返回一個(gè)Promise對(duì)象。 ```javascript var promise = get('http://www.example.com'); ``` 然后,Promise對(duì)象有一個(gè)then方法,可以用來(lái)指定回調(diào)函數(shù)。一旦非同步操作完成,就調(diào)用指定的回調(diào)函數(shù)。 ```javascript promise.then(function (content) { console.log(content) }) ``` 可以將上面兩段代碼合并起來(lái),這樣程序的流程看得更清楚。 ```javascript get('http://www.example.com').then(function (content) { console.log(content) }) ``` 在1.7版之前,jQuery的Ajax操作采用回調(diào)函數(shù)。 ```javascript $.ajax({ url:"/echo/json/", success: function(response) { console.info(response.name); } }); ``` 1.7版之后,Ajax操作直接返回Promise對(duì)象,這意味著可以用then方法指定回調(diào)函數(shù)。 ```javascript $.ajax({ url: "/echo/json/", }).then(function (response) { console.info(response.name); }); ``` ## deferred對(duì)象的方法 ### 基本用法 **(1)生成deferred對(duì)象** 第一步是通過$.Deferred()方法,生成一個(gè)deferred對(duì)象。 ```javascript var deferred = $.Deferred(); ``` **(2)deferred對(duì)象的狀態(tài)** deferred對(duì)象有三種狀態(tài)。 - pending:表示操作還沒有完成。 - resolved:表示操作成功。 - rejected:表示操作失敗。 state方法用來(lái)返回deferred對(duì)象當(dāng)前狀態(tài)。 ```javascript $.Deferred().state() // 'pending' $.Deferred().resolve().state() // 'resolved' $.Deferred().reject().state() // 'rejected' ``` **(3)改變狀態(tài)的方法** resolve方法將deferred對(duì)象的狀態(tài)從pending改為resolved,reject方法則將狀態(tài)從pending改為rejected。 ```javascript var deferred = $.Deferred(); deferred.resolve("hello world"); ``` resolve方法的參數(shù),用來(lái)傳遞給回調(diào)函數(shù)。 **(4)綁定回調(diào)函數(shù)** deferred對(duì)象在狀態(tài)改變時(shí),會(huì)觸發(fā)回調(diào)函數(shù)。 done方法指定狀態(tài)變?yōu)閞esolved(操作成功)時(shí)的回調(diào)函數(shù);fail方法指定狀態(tài)變?yōu)閞ejected(操作失?。r(shí)的回調(diào)函數(shù);always方法指定,不管狀態(tài)變?yōu)閞esolved或rejected,都會(huì)觸發(fā)的方法。 ```javascript var deferred = $.Deferred(); deferred.done(function(value) { console.log(value); }).resolve('hello world'); // hello world ``` 上述三種方法都返回的原有的deferred對(duì)象,因此可以采用鏈?zhǔn)綄懛?,在后面再鏈接別的方法(包括done和fail在內(nèi))。 ```javascript $.Deferred().done(f1).fail(f2).always(f3); ``` ### notify() 和 progress() progress()用來(lái)指定一個(gè)回調(diào)函數(shù),當(dāng)調(diào)用notify()方法時(shí),該回調(diào)函數(shù)將執(zhí)行。它的用意是提供一個(gè)接口,使得在非同步操作執(zhí)行過程中,可以執(zhí)行某些操作,比如定期返回進(jìn)度條的進(jìn)度。 ```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方法的作用也是指定回調(diào)函數(shù),它可以接受三個(gè)參數(shù),也就是三個(gè)回調(diào)函數(shù)。第一個(gè)參數(shù)是resolve時(shí)調(diào)用的回調(diào)函數(shù)(相當(dāng)于done方法),第二個(gè)參數(shù)是reject時(shí)調(diào)用的回調(diào)函數(shù)(相當(dāng)于fail方法),第三個(gè)參數(shù)是progress()方法調(diào)用的回調(diào)函數(shù)。 ```javascript deferred.then( doneFilter [, failFilter ] [, progressFilter ] ) ``` **(2)返回值** 在jQuery 1.8之前,then()只是.done().fail()寫法的語(yǔ)法糖,兩種寫法是等價(jià)的。在jQuery 1.8之后,then()返回一個(gè)新的promise對(duì)象,而done()返回的是原有的deferred對(duì)象。如果then()指定的回調(diào)函數(shù)有返回值,該返回值會(huì)作為參數(shù),傳入后面的回調(diào)函數(shù)。 ```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版本之前,上面代碼的結(jié)果是: ```javascript result = 2 result = 2 result = 2 ``` 在jQuery 1.8版本之后,返回結(jié)果是 ```javascript result = 2 result = 6 result = NaN ``` 這一點(diǎn)需要特別引起注意。 ```javascript $.ajax( url1, { dataType: "json" } ) .then(function( data ) { return $.ajax( url2, { data: { user: data.userId } } ); }).done(function( data ) { // 從url2獲取的數(shù)據(jù) }); ``` 上面代碼最后那個(gè)done方法,處理的是從url2獲取的數(shù)據(jù),而不是從url1獲取的數(shù)據(jù)。 **(3)對(duì)返回值的修改** 利用then()會(huì)修改返回值這個(gè)特性,我們可以在調(diào)用其他回調(diào)函數(shù)之前,對(duì)前一步操作返回的值進(jìn)行處理。 ```javascript var post = $.post("/echo/json/") .then(function(p){ return p.firstName; }); post.done(function(r){ console.log(r); }); ``` 上面代碼先使用then()方法,從返回的數(shù)據(jù)中取出所需要的字段(firstName),所以后面的操作就可以只處理這個(gè)字段了。 有時(shí),Ajax操作返回json字符串里面有一個(gè)error屬性,表示發(fā)生錯(cuò)誤。這個(gè)時(shí)候,傳統(tǒng)的方法只能是通過done()來(lái)判斷是否發(fā)生錯(cuò)誤。通過then()方法,可以讓deferred對(duì)象調(diào)用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"); }); ``` 上面代碼中,不管是通信出錯(cuò),或者服務(wù)器返回一個(gè)錯(cuò)誤,都會(huì)調(diào)用reject方法,返回一個(gè)新的deferred對(duì)象,狀態(tài)為rejected,因此就會(huì)觸發(fā)fail方法指定的回調(diào)函數(shù)。 關(guān)于error的處理,jQuery的deferred對(duì)象與其他實(shí)現(xiàn)Promises規(guī)范的函數(shù)庫(kù)有一個(gè)重大不同。就是說,如果deferred對(duì)象執(zhí)行過程中,拋出一個(gè)非Promises對(duì)象的錯(cuò)誤,那么將不會(huì)被后繼的then方法指定的rejected回調(diào)函數(shù)捕獲,而會(huì)一直傳播到應(yīng)用程序?qū)用?。為了代碼行為與Promises規(guī)范保持一致,建議出錯(cuò)時(shí),總是使用reject方法返回錯(cuò)誤。 ```javascript d = $.Deferred() d.then(function(){ throw new Error('err') }).fail(function(){ console.log('fail') }) d.resolve() // Error: err ``` 上面代碼中,then的回調(diào)函數(shù)拋出一個(gè)錯(cuò)誤,按照Promises規(guī)范,應(yīng)該被fail方法的回調(diào)函數(shù)捕獲,但是jQuery的部署是上升到應(yīng)用程序的層面。 **(4)回調(diào)函數(shù)的返回值** 如果回調(diào)函數(shù)返回deferred對(duì)象,則then方法的返回值將是對(duì)應(yīng)這個(gè)返回值的promise對(duì)象。 ```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方法的回調(diào)函數(shù),正常情況下只能接受一個(gè)參數(shù)。但是由于then方法的回調(diào)函數(shù),返回一個(gè)when方法生成的deferred對(duì)象,導(dǎo)致它可以接受兩個(gè)參數(shù)。 ### pipe方法 pipe方法接受一個(gè)函數(shù)作為參數(shù),表示在調(diào)用then方法、done方法、fail方法、always方法指定的回調(diào)函數(shù)之前,先運(yùn)行pipe方法指定的回調(diào)函數(shù)。它通常用來(lái)對(duì)服務(wù)器返回的數(shù)據(jù)做初步處理。 ### 與Promise A+規(guī)格的差異 Promise事實(shí)上的標(biāo)準(zhǔn)是社區(qū)提出的Promise A+規(guī)格,jQuery的實(shí)現(xiàn)并不完全符合Promise A+,主要是對(duì)錯(cuò)誤的處理。 ```javascript var promise2 = promise1.then(function () { throw new Error("boom!"); }); ``` 上面代碼在回調(diào)函數(shù)中拋出一個(gè)錯(cuò)誤,Promise A+規(guī)定此時(shí)Promise實(shí)例的狀態(tài)變?yōu)閞eject,該錯(cuò)誤被下一個(gè)catch方法指定的回調(diào)函數(shù)捕獲。但是,jQuery的Deferred對(duì)象此時(shí)不會(huì)改變狀態(tài),亦不會(huì)觸發(fā)回調(diào)函數(shù),該錯(cuò)誤一般情況下會(huì)被window.onerror捕獲。換句話說,在Deferred對(duì)象中,總是必須使用reject方法來(lái)改變狀態(tài)。 ## promise對(duì)象 **(1)概念** 一般情況下,從外部改變第三方完成的異步操作(比如Ajax)的狀態(tài)是毫無(wú)意義的。為了防止用戶這樣做,可以在deferred對(duì)象的基礎(chǔ)上,返回一個(gè)針對(duì)它的promise對(duì)象。 簡(jiǎn)單說,promise對(duì)象就是不能改變狀態(tài)的deferred對(duì)象,也就是deferred的只讀版。或者更通俗地理解成,promise是一個(gè)對(duì)將要完成的任務(wù)的承諾,排除了其他人破壞這個(gè)承諾的可能性,只能等待承諾方給出結(jié)果。 你可以通過promise對(duì)象,為原始的deferred對(duì)象添加回調(diào)函數(shù),查詢它的狀態(tài),但是無(wú)法改變它的狀態(tài),也就是說promise對(duì)象不允許你調(diào)用resolve和reject方法。 **(2)生成promise對(duì)象** deferred對(duì)象的promise方法,用來(lái)生成對(duì)應(yīng)的promise對(duì)象。 ```javascript function getPromise(){ return $.Deferred().promise(); } try{ getPromise().resolve("a"); } catch(err) { console.log(err); } // TypeError ``` 上面代碼對(duì)promise對(duì)象,調(diào)用resolve方法,結(jié)果報(bào)錯(cuò)。 jQuery的`ajax()`方法返回的就是一個(gè)Promise對(duì)象。此外,Animation類操作也可以使用`promise`方法。 ```javascript $('body').toggle('blinds').promise().then( function(){ $('body').toggle('blinds') } ) ``` ## 輔助方法 deferred對(duì)象還有一系列輔助方法,使它更方便使用。 ### `$.when()`方法 `$.when()`接受多個(gè)deferred對(duì)象作為參數(shù),當(dāng)它們?nèi)窟\(yùn)行成功后,才調(diào)用resolved狀態(tài)的回調(diào)函數(shù),但只要其中有一個(gè)失敗,就調(diào)用rejected狀態(tài)的回調(diào)函數(shù)。它相當(dāng)于將多個(gè)非同步操作,合并成一個(gè)。實(shí)質(zhì)上,when方法為多個(gè)deferred對(duì)象,返回一個(gè)單一的promise對(duì)象。 ```javascript $.when( $.ajax( "/main.php" ), $.ajax( "/modules.php" ), $.ajax( "/lists.php" ) ).then(successFunc, failureFunc); ``` 上面代碼表示,要等到三個(gè)ajax操作都結(jié)束以后,才執(zhí)行then方法指定的回調(diào)函數(shù)。 when方法里面要執(zhí)行多少個(gè)操作,回調(diào)函數(shù)就有多少個(gè)參數(shù),對(duì)應(yīng)前面每一個(gè)操作的返回結(jié)果。 ```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); }); ``` 上面代碼的回調(diào)函數(shù)有三個(gè)參數(shù),resp1、resp2和resp3,依次對(duì)應(yīng)前面三個(gè)ajax操作的返回結(jié)果。 如果when方法的參數(shù)不是deferred或promise對(duì)象,則直接作為回調(diào)函數(shù)的參數(shù)。 ```javascript d = $.Deferred() $.when(d, 'World').done(function (s1, s2){ console.log(s1); console.log(s2); }) d.resolve('Hello') // Hello // World ``` 上面代碼中,when的第二個(gè)參數(shù)是一個(gè)字符串,則直接作為回調(diào)函數(shù)的第二個(gè)參數(shù)。 此外,如果when方法的參數(shù)都不是deferred或promise對(duì)象,那么when方法的回調(diào)函數(shù)將立即運(yùn)行。 ## 使用實(shí)例 ### wait方法 我們可以用deferred對(duì)象寫一個(gè)wait方法,表示等待多少毫秒后再執(zhí)行。 ```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方法的基礎(chǔ)上,還可以改寫setTimeout方法,讓其返回一個(gè)deferred對(duì)象。 ```javascript function doSomethingLater(fn, time) { var dfd = $.Deferred(); setTimeout(function() { dfd.resolve(fn()); }, time || 0); return dfd.promise(); } var promise = doSomethingLater(function (){ console.log( '已經(jīng)延遲執(zhí)行' ); }, 100); ``` ### 自定義操作使用deferred接口 我們可以利用deferred接口,使得任意操作都可以用done()和fail()指定回調(diào)函數(shù)。 ```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對(duì)象的另一個(gè)優(yōu)勢(shì)是可以附加多個(gè)回調(diào)函數(shù)。下面的例子使用了上面所改寫的setTimeout函數(shù)。 ```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工具庫(kù)。據(jù)[統(tǒng)計(jì)](http://w3techs.com/technologies/details/js-jquery/all/all),目前全世界57.3%的網(wǎng)站使用它。也就是說,10個(gè)網(wǎng)站里面,有6個(gè)使用jQuery。如果只考察使用工具庫(kù)的網(wǎng)站,這個(gè)比例就會(huì)上升到驚人的91.7%。 jQuery如此受歡迎,以至于有被濫用的趨勢(shì)。許多開發(fā)者不管什么樣的項(xiàng)目,都一股腦使用jQuery。但是,jQuery本質(zhì)只是一個(gè)中間層,提供一套統(tǒng)一易用的DOM操作接口,消除瀏覽器之間的差異。多了這一層中間層,操作的性能和效率多多少少會(huì)打一些折扣。 2006年,jQuery誕生的時(shí)候,主要是為了解決IE6與標(biāo)準(zhǔn)的不兼容問題。如今的[情況](http://en.wikipedia.org/wiki/Usage_share_of_web_browsers)已經(jīng)發(fā)生了很大的變化。IE的市場(chǎng)份額不斷下降,以ECMAScript為基礎(chǔ)的JavaScript標(biāo)準(zhǔn)語(yǔ)法,正得到越來(lái)越廣泛的支持,不同瀏覽器對(duì)標(biāo)準(zhǔn)的支持越來(lái)越好、越來(lái)越趨同。開發(fā)者直接使用JavaScript標(biāo)準(zhǔn)語(yǔ)法,就能同時(shí)在各大瀏覽器運(yùn)行,不再需要通過jQuery獲取兼容性。 另一方面,jQuery臃腫的[體積](http://mathiasbynens.be/demo/jquery-size)也讓人頭痛不已。jQuery 2.0的原始大小為235KB,優(yōu)化后為81KB;如果是支持IE6、7、8的jQuery 1.8.3,原始大小為261KB,優(yōu)化后為91KB。即使有CDN,瀏覽器加載這樣大小的腳本,也會(huì)產(chǎn)生不小的開銷。 所以,對(duì)于一些不需要支持老式瀏覽器的小型項(xiàng)目來(lái)說,不使用jQuery,直接使用DOM原生接口,可能是更好的選擇。開發(fā)者有必要了解,jQuery的一些常用操作所對(duì)應(yīng)的DOM寫法。而且,理解jQuery背后的原理,會(huì)幫助你更好地使用jQuery。要知道有一種極端的說法是,如果你不理解一樣?xùn)|西,就不要使用它。 下面就探討如何用JavaScript標(biāo)準(zhǔn)語(yǔ)法,取代jQuery的一些主要功能,做到j(luò)Query-free。 ## 選取DOM元素 jQuery的核心是通過各種選擇器,選中DOM元素,可以用querySelectorAll方法模擬這個(gè)功能。 ```javascript var $ = document.querySelectorAll.bind(document); ``` 這里需要注意的是,querySelectorAll方法返回的是NodeList對(duì)象,它很像數(shù)組(有數(shù)字索引和length屬性),但不是數(shù)組,不能使用pop、push等數(shù)組特有方法。如果有需要,可以考慮將Nodelist對(duì)象轉(zhuǎn)為數(shù)組。 ```javascript myList = Array.prototype.slice.call(myNodeList); ``` ## DOM操作 DOM本身就具有很豐富的操作方法,可以取代jQuery提供的操作方法。 獲取父元素。 ```javascript // jQuery寫法 $("#elementID").parent() // DOM寫法 document.getElementById("elementID").parentNode ``` 獲取下一個(gè)同級(jí)元素。 ```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) ``` ## 事件的監(jiān)聽 jQuery使用on方法,監(jiān)聽事件和綁定回調(diào)函數(shù)。 ```javascript $('button').on('click', function(){ ajax( ... ); }); ``` 完全可以自己定義on方法,將它指向addEventListener方法。 ```javascript Element.prototype.on = Element.prototype.addEventListener; ``` 為了使用方便,可以在NodeList對(duì)象上也部署這個(gè)方法。 ```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; ``` ## 事件的觸發(fā) jQuery的trigger方法則需要單獨(dú)部署,相對(duì)復(fù)雜一些。 ```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對(duì)象上也部署這個(gè)方法。 ```javascript NodeList.prototype.trigger = function (event) { []['forEach'].call(this, function (el) { el['trigger'](event); }); return this; }; ``` ## `$(document).ready` DOM加載完成,會(huì)觸發(fā)DOMContentLoaded事件,等同于jQuery的`$(document).ready`方法。 ```javascript document.addEventListener("DOMContentLoaded", function() { // ... }); ``` 不過,目前的最佳實(shí)踐,是將JavaScript腳本文件都放在頁(yè)面底部加載。這樣的話,其實(shí)$(document).ready方法(可以簡(jiǎn)寫為$(function))已經(jīng)不必要了,因?yàn)榈鹊竭\(yùn)行的時(shí)候,DOM對(duì)象已經(jīng)生成了。 ## attr方法 jQuery使用attr方法,讀寫網(wǎng)頁(yè)元素的屬性。 ```javascript $("#picture").attr("src", "http://url/to/image") ``` DOM提供getAttribute和setAttribute方法讀寫元素屬性。 ```javascript imgElement.setAttribute("src", "http://url/to/image") ``` DOM還允許直接讀取屬性值,寫法要簡(jiǎn)潔許多。 ```javascript imgElement.src = "http://url/to/image"; ``` > 需要注意的是,文本框元素(input)的this.value返回的是輸入框中的值,鏈接元素(a標(biāo)簽)的this.href返回的是絕對(duì)URL。如果需要用到這兩個(gè)網(wǎng)頁(yè)元素的屬性準(zhǔn)確值,可以用this.getAttribute('value')和this.getAttibute('href')。 ## addClass方法 jQuery的addClass方法,用于為DOM元素添加一個(gè)class。 ```javascript $('body').addClass('hasJS'); ``` DOM元素本身有一個(gè)可讀寫的className屬性,可以用來(lái)操作class。 ```javascript document.body.className = 'hasJS'; // or document.body.className += ' hasJS'; ``` HTML 5還提供一個(gè)classList對(duì)象,功能更強(qiáng)大(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方法,用來(lái)設(shè)置網(wǎng)頁(yè)元素的樣式。 ```javascript $(node).css( "color", "red" ); ``` DOM元素有一個(gè)style屬性,可以直接操作。 ```javascript element.style.color = "red”;; // or element.style.cssText += 'color:red'; ``` ## 數(shù)據(jù)儲(chǔ)存 jQuery對(duì)象可以儲(chǔ)存數(shù)據(jù)。 ```javascript $("body").data("foo", 52); ``` HTML 5有一個(gè)dataset對(duì)象,也有類似的功能(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 ); }); ``` 我們自定義一個(gè)ajax函數(shù),簡(jiǎn)單模擬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); } ``` 使用的時(shí)候,除了網(wǎng)址,還需要傳入一個(gè)自己構(gòu)造的option對(duì)象。 ```javascript ajax('/foo', { method: 'GET', success: function(response){ console.log(response); }, error: function(response){ console.log(response); } }); ``` ## 動(dòng)畫 jQuery的animate方法,用于生成動(dòng)畫效果。 ```javascript $foo.animate('slow', { x: '+=10px' }) ``` jQuery的動(dòng)畫效果,很大部分基于DOM。但是目前,CSS 3的動(dòng)畫遠(yuǎn)比DOM強(qiáng)大,所以可以把動(dòng)畫效果寫進(jìn)CSS,然后通過操作DOM元素的class,來(lái)展示動(dòng)畫。 ```javascript foo.classList.add('animate') ``` 如果需要對(duì)動(dòng)畫使用回調(diào)函數(shù),CSS 3也定義了相應(yīng)的事件。 ```javascript el.addEventListener("webkitTransitionEnd", transitionEnded); el.addEventListener("transitionend", transitionEnded); ``` ## 替代方案 由于jQuery體積過大,替代方案層出不窮。 其中,最有名的是[zepto.js](http://zeptojs.com/)。它的設(shè)計(jì)目標(biāo)是以最小的體積,做到最大兼容jQuery的API。它的1.0版的原始大小是55KB,優(yōu)化后是29KB,gzip壓縮后為10KB。 如果不求最大兼容,只希望模擬jQuery的基本功能。那么,[min.js](https://github.com/remy/min.js)優(yōu)化后只有200字節(jié),而[dolla](https://github.com/lelandrichardson/dolla)優(yōu)化后是1.7KB。 此外,jQuery本身也采用模塊設(shè)計(jì),可以只選擇使用自己需要的模塊。具體做法參見jQuery的[github網(wǎng)站](https://github.com/jquery/jquery),或者使用專用的[Web界面](http://projects.jga.me/jquery-builder/)。
精品一区二区三区四区日产| av青青草原在线观看| 中文字幕一区二区不卡顿| 88在线观看91蜜桃国自产| 国产三级久久久999111| 中文字幕的不卡人妻| 国产精品首页在线播放| 天天色天天干天天舔| 国内精品久久久久久久久久清纯| 日韩三级伦理片免费看| 清纯唯美激情五月| 日韩不卡一级成人免费视频| 久久久亚洲精品久久仙| 欧美爱情动作片在线一区| 亚洲久久久久久久人妻| 真实玩弄白嫩丰满人妻少妇三级| 五月婷婷激情四射综合网| 国产 欧美 日韩 视频| 久久草免费在线视频| 97cao瑟瑟在线观看| 人妻丰满精品一区二区三区| 中文国产成人精品久久一| 五月天色婷婷av在线| 91中日在线中文字幕| 亚洲天堂黄色免费| 日日日日日夜夜夜夜| 午夜精品久久婷婷蜜桃| 久久蜜桃视频亚洲精品| 中文字幕精品久久伊人| 成人午夜激情福利片| 亚洲狠狠久久综合一区| 欧美区 日韩区 国产区| 久久男人精品男人天堂免费视频 | 高潮久久久久久久av免费| av日韩在线有码a区| 亚洲国模私拍视频| 中文字幕av久久爽爽| 久久久久久久精品综合| 久久久精品蜜桃在线| 少妇高潮一区二区三区99欧美| 91偷拍老熟女露脸合集| 国产成人免费精品视频大全| 精品中文久久久久久| 久久久久久久黄色午夜精品| 91porny国产九色| 性欧美另类sex极品free| 免费中文字幕在线播放| 日韩亚洲图色在线| 日本女同性恋视频| 1024 国产高清の最新合集| 亚洲中文字幕精品高清| japanese 在线中文字幕| 韩国女团午夜大尺度福利| 91免费av在线观看| 日韩av午夜精品| 久久久精品国产亚洲av网深田| 青青草国产福利视频| 91在线国产九色porny| 中文字幕人妻互换久久视频| 久久久久久精品国产成人| 一本色道久久亚洲综合精品蜜桃| 久久久久久久久久久久久熟女a∨| 成人精品一区一区二区看片| 久久爱免费视频16| 久久亚洲熟妇中文字幕| 欧美日韩成人三级在线| 中文国产成人精品久久一| 日韩a视频在线播放视频| 日本女同性恋视频| 久久精品国产av网| 88在线观看91蜜桃国自产| 日本熟妇人妻在线| 在线免费观看日韩精品视频| 国产精品久久久久久久久粉嫩av| 欧美性久久久久久久久久久| 国产日韩欧美人妻在线观看| 免费中文字幕在线播放| 北条麻妃裤袜一区二区| 久久人妻少妇嫩草av蜜桃动态图| 久久久久女优免费视频| 日本最新中文字幕| 日韩一级黄色大片免费观看| 精品一区二区三区四区日产| 亚洲欧美日韩偷拍综合| 91制片在线观看视频| 在线观看视频 你懂得| 美日韩美女三级电影| 欧美日韩在线卡一卡二卡三| 最新偷窥偷拍免费视频观看| av岛国不卡在线观看| 色综合色综合色综合久久| 99久久窝窝午夜影视| 一区,二区,三区视频| 日韩福利视频在线看| 国产一区二区三区黄色| 亚洲欧美日韩国产精品综合| 国产青青91av在线视频| av成人教育在线播放| 最新偷窥偷拍免费视频观看| 久久国产精品久久伊人麻豆| 亚洲精品久久第一页| 国产五月天在线观看视频| 国产欧美精品久久无广告| 欧美爱情动作片在线一区| 久久av资源男人站| 黄色片一级美女黄色片| 草裙成人精品一区二区三区| 成人黄视频在线播放| av传媒高清影院免费| 国产精品色呦呦视频免费看| 99热在线精品免费观看| 91精品久久久久久久久不卡网站 | av一级二级三级久久久| 亚洲av不卡码在线看| 欧美日韩精品一区电影| 日本女优中文字幕在线| 91在线精品国自产拍| 国产乱子伦视频免费| 日韩a视频在线播放视频| 日本vs欧美一区二区三区| 国产成AV人片在线观看天堂无码| 99久久国语露脸精彩对白| 高清中文字幕乱码在线| 99久久极品蜜桃臀精品久久| 国内精品久久久久久久久久清纯| 国产精品久久久久久久漫画| 亚洲欧洲偷拍自拍| huangse网站在线观看| 久久人搡人人玩人妻精品| 91在线播放视频免费| 欧美日本韩国乱搞视频| 青青草久久大香蕉| 国产亚洲欧美日韩在线首页| 天天操天天干天天色| 国产av在线观看麻豆| 亚洲青青青国产观看视频| 国产精品久久久久久xxx| 蜜桃精品一区二区三区在| 欧美中文字幕久久久| 高潮久久久久久久av免费| 亚洲自拍偷拍 av| 精品久久精品久久久久| 国产一区二区三区女同| 在线观看视频 你懂得| 国产清纯白嫩美女无套| 亚洲国产欧美激情图区| 亚洲阿v天堂久久| 久久精品国产91久久麻豆自制| 亚洲av综合色区手机| 久久精品国产91久久麻豆自制| 欧美日韩亚洲大片在线| 日韩草比网站在线免费观看| 清纯唯美激情五月| 亚洲成人av久久久久| 亚洲情色av网站| 欧美日韩精品综合在线一区二区| 97精品久久人人爽人人爽| 精品激情视频在线免费观看| 91精品一二三区在线观看| 国产精品亚洲精品成人| 亚洲av微乳在线| 久久草免费在线视频| 日韩精品午夜免费观看| 久热中文字幕在线精品| 91麻豆免费国产在线| 久久国产精品——国产精品| 另类h小视频在线观看| 91偷伦一区二区三区蜜臀| 最新精品成人在线| 国产一区二中文字幕在线免费观看| 美女差点操死在线观看| av福利网站在线观看| 777亚洲精品乱码久久| 天堂av最新资源在线| 日本女优中文字幕在线| 丰满人妻一区二区三区av| 欧美日韩精品综合在线一区二区| 日韩在线啊啊啊的视频| 亚洲aⅴ欧美综合一区二区三区 | 久久视频在线免费播放| 免费观看a级在线视频| 蜜臀久久99精品久久久久久久久 | 久久久久久久夜精品精品| 亚洲乳大丰满中文字幕少妇av| av福利网站在线观看| 国产激情综合视频在线观看| 欧美一区二区三区视频午夜| 午夜日韩麻豆福利| 狠狠久久综合丁香777米奇| 亚洲 欧美 日本 国产| 中文字幕+人妻熟女| 日韩av网址在线播放| 四虎网站免费av| 伊人久久热青青草| 日韩亚洲丝袜系列| 午夜日韩麻豆福利| 欧美日韩精品成人在线| 亚洲五月婷婷激情图片| 日韩在线播放视频1区| 国产日韩一区二区三区啪啪啪| 久久伊人精品青青草原| 久久传奇网站一区三区视频| 人妻一区二区中文字幕在线| 制服丝袜亚洲欧美第一| 黑人操日本女人电影| 久久婷婷激情综合色综合俺也去| 日韩久久久三级电影| 秋霞在线观看色哟哟视频| 日韩厕所偷拍美女| 亚洲激情熟女色图| 国产自拍 偷拍 在线| 精品人妻二区中文字幕| 婷婷亚洲免费基地| 日韩欧美激情入口| 久久精品色婷婷国产网站| 日本av熟女人妻| 色综合精品一区二区三区| 色图av亚洲综合| 亚洲国产精品97久久宅男| 久久久久久网站精品免费| 68国产成人综合久久精品| 日本高清不卡视频在线播放| 日韩中文字幕有码人妻在线| 亚洲少妇黄色一级片| 五月激情婷婷网络| 国产av在线观看麻豆| 中文字幕亚洲专区欧美| 2020日韩中文字幕| 牛牛成人手机视频在线| 欧洲美女黑人粗性暴交视| 国产亚洲欧美日韩在线首页| 四十路av熟女俱乐部| 91久久婷婷国产麻豆精品| 国产av一区二区三区天美| 日韩在线啊啊啊的视频| 久久久精品99国产国产精| 91久久久久久久久久粉嫩| 国产日韩欧美成人一二三区| 午夜国产福利电影| huangse网站在线观看| www.199麻豆在线视频| 人妻精品无码一区二区三区| 欧美成人黄色一区二区三区| 亚洲av精品久久久蜜| 久久精品琪琪男人的天堂| 精品久久久久久亚洲国产999| 日韩精品少妇人妻熟女| 亚洲图色91东京热| 成人人妻视频一区二区| 天美麻豆成人av精品小说| 青草青草视频免费2在线观看| 欧美日韩国产色图视频| 人妻夜夜爽天天爽麻豆| 亚洲av精品久久久蜜| www.199麻豆在线视频| 人妻熟女视频免费观看| 2020日本中文字幕| 久久人搡人人玩人妻精品| 日韩美女夜夜爽av| 国产欧美日韩制服丝袜在线| 国产91熟女免费视频| 三级亚洲天堂亚洲天堂| av在线免费播放成人| 激情黄色开心五月天| 日韩美女主播人体视频自拍首页| av在线免费在线观看av| 久久久成人在线免费视频| 精品无人区一区二区三区竹菊| av日韩中文字幕人妻| 91免费av在线观看| 婷婷丁香花五月天| 久久久久在线免费看| 精品人妻久久久久中文字幕19禁| 欧美视频 亚洲视频| 国产亚洲精久久久久久无码色戒| 亚洲综合成人久久av| 中文字幕人妻一区二区在线看| 亚洲不卡一区av| 国产成人无码AA精品一区| 大香蕉影视日本大香蕉97| 国产探花在线播放精品| 88av亚洲精品日韩一区二区| 久久99永久免费看| 亚洲乳大丰满中文字幕少妇av| 91精品欧美久久久久视频| 亚洲欧美日韩偷拍综合| 激情四射五月开心六月婷婷| 91欧美亚洲综合网| 中文字幕一区二区三区中文字幕| 99精品高清视频一区二区| laoyawo老鸭窝在线视频| 亚洲不卡一区av| 久久精品熟女人妻一区二区三区| 蜜桃黄色av网站免费播放| 91嫩草17c欧美国产| 亚洲av色图com| 亚洲 自拍偷拍 欧美| 一区二区 熟女人妻| 欧美日本一道本一区二区| 日韩av在线观看免费看看| 亚洲综合成人久久av| 日韩三级伦理片免费看| 久久久久中文字幕免费久久久久久| 一本一道久久a久久久| 国产精品久久久久久久网站门| 日韩日韩av在线| 篠田优中文字幕在线播| 亚洲天堂网av中文字幕| 国产91久久久久久久| 亚洲综合色激情五月| 99热精品国产影视久久久影院| 91国偷自产一区二区三区偷拍| 成人黄色午夜污网站在线观看| 日韩欧美国产成人一区| 日本美女大学生一区二区三区| 色噜噜狠狠躁夜夜躁| 精品乱码久久久久久蜜臀| 国产日本精品久久久久| 免费观看a级在线视频| 18在线观看久久久麻豆| 国产精品首页在线播放| 91沈先生探花极品在线| 日本美女大学生一区二区三区| 麻豆精品一区在线免费观看| 日韩精品少妇人妻熟女| 日韩一区二区三区水蜜桃| 精品久久久久久18免费网站| 精品99国内中文字幕| 欧美熟女高清视频一区二区| 人妻熟 中文字幕| 208精品福利导航| 日韩中文字幕一区二区三区四区| 午夜国产福利电影| 无码人妻精品一区二区三区久久| 中文字幕一区二区不卡顿| 午夜在线看1000集| 久久在线人妻熟女高清完整版| 日韩av中文字幕一区| 影音在线精品免费国产资源| 亚洲欧美日韩大陆| 熟女人妻之中文字幕 | 国产成AV人片在线观看天堂无码| 伊人久久中文字幕综合观看| 国产 另类 在线 欧美日韩| 欧美日韩少妇一二三| 亚洲人妻中文字幕一区二区| 亚洲情欲大片在线观看 | 欧美一区二区三区激情啪啪啪| 日韩一区二区三区水蜜桃| 国产欧美一区二区二区精品| 欧美中文字幕在线观看视频| 2023亚洲男人的天堂| 国产日韩一区二区三区啪啪啪| 久久久久国产亚洲av麻豆| 成年人在线免费观看黄色片| 亚洲 欧洲 日韩 人妻| 亚洲天堂网av中文字幕| 国产精品首页在线播放| 久久久久久精品国产成人| 韩国女团午夜大尺度福利| 狠狠久久综合丁香777米奇| 日韩av一区中文| 精品一级国产 av| 国产剧情高清在线观看| 欧美一区二区三区成人免费看| 亚洲综合在线伊人| 人妻少妇精品久久人妻| 久久亚洲AV成人无码| 青青草久久大香蕉| 青青青青青欧美在线视频观看 | 亚洲女人老师毛茸茸| 日韩欧美在线视频第一页| 91沈先生探花极品在线| 日韩成视频在线播放| 天天干天天草天天日天天天射伊人| 999热精品在线观看| 99久久极品蜜桃臀精品久久| 日韩av手机在线电影| 免费av网站在线浏览| 日韩av福利大片在线观看| 国产91熟女免费视频| 久久99永久免费看| 人人妻人人澡人人爽dv| 久久久久国产亚洲av麻豆| 欧美激情电影在线观看不卡| 亚洲久久亚女同性| 日韩av在线观看免费看看 | 国产精品色呦呦视频免费看| 日韩免费美女电影| 亚洲欧洲国产一区二区| 久久99永久免费看| 人妻精品无码一区二区三区| 国语黄色淫秽录像带| 天天做天天舔天天射| 男人的天堂久久久亚洲| 丰满人妻av白石茉莉奈电影| 久久国产经典三级av| 自拍偷拍 亚洲 在线| 久久久久久久久久性| 五月天色婷婷av在线| 婷婷基地中文字幕| 久久久久久久夜精品精品| 日韩a v日日夜夜| 蜜桃黄色av网站免费播放| 四虎国产精品久久免费精品| 初撮日本五十路人妻| 天天操天天干天天妻| 99爱99久久久久久久久久| 亚洲欧美日韩偷拍综合| 美女18禁久久久久麻豆| 自拍偷拍 亚洲 在线| 国产日韩欧美春色另类小说| 亚洲自拍偷拍色图区| 99精品视频69v精品视频| ass日本丰满熟妇pics| 国产美女深喉口爆吞精免费| 明天我们好好过高清免费| 日本久久久大片中文字幕| 女人体内射精一区二区三区| 精品国产一区二区三区无码蜜桃 | 日韩av一区二区三区久久久| 精品欧美乱子伦一区二区三区| 国产精品一区二区三区福利| 208精品福利导航| 日韩美女影院免费在线观看| 成年人在线免费观看黄色片| 91超碰免费在线播放| 篠田优中文字幕在线播| 精品国产久久久久蜜臀| 人妻丰满精品一区二区三区| 日韩欧美综合一区二区在线| 国产99不卡免费在线观看| 天天做天天舔天天射| 厕所偷拍视频一区二区三区| 一本大道av伊人久久综合| 久久久96精品久久久| 精品中文久久久久久| 亚洲综合在线伊人| 精品久久精品久久久久| 91成人国产精品视频| 天天操天天爽天天舔天天操一操| 乱女乱妇熟女熟妇综合站| av成人教育在线播放| 亚洲熟妇在线观看一区二区| 综合激情五月天久久| 国产精品网址在线观看| 日韩av午夜精品| 亚洲 免费 在线 观看| 亚洲国产精品五月天久久久| 99精品高清免费在线视频| 国产成人免费精品视频大全| 少妇激情视频一区二区| 熟女人妻之中文字幕| 玖玖玖玖日在线视频| 亚洲av色图com| 日本午夜在线免费观看| 中文字幕+人妻熟女| 美国av 在线播放| 久久亚洲AV成人无码| 日本高清不卡视频在线播放| 五月婷婷蜜臀性色av| 成人精品一区一区二区看片| 欧美高清在线视频99| 久久免费视频观看99| 亚洲香蕉av电影| 久久99精品久久久久久三级| 日韩av中文网址| 高清一区二区三区日本4| 亚洲国产欧美日本视频| 久久99永久免费看| 人人妻人人澡人人爽国产一区| laoyawo老鸭窝在线视频| 成人精品1024欧美日韩| 亚洲aⅴ欧美综合一区二区三区| 中文字幕av最新在线| 先锋资源站中文字幕| 91插插插操美女视频| www日本不卡一二三区| 丰满人妻av白石茉莉奈电影| 国产精品亚洲精品成人| 91在线国产九色porny| 国产又粗又硬又长又爽视频| 久久人妻少妇嫩草av蜜桃动态图| 亚洲综合成人久久av| 91成人国产精品视频| 亚洲女人老师毛茸茸| 婷婷亚洲天堂中文字幕| 国产精品99久久电影| 影音在线精品免费国产资源| 国语黄色淫秽录像带| 大尺度av一区二区三区| 亚洲香蕉av电影| 日韩美女在线视频一区| 精品久久久久久亚洲国产999| 中文字幕+人妻熟女| 91在线观看视频,| 高清一区二区三区日本4| 亚洲狠狠久久综合一区| 日韩欧美三级一区二区在线观看| 中文字幕在线有码二区| 日韩有码视频在线免费观看| 亚洲综合在线伊人| 亚洲欧美自拍偷拍在线观看| 婷婷中文字幕色婷婷| 久久最近最新中文字幕大全| 日本熟妇乱子伦a片在线观看| 久久五月婷婷综合视频| 精品999久久久一免费ww| 激情一区日韩一区欧美一区| 成人精品一区一区二区看片| 亚洲午夜电影久久久| 国产视频av一区二区| 日本女优中文字幕在线| 国产精品久久久久成人片| 人妻啪啪视频免费看| 欧美日韩精品在线观看免费| 精品视频在线观看一区二区97| 欧美日韩久久久久久精品| 男人亚洲天堂2018| 亚洲最大成人网一区二区三区| 欧美激情 另类视频 亚洲| aaa久久久久久久久久网站| 国产五月天在线观看视频| 中文字幕精品久久久人妻| 亚洲欧美日韩国产另类专区| 91人妻露出精品在线| 欧洲av偷拍亚洲av偷拍| 免费色婷婷在线视频| 日本vs欧美一区二区三区| 日韩av在线不卡网站| 久久精品国产亚洲av不卡性色| 97久久视频免费在线播放| 久久成人网男人的天堂| 蜜桃一区二区三区大香蕉| 亚洲国产精品97久久宅男| 亚洲丰满熟妇乱xxxxx| 国产精品一区二区久久精品蜜臀 | 日韩一区二区三区水蜜桃| 日韩日韩av在线| 少妇高潮喷水久久久久久桃花| 日韩av黄片在线观看| 久久久亚洲熟妇熟女在线| 五月天色婷婷av在线| 影音在线精品免费国产资源| 91精品久久久久久五月天| 蜜桃视频在线观看一区精品| 福利一福利二福利三| 熟女激情一区二区三区| 欧美又色又爽又黄又粗暴| 午夜精品久久婷婷蜜桃| 久久97久久免费视频| 天美麻豆成人av精品小说| 成人精品1024欧美日韩| 欧美肥臀精品一区二区三区| 久久精品琪琪男人的天堂| 国产一区二区三区a级毛片| 影音在线精品免费国产资源| av久久伊人精品中文字幕| 欧美中文字幕在线一区| 激情四射五月开心六月婷婷| 麻豆精品123在线观看| 欧美熟妇人妻视频在线| 中文字幕一线一区和二区 | 国内精品久久久久久久久久清纯| 国产精品久久久久久久久粉嫩av| 欧美va亚洲va精品| 欧美最猛性亚洲精品推荐| av福利网站在线观看| 清纯唯美激情亚洲综合另类 | 97国产免费电影网| 99.com精品视频| 欧美日韩人妻久久精品| 2001年亚洲区十强赛| 精品aa级中文字幕人妻| 欧美熟女高清视频一区二区| 久久久久久久夜精品精品| 国产99久久久国产精品成人免费| 92视频影院视频影院麻豆| 久久久96精品久久久| 777亚洲精品乱码久久| 人妻中文字幕一二三区| 中文字幕精品久久伊人| 日本黄色成年视频| 亚洲综合色激情五月| 中文字幕版婷婷久久| 欧美日韩在线卡一卡二卡三| 色熟女蜜臀又伦av| 欧美一区二区三区综合色| 久久久久久久久久性| 国产人妻另类综合专区| 人妻中出视频一区二区| 在线观看视频 你懂得| 国产a级片免费在线观看| 欧美一区二区三区aa大片| 老鸭子在线观看免费播放| 亚洲久久亚女同性| 精品99国内中文字幕| 亚洲视频另类专区| 丰满的人妻一区七区| 真实玩弄白嫩丰满人妻少妇三级 | 中文字幕在线有码二区| 999精品插丰满少妇人妻| 日韩欧美一二三级| 亚洲天堂黄色免费| 五月激情婷婷网络| 久久精品久久一区二区三区| 少妇高潮喷水久久久久久桃花 | 欧美一区二区三区成人免费看 | 久久99精品国产91| 精品人妻熟女在线视频| 99re在线精品国产欧美久久| 丰满人妻一区二区三区av| 亚洲国模在线视频| 久久久亚洲熟妇熟女ⅹx| 亚洲欧美日韩国产另类专区| 久久久久久久免费女人体| 丰满的人妻一区七区| 亚洲熟妇在线观看一区二区| 777亚洲精品乱码久久| 日韩欧美国产成人一区| 91人妻九色大屁股| 日韩毛片亚洲av| 欧美激情欧美情色成人在线| 高潮久久久久久久av免费| 九九久久国产精品久久久久| 久久草免费在线视频| 精品国产丝袜久久久一区二区| 青青青在线视频自拍| 黑人粗大精品一区二区| 日本av男优巧克力| 99精品视频69v精品视频| 成人黄视频在线播放| 日韩视频在线播放一区二区| 日韩中文字幕在线综合网| 日韩a v日日夜夜| 91在线观看视频,| 国产又粗又硬又长又爽视频| 国产乱子伦视频免费| 99精品视频在线看17| 另类h小视频在线观看| 日韩av黄片在线观看| 91麻豆免费国产在线| 热热久久这里只有精品| 97精品久久人人爽人人爽| 亚洲久久精品午夜| 伊人久久婷婷av| 免费精品国产日韩热久久| 欧美激情 另类视频 亚洲| 欧美日韩精品在线观看免费| 91一区二区三区精华液| 蜜臀av 麻豆av| 国产亚洲欧美日韩在线首页| 日韩精品福利性无码专区| 久久国产精品色av免费看| 思思久久国产精品视频| 蜜久久久91精品人妻| 国产欧美日韩一区二区三区''| 国产精品福利久久久久久久| 日韩欧美颜色渔网| 国产精品久久久久久久漫画| 精品aa级中文字幕人妻| 亚洲av电影在线一区二区| 日韩欧美激情入口| 婷婷基地中文字幕| 亚洲欧美在线视频播放| 久久五月婷婷综合视频| 视频一区视频二区三区| 青草青草视频免费2在线观看 | 色亚洲天堂色派对欧美色| 久久久国产一区二区视| 91成人国产精品视频| 欧美精品一区二区在线视频| 色哟哟视频在线观看国产| 国产黄色主播网址大全在线播放| 最新精品成人在线| 日本aaaa视频在线观看| 精品一区二区三区四区| 国产精品久久久久久久久粉嫩av| 日韩中文字幕有码人妻在线| www.199麻豆在线视频| 久久久九九九九九精品6| youwu视频在线| 亚洲精品蜜桃久久久久| 七十路熟女俱乐部| 国产精品一区二区久久精品蜜臀| 亚洲激情国产一区| 在线免费观看日韩精品视频| 伊人久久热青青草| 午夜寻花美女在线| 久久精品人人看人人爽| 青青青手机版视频在线看| 免费人妻一区二区三区免费视频| 中文字幕亚洲欧美国产| 午夜精品久久婷婷蜜桃| 精品国产一区二区三区制服| 91亚洲国产成人久久精品app| 一本色道久久亚洲综合精品蜜桃| 精品久久久久久亚洲国产999| 亚洲欧洲色图动图| 色熟女蜜臀又伦av| 亚洲中文字幕精品高清| 日韩中文字幕理伦| 欧美日韩久久久久久精品| 国产亚洲精久久久久久无码色戒| 欧美一区二区三区视频午夜| av福利网站在线观看| 日韩成人免费电影三区| 秋霞电影韩国一区二区二区三区 | 中文字幕一区二区三区中文字幕 | 91中文字幕制服诱惑| 国产精品久久久久一区二区| 人妻一区二区中文字幕在线 | 色熟女蜜臀又伦av| w疯狂的少妇2做爰韩国| 精品国产乱码久久久久夜深| 免费中文字幕在线播放| 亚洲天堂性色综合| 99精品视频69v精品视频| 麻豆视传媒官网直接进入免费观看 | 久久精品一区二区三区人妻蜜桃| 久久视频黄色观看网站| 日韩av在线观看卡一卡| 欧美肥臀精品一区二区三区| 精品久久一区电影亚洲| 久久久免费精品少妇| av在线免费播放成人| 9久热这里有国产精品| 亚洲av不卡码在线看| 色婷婷一区二区三区四区五| 亚洲精品乱码97久久久久久| 精品人妻二区中文字幕| 97色视频在线看视频| 久久精品国产久精果冻传媒| 亚洲av综合色区手机| 美女18禁久久久久麻豆| 日本av男优巧克力| 日韩电影黄色免费| 亚洲一级av大片| 人妻丰满精品一区二区三区| 伊人久久婷婷av| 之久精品一区二区| 黄色片一级美女黄色片| 在线不卡日韩视频播放| 久久久久国产麻豆婷婷| 最美人妻一区二区三区| 91久久国产综合久久91在线| 欧美人妻a∨中文字幕在线| 成年人晚上免费看的视频| 国产高潮国产高潮久久久久久91| 亚洲中文字幕成人久久| 欧美又色又爽又黄又粗暴| 国产中文字幕在线91| 亚洲狠狠久久综合一区| 99精品视频在线看17| 久久久九九九九九精品6| 久久五月婷婷综合视频| 97精品久久人人爽人人爽| 日韩成人免费电影三区| 天天摸天天舔天天爱| 18久久久免费视频| 蜜桃一区二区三区大香蕉| 亚洲久久久久久久人妻| 99精品视频在线看17| 色视频在线观看123| 91亚洲国产成人久久精品app| 国产精品男人的天堂999| 最美人妻一区二区三区| 日韩欧美亚州综合久久| 欧美又色又爽又黄又粗暴| 天天操,天天干,天天| 久久av资源男人站| 国产精品99久久电影| 日韩av电影免费在线播放| 999精品插丰满少妇人妻| 亚洲成a人片7777| 日本av男优巧克力| 国产+日本+欧美在线观看| 欧美熟女高清视频一区二区| 久久久精品欧美一区二区国产| 国产一区二区久久久久久| 七十路熟女俱乐部| japanese 在线中文字幕| 五月婷婷51视频免费| 日韩av网址在线播放| 老熟女17页一91| 亚洲综合在线伊人| 丰满少妇精品一区二区| 亚洲尺码和欧洲尺码av| 先锋男人资源中文字幕| 国产 另类 在线 欧美日韩| 日韩av在线观看卡一卡| 国产精品久久久久久久久久69| 性欧美另类sex极品free| 国内精品久久久久久久久久清纯| 蜜臀久久久久精品一区二区三区| 蜜臀av 麻豆av| 日本a级2020在线观看| 久久婷婷激情综合色综合俺也去| 熟女人妻之中文字幕| 久久视频一区二区三| 日日夜夜夜视频伊人久久 | 丝袜日韩中文字幕| 日韩欧美综合一区二区在线| 久久av资源男人站| 男人亚洲天堂2018| 久久五月天天婷婷激情综合| 久久亚洲精品无码系列客服| 欧美3d成人动漫在线| 99精品中文字幕在线视频| 欧美激情欧美情色成人在线 | 免费看久久久久久久性大片| 另类欧美亚洲中文综合| 青青青在线视频自拍| 日本av都有哪些系列| 亚洲一线产区二线产区区| 日韩 美女 在线观看| 欧美日韩久久久久久精品| 日韩精品午夜免费观看| 日本少妇人妻xxxx| 成人av电影免费版| 色图av亚洲综合| 美国av 在线播放| 久久婷婷激情综合色综合俺也去| 久久免费视频观看99| 日本aaaa视频在线观看| 5858s在线视频| 天天日天天干一道一小| 日韩三级伦理片免费看| 50岁老熟女一区二区三区| 久久综合久久综合久久色| 亚洲制服高清中文字幕| 又爽又黄的免费视频91| 久久婷婷激情综合色综合俺也去| 青青草久久大香蕉| 人妻有码中文字幕中文| 亚洲欧洲偷拍自拍| www.色av成人| 亚洲狠狠婷婷综合久久a| 欧美熟女高清视频一区二区| 欧美熟妇精品在线观看| 久久久久久少妇被弄高潮| 91亚洲精品成人在线| 日韩欧美激情入口| 97cao瑟瑟在线观看| 人妻熟女视频免费观看| 91插插插操美女视频| 成人黄色午夜污网站在线观看| 欧美日韩成人精品视频| 国产无av码在线观看| 日韩一区二区三区水蜜桃 | 久久五月婷婷综合视频| 人妻精品无码一区二区三区| 国产av一区二区三区天美| 日韩在线免费视频精品| 日韩欧美颜色渔网| 日日碰狠狠躁久久躁一区二区| 亚洲伊人网在线观看| 日韩国产精品电影网| 亚洲久久久久久久人妻| 蜜臀久久久久精品一区二区三区| 99热这里只有精品网| 国产成人精品视频免费网站| 天天日天天干天天色| 欧美一区二区三区综合色| 四虎网站免费av| 久久久久中文字幕免费久久久久久| 人妻中出视频一区二区| 亚洲久久久久久久人妻| 国产视频av一区二区| 91麻豆精品91久久久久同性 | 91九色porny国产视频| 久久视频这里都是精品| 日韩精品刺激视频| 久久婷婷亚洲中文一区二区| 日韩成人av在线电影观看| 亚洲不卡一区av| 久久人人爽爽人人爽av| 欧美人妻激情中文视频| 明天我们好好过高清免费| 亚洲国产欧美激情图区| 日韩少妇视频在线直播| 日韩国产91综合精品| 国产麻豆一级美女精品| 日韩福利视频在线看| 777亚洲精品乱码久久| 青青青高清国产视频| 日韩中文字幕在线网站| 男人亚洲天堂2018| 精品日韩偷拍欧美另类| av岛国不卡在线观看| 久久婷婷亚洲中文一区二区| 欧美熟女高清视频一区二区| 国产精品日韩欧美综合| 嫩草一区二区三区四区中文| 中文字幕久久91| 日韩av在线点播| 日韩毛片亚洲av| 日本精品久久久久中人妻| 亚洲熟妇av熟妇在线| 日韩卡一卡二卡三卡四卡五| 日韩欧美激情入口| 色熟女蜜臀又伦av| 日韩av一区二区三区久久久| 欧美日本一道本一区二区| 五月婷婷久久久久久久久| 99热这里只有的精品| 熟女激情一区二区三区| 人人妻人人澡人人爽国产一区| 蜜久久久91精品人妻| 97国产免费电影网| 亚洲国模在线视频| 亚洲aⅴ欧美综合一区二区三区| 久久久久久精品日韩| 精品99国内中文字幕| 日韩av网址在线播放| 国产精品久久久久久久网站门| 亚洲天堂性色综合| 日本女优和黑人的| 亚洲欧洲国产一区二区| 67914熟女在线观看| 亚洲欧美自拍偷拍在线观看| 日韩亚洲丝袜系列| 精品人妻熟女在线视频| 综合激情伊人久久| 中文字幕av最新在线| 亚洲天堂网av中文字幕| 国产丝袜香蕉在线观看| 亚洲少妇黄色一级片| 日本女同性恋视频| 国产日韩欧美春色另类小说| 麻豆视传媒官网直接进入免费观看| 中文字幕亚洲中文字幕| 激情黄色开心五月天| 天天操,天天干,天天| 97精品国产自在在线观看蜜臀| 亚洲国模在线视频| 国产成人一区二区三区在线视频| 日韩美女夜夜爽av| 亚洲久久久久久久人妻| 麻豆亚州av熟女国产一区二| 色噜噜狠狠躁夜夜躁| 少妇惨叫久久久久久久| 蜜桃黄色av网站免费播放| 国产精品99精品一区二区三区∴| 五月综合缴情婷婷六月| 国产 日韩 欧美 日本| 久久蜜臀精品一区二区| 人人妻人人澡人人看| 精品蜜臀久久久久抄底| 97偷拍视频在线观看| 亚洲:西西一区二区三区四区五区| 久久久亚洲熟妇熟女在线| 人人狠狠久久综合网| 99爱99久久久久久久久久| 婷婷午夜美女诱惑福利电影网| 亚洲欧美一二三视频| 热热久久这里只有精品| 欧美va亚洲va精品| 91沈先生探花极品在线| 国产99成人自拍视频| 天天摸天天舔天天爱| 91偷伦一区二区三区蜜臀| 免费av网站在线浏览| 国产又大又长又粗又硬免费视频| 欧美一区二区三区成人免费看 | 2019年中文字幕在线看| 91嫩草17c欧美国产| 福利小视频国产一区| 亚洲日本精彩视频在线观看| 色噜噜狠狠躁夜夜躁| 91插插插操美女视频| 国产高潮好爽受不了了视频| 91一区二区精品在线| 成人精品一区一区二区看片| 久久婷婷激情综合色综合俺也去| 天天做天天舔天天射| 999精品插丰满少妇人妻| 国产三级三级三级三级av精品| 国产欧美一二三区视频| 香蕉久久a v一区二区三区| 激情亚洲一区蜜桃在线| 亚洲欧美日韩中出| 五月天丁花香婷婷| 91国产视频网站在线观看| 色综合色综合色综合久久| 91亚洲精品成人在线| 2020中文字幕在线看电影| 中文字幕熟女人妻在线观看| 亚洲尺码和欧洲尺码av| 婷婷激情五月天图片| 天天操天天干天天色| 国产精品久久久久久久漫画| 久久精品视频在线国国| 久久男人精品男人天堂免费视频 | 久久爱免费视频16| 一区二区 熟女人妻| 91九色蝌蚪熟妇出轨| 久久久久久网站精品免费| 国产欧美日韩不卡在线观看| 99久久老熟妇仑乱一区 | 素人阁久久久久精品人妻| 久久久久久添逼视频| 制服巨乳人妻在线| 亚洲.欧美.日韩.| 熟女人妻之中文字幕| 人妻丰满精品一区二区三区| 99爱99久久久久久久久久| 精品国产一区二区三区无码蜜桃| av色香蕉一区二区三区| 免费看久久久久久久性大片| 一本大道av伊人久久综合| 欧美日韩国产一区二区三区免费| 亚洲激情 欧美激情| 18禁成年av网站免费看| 国产精品成人av麻豆| 亚洲熟妇av熟妇在线| japanese 在线中文字幕| 1024 国产高清の最新合集| 风间由美交换夫中文字幕| 国产欧美一区二区二区精品| 99热这里只有的精品| 日韩电影黄色免费| 欧美激情欧美情色成人在线| 午夜无人影视在线| av福利网站在线观看| av熟妇翔田千里俱乐部| 精品国产久久久久蜜臀| 日本中文字幕一区二区在线视频| 国产乱人妻精品久久久| 亚洲 欧美 制服 人妻| av国产一区二区三区| 国产乱子伦视频免费| 91老司机免费福利| 午夜在线看1000集| 国产91久久久久久久| 蜜桃av 1区二区| 成年人晚上免费看的视频| 国产探花在线播放精品| 又爽又黄的免费视频91| 国产网址手机上可以看的国产网站| 日韩精品综合免费视频| 午夜在线看1000集| 日韩av中文字幕一区| 精品人妻久久久久中文字幕19禁| 风间由美交换夫中文字幕| 中文字幕亚洲欧美国产| 99re6热在线视频免费观看| 新版天堂av资源在线| 日韩精品刺激视频| 午夜精品久久婷婷蜜桃| 福利小视频国产一区| 色视频精品视频在线观看 | 欧美一区二区理论片在线观看| 婷婷丁香花五月天| 99精品高清免费在线视频| 中文字幕熟女人妻在线网页| 91在线精品国自产拍| 国产精品一区二区亚瑟不卡| 91高级会所在线播放| 日本av男优巧克力| 久久精品中文字幕av| 五月黄色激情视频| 国产精品欧美日韩五月香蕉| 另类蜜桃刺激视频在线观看| 日日夜夜夜视频伊人久久 | 久久久久久久久久久久久熟女a∨ 精品99国内中文字幕 | 国产一区二中文字幕在线免费观看| 国产亚洲天堂sss| 搡老熟女一区二区三区四区视频| 天天操天天干天天妻| 中文字幕一线一区和二区| 亚洲伊人网在线观看| 日韩在线中文字幕不卡| 67914熟女在线观看| 日韩 美女 在线观看| 亚洲激情有码一区二区| 中文字幕亚洲资源天堂| 久久精品国产久精果冻传媒| 亚洲激情国产一区| 日韩av在线精品观看| 色图av亚洲综合| 97人妻人人揉人人澡人人爽国产| 久久视频这里都是精品| 久久精品中文字幕av| 99久久999久久久精品综合| 久久综合久久综合久久色| 日本aaaa视频在线观看| 中文字幕+人妻熟女| 色男人天堂东京热| 天天色天天日天天操| 欧美激情欧美情色成人在线| 清纯唯美激情亚洲综合另类| 明天我们好好过高清免费| av熟妇翔田千里俱乐部| 精品无码久久久久久毛片| 欧美一区二区三区激情无套| 日本黄色成年视频| 国产高潮好爽受不了了视频| 99爱99久久久久久久久久| 久久行黑国产露脸精品| 亚洲另类色区欧美日韩| 亚洲综合在线伊人| 日本老女人视频在线观看| 久久精品一区二区三区人妻蜜桃| 久久久精品蜜桃在线| 中文字幕一区二区三区六区9区| 婷婷亚洲免费基地| 日韩欧美亚州综合久久| 欧美最猛性亚洲精品推荐| 五月综合缴情婷婷六月| 亚洲丰满熟妇乱xxxxx| 五月爱婷婷六月丁香性| 伊人久久婷婷av| 热热久久这里只有精品| 91在线播放视频免费| 99re6热在线视频免费观看| 免费看久久久久久久性大片| 人妻中文字幕一二三区| 亚洲精品久久久久久久久蜜桃| 黑人粗大精品一区二区| 七十路熟女俱乐部| 亚洲制服高清中文字幕| 亚洲精品图片第十八页| 欧美又色又爽又黄又粗暴| 日韩一级黄色大片免费观看| 色男人天堂东京热| 麻豆免费国产福利免费国产福利| 国产成人av最新网址| 精品乱码久久久久久蜜臀| 18在线观看久久久麻豆| 久久亚洲 欧美 综合aⅴ| 成人av激情网一区二区三区| 久久精品中文字幕av| 久久久亚洲精品久久仙| 久久国产精品色av免费看| av蜜臀懂色啊啊啊| 91精品国产综合久久久久久粉嫩 | 熟女少妇一区二区精品| 福利日韩精品中文字幕专区| 久久久九九九九九精品6| 欧美精品一区二区在线视频| 91麻豆免费国产在线| 人妻一区二区中文字幕在线| 日韩欧美综合一区二区在线| 天天插天天日天天舔| 欧美最猛性亚洲精品推荐| 中文字幕在线视频亚洲| 99免费观看视频在线| 国产日韩欧美人妻在线观看| 亚洲制服欧美丝袜| 国产99久久久国产精品成人免费| 亚洲成人日韩丶av| 丰满的人妻一区七区| 国产有码av一区二区| 黄色片一级美女黄色片| 亚洲欧洲偷拍自拍| 亚洲熟妇免费在线视频| 久久免费视频观看99| 青青91免费视频| 91国产手机视频在线观看| 国产清纯白嫩美女无套| 性欧美另类sex极品free| 欧美不卡高清一区二区三区| 日韩人妻插舔激情午夜| 国产精品午夜蜜av| 亚洲乱精品中文字字幕| 国产精品久久欠久久al换脸综合| 亚洲制服欧美丝袜| 国产成人一区二区三区在线视频| 5858s在线视频| 久久这里只有欧美精品| 2019年中文字幕在线看| laoyawo老鸭窝在线视频| 久久久97精品国产| 国产中文字幕在线91| 国产欧美精品久久无广告| 亚洲欧美日韩中出| 国产精品久久久久一区二区| 91综合精品国产丝袜长腿| 福利小视频国产一区| 国产乱人妻精品久久久| 欧美高清在线视频99| 欧美最猛性亚洲精品推荐| 18久久久免费视频| 天天色天天日天天操| 国产精品一区二区三区三级| 骚妻少妇一区二区| 精品久久久久久999| 2020日韩中文字幕| 伊人久久热青青草| 九九re精品免费视频| 日韩av一区二区三区久久久| 久久久久国产亚洲av麻豆| 我要看一级国产黄色绿像| 五月婷婷激情四射综合网| 久久久九九九九九精品6| 国产成人av最新网址| 久久久久久精品日韩| 蜜久久久91精品人妻| 2020日韩中文字幕| 亚洲国产精品午夜av| 中文字幕版婷婷久久| 亚洲国模在线视频| 一区,二区,三区视频| 久久一本麻豆天美欧美日韩| 亚洲天堂网av中文字幕| 国产欧美日韩亚洲另类第一第二页 | 91亚洲精品成人在线| 亚洲一区二区三二区厕所偷拍| 日韩亚洲图色在线|