如何创建一个基本JQuery的插件
如何創建一個基本的插件
有時您希望在整個代碼中提供一些功能。例如,也許你想要一個單一的方法,你可以調用一個jQuery選擇,對選擇執行一系列的操作。在這種情況下,您可能需要編寫一個插件。
鏈接jQuery如何工作101:jQuery對象方法
在我們編寫自己的插件之前,首先要了解一下jQuery如何工作。看看這段代碼:
| 1 | $( "a" ).css( "color", "red" ); |
這是一些很基礎的jQuery代碼,但你知道幕后發生了什么嗎?無論何時使用該$函數來選擇元素,它返回一個jQuery對象。這個對象包含了所有的你已經使用(方法.css(),.click()等)和所有適合你的選擇要素。jQuery對象從對象中獲取這些方法$.fn。這個對象包含了所有的jQuery對象方法,如果我們想編寫自己的方法,它也需要包含這些方法。
鏈接基本插件創作
假設我們要創建一個插件,使一組檢索到的元素中的文本變為綠色。我們所要做的就是添加一個調用的函數greenify來$.fn,這將是可用的,就像任何其他的jQuery對象的方法。
| 1 2 3 4 五 | $.fn.greenify = function() { this.css( "color", "green" ); }; $( "a" ).greenify(); // Makes all the links green. |
注意使用.css(),另一種方法,我們使用this,而不是$( this )。這是因為我們的greenify函數是與之相同的對象的一部分.css()。
鏈接鏈接
這是有效的,但是我們需要做的幾件事情就是讓我們的插件在現實生活中生存下去。當您將五個或六個操作鏈接到一個選擇器上時,jQuery的功能之一就是鏈接。這是通過使所有jQuery對象方法再次返回原始的jQuery對象來實現的(有一些例外:.width()調用無參數返回所選元素的寬度,并且不可鏈接)。使我們的插件方法可鏈接需要一行代碼:
| 1 2 3 4 五 6 | $.fn.greenify = function() { this.css( "color", "green" ); return this; } $( "a" ).greenify().addClass( "greenified" ); |
鏈接保護$別名和添加范圍
該$變量是JavaScript庫中很受歡迎,如果你正在使用jQuery的另一個庫,你將不得不作出的jQuery不使用$帶jQuery.noConflict()。然而,這將會破壞我們的插件,因為它是用函數$的別名假設來寫的jQuery。為了與其他插件很好地工作,并且仍然使用jQuery的$別名,我們需要把我們所有的代碼里面立即調用函數表達式,然后傳遞給函數jQuery,并命名參數$:
| 1 2 3 4 五 6 7 8 | (function ( $ ) { $.fn.greenify = function() { this.css( "color", "green" ); return this; }; }( jQuery )); |
此外,立即調用函數的主要目的是允許我們擁有自己的私有變量。假裝我們想要一個不同的顏色綠色,我們想把它存儲在變量中。
| 1 2 3 4 五 6 7 8 9 10 | (function ( $ ) { var shade = "#556b2f"; $.fn.greenify = function() { this.css( "color", shade ); return this; }; }( jQuery )); |
鏈接最小化插件足跡
編寫插件時只需占用一個插槽就是很好的做法$.fn。這樣可以減少您的插件被覆蓋的機會,并且插件將覆蓋其他插件的機會。換句話說,這是壞的:
| 1 2 3 4 五 6 7 8 9 10 11 | (function( $ ) { $.fn.openPopup = function() { // Open popup code. }; $.fn.closePopup = function() { // Close popup code. }; }( jQuery )); |
有一個插槽會更好,并使用參數來控制一個插槽執行什么操作。
| 1 2 3 4 五 6 7 8 9 10 11 12 13 14 15 | (function( $ ) { $.fn.popup = function( action ) { if ( action === "open") { // Open popup code. } if ( action === "close" ) { // Close popup code. } }; }( jQuery )); |
鏈接使用each()方法
您的典型jQuery對象將包含對任意數量的DOM元素的引用,這就是為什么jQuery對象通常被稱為集合。如果要對特定元素進行任何操作(例如獲取數據屬性,計算特定位置),則需要使用.each()循環元素。
| 1 2 3 4 五 6 7 | $.fn.myNewPlugin = function() { return this.each(function() { // Do something to each element here. }); }; |
請注意,我們返回結果.each()而不是返回this。既然.each()已經是可鏈接的,它返回this,然后我們返回。這是保持可鏈接性的一種更好的方法,而不是我們迄今為止所做的一切。
鏈接接受選項
隨著您的插件越來越復雜,最好通過接受選項來使您的插件可自定義。最簡單的方法是這樣做,特別是如果有很多選項,那就是一個對象文字。我們更改我們的greenify插件以接受一些選項。
| 1 2 3 4 五 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | (function ( $ ) { $.fn.greenify = function( options ) { // This is the easiest way to have default options. var settings = $.extend({ // These are the defaults. color: "#556b2f", backgroundColor: "white" }, options ); // Greenify the collection based on the settings variable. return this.css({ color: settings.color, backgroundColor: settings.backgroundColor }); }; }( jQuery )); |
使用示例
| 1 2 3 | $( "div" ).greenify({ color: "orange" }); |
為默認值color的#556b2f獲取通過重寫$.extend()為橙色。
鏈接在一起
這是一個使用我們討論的一些技巧的小插件的例子:
| 1 2 3 4 五 6 7 8 9 10 11 12 13 14 15 16 17 | (function( $ ) { $.fn.showLinkLocation = function() { this.filter( "a" ).each(function() { var link = $( this ); link.append( " (" + link.attr( "href" ) + ")" ); }); return this; }; }( jQuery )); // Usage example: $( "a" ).showLinkLocation(); |
這個方便的插件遍歷集合中的所有錨點,并將該href屬性附加在括號中。
| 1 2 3 4 五 | <!-- Before plugin is called: --> <a href="page.html">Foo</a> <!-- After plugin is called: --> <a href="page.html">Foo (page.html)</a> |
我們的插件可以通過以下優化:
| 1 2 3 4 五 6 7 8 9 10 11 12 13 | (function( $ ) { $.fn.showLinkLocation = function() { this.filter( "a" ).append(function() { return " (" + this.href + ")"; }); return this; }; }( jQuery )); |
我們使用該.append()方法的能力來接受回調,并且該回調的返回值將決定附加到集合中每個元素的內容。還要注意,我們沒有使用該.attr()方法來檢索href屬性,因為本機DOM API可以方便地訪問aptly named?href屬性。
高級插件概念
鏈接提供公共訪問默認插件設置
我們可以而且應該對上面的代碼進行改進是公開默認的插件設置。這很重要,因為它使插件用戶很容易用最小的代碼覆蓋/定制插件。這就是我們開始利用函數對象的地方。
| 1 2 3 4 五 6 7 8 9 10 11 12 13 14 15 16 17 | // Plugin definition. $.fn.hilight = function( options ) { // Extend our default options with those provided. // Note that the first argument to extend is an empty // object – this is to keep from overriding our "defaults" object. var opts = $.extend( {}, $.fn.hilight.defaults, options ); // Our plugin implementation code goes here. }; // Plugin defaults – added as a property on our plugin function. $.fn.hilight.defaults = { foreground: "red", background: "yellow" }; |
現在,用戶可以在腳本中包含這樣一行:
| 1 2 3 | // This needs only be called once and does not // have to be called from within a "ready" block $.fn.hilight.defaults.foreground = "blue"; |
現在我們可以像這樣調用插件方法,它將使用藍色的前景色:
| 1 | $( "#myDiv" ).hilight(); |
您可以看到,我們允許用戶編寫一行代碼來更改插件的默認前景色。而用戶仍然可以選擇性地覆蓋這個新的默認值:
| 1 2 3 4 五 6 7 8 9 10 11 12 13 14 | // Override plugin default foreground color. $.fn.hilight.defaults.foreground = "blue"; // ... // Invoke plugin using new defaults. $( ".hilightDiv" ).hilight(); // ... // Override default by passing options to plugin method. $( "#green" ).hilight({ foreground: "green" }); |
鏈接提供公共訪問次要功能作為適用
該項目與上一個項目緊密相關,是擴展插件(并允許其他擴展插件)的有趣方式。例如,我們的插件的實現可以定義一個稱為“格式”的功能,其格式化高亮文本。我們的插件可能看起來像這樣,默認實現的格式方法定義在hilight函數下面:
| 1 2 3 4 五 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 | // Plugin definition. $.fn.hilight = function( options ) { // Iterate and reformat each matched element. return this.each(function() { var elem = $( this ); // ... var markup = elem.html(); // Call our format function. markup = $.fn.hilight.format( markup ); elem.html( markup ); }); }; // Define our format function. $.fn.hilight.format = function( txt ) { return "<strong>" + txt + "</strong>"; }; |
我們可以輕松地支持選項對象上的另一個屬性,允許提供回調函數來覆蓋默認格式。這是支持自定義插件的另一個很好的方法。這里顯示的技術通過實際暴露格式功能使其可以重新定義,從而進一步擴展。使用這種技術,其他人可能會發布自己的插件自定義覆蓋 - 換句話說,這意味著其他人可以為插件編寫插件。
考慮到我們在本文中構建的簡單的示例插件,您可能會想知道何時會有用。一個現實世界的例子是循環插件。循環插件是一個幻燈片插件,它支持多種內置的轉換效果 - 滾動,滑動,淡入淡出等。但是,實際上,沒有辦法定義可能希望應用于幻燈片轉換的每種類型的效果。這就是這種類型的可擴展性是有用的。循環插件公開了一個“transition”對象,用戶可以添加自己的自定義轉換定義。它在插件中定義如下:
| 1 2 3 4 五 | $.fn.cycle.transitions = { // ... }; |
這種技術使得其他人可以定義和提供插件到循環插件的轉換定義。
鏈接保持私人功能私有
暴露部分插件被覆蓋的技術可以非常強大。但是,您需要仔細考慮實施的哪些部分才能公開。一旦暴露,您需要記住,調用參數或語義的任何更改可能會破壞向后兼容性。作為一般規則,如果您不確定是否公開特定功能,那么您可能不應該。
那么,我們如何定義更多的功能,而不會混淆命名空間,而不會暴露實現?這是關閉的工作。為了演示,我們將在我們的插件中添加另一個函數叫做“debug”。調試功能將所選元素的數量記錄到控制臺。要創建一個閉包,我們將整個插件定義包裝在一個函數中(如jQuery創作指南中所詳述的)。
| 1 2 3 4 五 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | // Create closure. (function( $ ) { // Plugin definition. $.fn.hilight = function( options ) { debug( this ); // ... }; // Private function for debugging. function debug( obj ) { if ( window.console && window.console.log ) { window.console.log( "hilight selection count: " + obj.length ); } }; // ... // End of closure. })( jQuery ); |
我們的“調試”方法無法從關閉的外部訪問,因此對我們的實現是私有的。
鏈接Bob和Sue
假設鮑勃創建了一個邪惡的新畫廊插件(稱為“超級畫廊”),其中包含一幅圖像列表,并使其可導航。鮑勃拋出了一些動畫,使它更有趣。他試圖使插件盡可能的自定義,最終結果是這樣的:
| 1 2 3 4 五 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 三十 | jQuery.fn.superGallery = function( options ) { // Bob's default settings: var defaults = { textColor: "#000", backgroundColor: "#fff", fontSize: "1em", delay: "quite long", getTextFromTitle: true, getTextFromRel: false, getTextFromAlt: false, animateWidth: true, animateOpacity: true, animateHeight: true, animationDuration: 500, clickImgToGoToNext: true, clickImgToGoToLast: false, nextButtonText: "next", previousButtonText: "previous", nextButtonTextColor: "red", previousButtonTextColor: "red" }; var settings = $.extend( {}, defaults, options ); return this.each(function() { // Plugin code would go here... }); }; |
第一件事可能是你的頭腦(好的,也許不是第一個),這個插件必須有多大,以適應這樣一個級別的定制。插件,如果不是虛構的,可能會比必要的大得多。只有這么多千字節的人會愿意花費!
現在,我們的朋友鮑勃認為這一切都很好;?事實上,他對插件及其定制級別印象深刻。他認為,所有的選擇都可以使用更為通用的解決方案,可以在許多不同的情況下使用。
蘇,我們的另一個朋友,決定使用這個新的插件。她已經設置了所有的選項,現在有一個工作的方案坐在她面前。只有五分鐘后,玩插件后,如果每張圖片的寬度都以較慢的速度動畫,她意識到畫廊會更好看。她匆忙搜索Bob的文檔,但沒有發現animateWidthDuration選項!
鏈接你看到問題嗎?
這不是真的關于您的插件有多少選項;?但它有什么選擇!
鮑勃已經超過了頂峰。他提供的定制水平雖然可能高,但實際上相當低,特別是考慮到使用此插件時可能需要控制的所有可能的事情。鮑勃犯了錯誤提供了很多可笑的具體選擇,使他的插件更難以自定義!
鏈接更好的模型
所以很明顯:鮑勃需要一個新的定制模式,一個不放棄控制或抽出必要細節的模型。
鮑勃如此吸引這個高級簡單性的原因是,jQuery框架非常適合于這種觀念。提供一個previousButtonTextColor選項是好的和簡單的,但讓我們面對它,絕大多數的插件用戶將要更多的控制!
以下是一些提示,可幫助您為插件創建更好的可定制選項:
鏈接不要創建特定于插件的語法
使用您的插件的開發人員不應該學習一種新的語言或術語,只是為了完成這項工作。
鮑勃認為他提供最大的定制與他的延遲選項(看上面)。他做的這樣,他的插件可以指定四個不同的延遲,“很短”,“很短”,“相當長”或“很長”:
| 1 2 3 4 五 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | var delayDuration = 0; switch ( settings.delay ) { case "very short": delayDuration = 100; break; case "quite short": delayDuration = 200; break; case "quite long": delayDuration = 300; break; case "very long": delayDuration = 400; break; default: delayDuration = 200; } |
這不僅限制了人們的控制水平,而且占用了相當多的空間。十二行代碼只是為了定義延遲時間有點多,你不覺得嗎?構建此選項的更好方法是讓插件用戶以數字的形式指定時間(以毫秒為單位),以便不需要處理該選項。
這里的關鍵不在于通過抽象來減少控制的程度。您的抽象,無論如何,可以像您想要的一樣簡單,但確保使用您的插件的人仍然會有這么多追捧的低級控制!(低級我的意思是非抽象的)
鏈接充分控制元素
如果您的插件創建要在DOM中使用的元素,那么為插件用戶提供一些訪問這些元素的方法是個好主意。有時這意味著給予某些元素ID或類。但請注意,您的插件不應該在內部依賴這些鉤子:
執行不良
| 1 2 3 4 | // Plugin code $( "<div class='gallery-wrapper' />" ).appendTo( "body" ); $( ".gallery-wrapper" ).append( "..." ); |
為了允許用戶訪問甚至操縱該信息,您可以將其存儲在包含插件設置的變量中。以前的代碼的更好的實現如下所示:
| 1 2 3 4 五 6 7 | // Retain an internal reference: var wrapper = $( "<div />" ) .attr( settings.wrapperAttrs ) .appendTo( settings.container ); // Easy to reference later... wrapper.append( "..." ); |
請注意,我們創建了一個引用包裝器的引用,并且我們還調用該.attr()方法來向元素添加任何指定的屬性。所以在我們的設置中可能會像這樣處理:
| 1 2 3 4 五 6 7 8 9 10 | var defaults = { wrapperAttrs : { class: "gallery-wrapper" }, // ... rest of settings ... }; // We can use the extend method to merge options/settings as usual: // But with the added first parameter of TRUE to signify a DEEP COPY: var settings = $.extend( true, {}, defaults, options ); |
在$ .extend()方法現在將通過所有嵌套對象遞歸給我們設定值和通過選擇兩者的合并版本,給人傳遞的選項優先。
插件用戶現在有權力指定該包裝器元素的任何屬性,因此如果它們需要任何CSS樣式的鉤子,那么他們可以很容易地添加一個類或更改ID的名稱,而無需去挖掘插件源碼。
可以使用相同的模型來讓用戶定義CSS樣式:
| 1 2 3 4 五 6 7 8 9 10 | var defaults = { wrapperCSS: {}, // ... rest of settings ... }; // Later on in the plugin where we define the wrapper: var wrapper = $( "<div />" ) .attr( settings.wrapperAttrs ) .css( settings.wrapperCSS ) // ** Set CSS! .appendTo( settings.container ); |
您的插件可能有一個關聯的樣式表,開發人員可以添加CSS樣式。即使在這種情況下,最好提供一些方便的方法來設置JavaScript中的樣式,而無需使用選擇器來獲取元素。
鏈接提供回調功能
什么是回調??- 回調本質上是一個稍后調用的函數,通常由一個事件觸發。它作為參數傳遞,通常是組件的發起調用,在這種情況下是一個jQuery插件。
如果您的插件由事件驅動,那么為每個事件提供回調功能可能是一個好主意。此外,您可以創建自己的自定義事件,然后為其提供回調。在這個gallery插件中添加一個“onImageShow”回調可能是有意義的。
| 1 2 3 4 五 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | var defaults = { // We define an empty anonymous function so that // we don't need to check its existence before calling it. onImageShow : function() {}, // ... rest of settings ... }; // Later on in the plugin: nextButton.on( "click", showNextImage ); function showNextImage() { // Returns reference to the next image node var image = getNextImage(); // Stuff to show the image here... // Here's the callback: settings.onImageShow.call( image ); } |
而不是通過傳統方式(加括號)啟動回調,我們在上下文中調用它將image是對圖像節點的引用。這意味著您可以通過this回調中的關鍵字訪問實際的圖像節點:
| 1 2 3 4 五 6 7 | $( "ul.imgs li" ).superGallery({ onImageShow: function() { $( this ).after( "<span>" + $( this ).attr( "longdesc" ) + "</span>" ); }, // ... other options ... }); |
同樣,您可以添加一個“onImageHide”回調函數和其他許多回調函數。回調點是給插件用戶一個簡單的方法來添加額外的功能,而不需要在源代碼中挖掘。
鏈接記住,這是一個妥協
您的插件無法在每種情況下工作。同樣地,如果您提供的控制方法不是很少或很少,則不會非常有用。所以,請記住,這將是一個妥協。您必須始終考慮的三件事情是:
- 靈活性:您的插件可以處理多少情況?
- 大小:插件的大小是否對應于其功能級別?也許你使用一個非常基本的工具提示插件,如果它是20k的大小?- 可能不會!
- 性能:您的插件是否以任何方式大量處理選項?這是否影響速度?為最終用戶帶來的開銷是否值得?
轉載于:https://www.cnblogs.com/burningmyself/p/7451399.html
總結
以上是生活随笔為你收集整理的如何创建一个基本JQuery的插件的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: BZOJ1026 [SCOI2009]w
- 下一篇: 《大道至简》第八章读后感