The old driver will show you the jQuery plug-in development process and the jquery plug-in process
Jquery plug-in Development Mode
Jquery plug-ins generally have three development methods:
- Use $. extend () to extend jQuery
- Add a new method to jQuery through $. fn
- Use $. widget () to apply jQuery UI's component factory method to create
The first type of $. extend () is relatively simple. Generally, complex plug-ins are rarely developed independently. The third type is an advanced development mode, which is not described in this article. The second method is used by General Plug-in development. This article focuses on the second method.
Plug-in development
The second plug-in development method is generally defined as follows:
$.fn.pluginName = function() { //your code here}
Plug-in development, we generally use the object-oriented way of thinking
For example, defining an object
Var Haorooms = function (el, opt) {this. $ element = el, this. ults = {'color': 'red', 'fontsize': '12px ', 'textrecoration': 'none'}, this. options = $. extend ({}, this. defaults, opt)} // defines the haorooms method. prototype = {changecss: function () {return this.w.element.css ({'color': this. options. color, 'fontsize': this. options. fontSize, 'textrecoration': this. options. textDecoration });}}
$. Extend ({}, this. defaults, opt) has {} mainly to create a new object and retain the default value of the object.
$. Fn. myPlugin = function (options) {// create the haorooms object var haorooms = new Haorooms (this, options); // call the return Haorooms method. changecss ();}
You can call this plug-in directly as follows:
$(function() { $('a').myPlugin({ 'color': '#2C9929', 'fontSize': '20px' });})
Problems with the above development methods
The above development method has a serious problem, that is, defining a global Haorooms, which is not good for plug-in compatibility and other aspects. In case Haorooms is used elsewhere, your code will be miserable! Now we wrap up the above Code and wrap it with a self-called anonymous function (sometimes block-level scope or private scope). This will not happen! The same is true for js plug-in development. We can use a self-called anonymous function to wrap our own code! The package method is as follows:
(function(){})()
Use the package above.
But there is another problem. When we study the code of Daniel, we often see ";", which is to avoid unnecessary errors such as code merging.
For example, we define a function:
var haoroomsblog=function(){}(function(){ })()
The haoroomsblog function is not followed by a plus sign, which leads to code errors. To avoid such a situation, it is usually written like this!
;(function(){ })()
Wrap your plug-in code in it, which is a simple plug-in. (This is true for js plug-ins and jquery plug-ins)
Another problem
Package Your plug-in
;(function(){ })()
Basically, it can be said that it is perfect. However, to make your plug-ins more widely used and more compatible, you must consider some special practices of plug-ins. For example, some friends want to avoid conflicts between jquery and zeptojs, modify the prefix "$" of jquery to "jQuery", and some friends modify the default document and other methods. To make your plug-in run as usual when these items are fixed, we will wrap the Code in the following:
; (Function ($, window, document, undefined) {// Our code ..}) (JQuery, window, document );
You can avoid the above situations!
Excellent example
Below, we have a basic layer of plug-ins:
(Function ($) {var privateFunction = function () {// code is run here} var methods = {init: function (options) {return this. each (function () {var $ this = $ (this); var settings = $ this. data ('ininname'); if (typeof (settings) = 'undefined') {var defaults = {propertyName: 'value', onSomeEvent: function () {}} settings = $. extend ({}, defaults, options); $ this. data ('ininname', settings);} else {settings = $. Extend ({}, settings, options) ;}// code runs here}) ;}, destroy: function (options) {return $ (this ). each (function () {var $ this = $ (this); $ this. removeData ('ininname') ;}, val: function (options) {var someValue = this.eq(0).html (); return someValue ;}; $. fn. pluginName = function () {var method = arguments [0]; if (methods [method]) {method = methods [method]; arguments = Array. prototype. slice. Call (arguments, 1);} else if (typeof (method) = 'object' |! Method) {method = methods. init;} else {$. error ('method' + Method + 'does not exist on jQuery. pluginName '); return this;} return method. apply (this, arguments) ;}} (jQuery );
You may notice that the structure of the code I mentioned is very different from that of other plug-in codes. Depending on your usage and needs, the development methods of plug-ins may also be diversified. My goal is to clarify some concepts in the Code, so that you can find a suitable method to understand and develop a jQuery plug-in.
Now, let's dissect our code!
Container: an instant function execution
Basically, the code of each plug-in is contained in a function that is executed instantly, as follows:
(Function (arg1, arg2) {// code}) (arg1, arg2 );
An instant function, as its name implies, is a function. What makes it special is that it is contained in a pair of parentheses, which allows all code to run in the local scope of the anonymous function. This does not mean that the DOM (global variable) is blocked in the function, but that the external cannot access the public variables and object namespaces inside the function. This is a good start. When you declare your variables and objects, you don't have to worry about conflicting variable names and existing code.
Now, because all the public variables in the function cannot be accessed, it is a problem to use jQuery as an internal public variable. Just like a common function, real-time functions also pass in object parameters based on references. We can pass the jQuery object into the function as follows:
(Function ($) {// use $ to reference jQuery in a local scope}) (jQuery );
We passed in a function that passed in the public variable "jQuery" for immediate execution. In the function part (container), we can reference it through "$. That is to say, we call the container as a function, and the parameter of this function is jQuery. Because the referenced "jQuery" is passed in as a public variable, rather than its abbreviation "$", we can be compatible with the Prototype library. If you do not use Prototype or other libraries that use "$" for short, it will not affect you if you do not, but it is still a good thing to know this usage.
Plug-in: A Function
A jQuery plug-in is essentially a huge function in the jQuery namespace. Of course, we can easily use "jQuery. pluginName = function "to achieve our goal, but if we do so, the code of our plug-in is in the unprotected exposure state. "JQuery. fn" is short for "jQuery. prototype", which means that when we get our plug-in through the jQuery namespace, it is only writable (unchangeable ). What can it actually do for you? It allows you to properly organize your code and understand how to protect your code from unnecessary modifications during running. This is a good practice!
Using a plug-in, we get a basic jQuery function:
(Function ($) {$. fn. pluginName = function (options) {// The code runs return this ;}} here) (jQuery );
The functions in the above Code can be called by "$ (# element '). pluginName ()" like other jQuery functions. Note: How do I add the "return this" Statement; this small piece of code returns a set of original elements (included in this) and these elements are wrapped by a jQuery object. You should also note that "this" is a jQuery object in this specific scope, which is equivalent to "$ ('# element ')".
Based on the returned object, we can conclude that in the above Code, we use "$ ('# element '). the effect of pluginName () is the same as that of "$ ('# element. In your instant function execution scope, you do not need to wrap this into a jQuery object in the "$ (this)" method, because this is already a packaged jQuery object.
Multiple elements: Understanding Sizzle
The selector engine used by jQuery is called Sizzle. Sizzle can provide multi-element operations for your function (for example, for all elements with the same class name ). This is one of several excellent jQuery features, but it is also something you need to consider when developing plug-ins. Even if you are not prepared to provide multi-element support for your plug-in, it is still a good practice to prepare for it.
Here I have added a small piece of code that enables your plug-in code to work independently for each element in a multi-element collection:
Function ($) {// Add your plug-in code to the protected "fn" namespace in jQuery, and use "pluginName" as the plug-in function name $. fn. pluginName = function (options) {// return "this" (the return value of function each () is also this) for chained calls. Return this. each (function () {// run the code here. You can use "this" to obtain each individual element // For example: $ (this ). show (); var $ this = $ (this) ;}}) (jQuery );
In the above sample code, I do not use each () to run code on each element in my selector. In the local scope of the function called by each (), you can use this to reference each element that is processed separately, that is, you can use $ (this) to reference its jQuery object. In the local scope, I use the $ this variable to store jQuery objects, instead of using $ (this) for every function call. this is a good practice. Of course, this is not always necessary; but I have included it in my code. Note that we will use each () for each separate method, so that we can return the value we need instead of a jQuery object.
The following is an example. If our plug-in supports a val method, it can return the value we need:
$ ('# Element'). pluginName ('val '); // return the value we need instead of a jQuery object.
Function: public and private methods
A basic function may work well in some cases, but a slightly more complex plug-in requires various methods and private functions. You may use different namespaces to provide various methods for your plug-ins, but it is best not to confuse your source code with excessive namespaces.
The following code defines a JSON object that stores public methods and shows how to use the main function in the plug-in to determine which methods are called, and how to make the method work on each element of the selector.
(Function ($) {// in our plug-in container, create a public variable to construct a private method var privateFunction = function () {// code here} // create an object by literally, and store the common method var methods ={// define each separate method in the literal object init: function () {// for better flexibility, the independent function is implemented and the code return this is executed for each element of the selector in each method. each (function () {// create a jQuery object var $ this = $ (this) for each independent element; // execute code // For example: privateFunction () ;}) ;}, destroy: function () {// return this for each element of the selector. each (funct Ion () {// Execution Code}) ;}}; $. fn. pluginName = function () {// obtain our method. Unfortunately, if we use function (method) {} for implementation, this will destroy all var method = arguments [0]; // check whether the method exists if (methods [method]) {// if the method exists, save it for use // Note: I am doing this to make it easier to use each () method = methods [method]; // if the method does not exist, check whether the object is a JSON object or the method is not passed in.} else if (typeof (method) = 'object' |! Method) {// if we input an object parameter or there is no parameter at all, the init method will be called method = methods. init;} else {// if the method does not exist or the parameter is not passed in, an error is returned. The method to be called is not called correctly $. error ('method' + Method + 'does not exist on jQuery. pluginName '); return this;} // call the method we selected // once again, pay attention to how we transfer each () from here to the return method of each separate method. call (this) ;}} (jQuery );
Note that I regard privateFunction as a global variable in a function. Considering that all code runs in the plug-in container, this method is acceptable because it is only available in the scope of the plug-in. In the main function of the plug-in, I checked whether the method pointed to by the input parameter exists. If the method does not exist or the input parameter is an object, the init method will be run. Finally, if the input parameter is not an object but a non-existent method, an error message is returned.
Also note how I use this. each () in each method. When we call method. call (this) in the main function, this is actually a jQuery object, which is passed into each method as this. Therefore, in the instant scope of our method, it is already a jQuery object. It is necessary to wrap this in a jQuery object only in functions called by each.
The following are examples of usage:
/* Note that these examples can run correctly in the current plug-in code. Not all plug-ins use the same code structure * // name each class ". execute the init method $ ('. classname '). pluginName (); $ ('. classname '). pluginName ('init '); $ ('. classname '). pluginName ('init ', {}); // input the "{}" object to the init method as the function parameter $ ('. classname '). pluginName ({}); // input the "{}" object to the init method as the function parameter. // name each class ". execute the destroy method $ ('. classname '). pluginName ('deststroy'); $ ('. classname '). pluginName ('deststroy', {}); // pass the "{}" object to the destroy method as the function parameter. // all codes can run normally. $ ('. classname '). pluginName ('init ', 'argument1', 'argument2 '); // pass "argument 1" and "argument 2" into "init" // incorrect usage $ ('. classname '). pluginName ('nonexistantmethod'); $ ('. classname '). pluginName ('nonexistantmethod', {}); $ ('. classname '). pluginName ('argument 1'); // you will try to call the "argument 1" method $ ('. classname '). pluginName ('argument 1', 'argument 2'); // calls the "argument 1", "argument 2" method $ ('. classname '). pluginName ('privatefunction '); // 'privatefunction' is not a method
In the preceding example, {} appears multiple times, indicating parameters in the input method. In this section, the above Code can run normally, but the parameters are not passed into the method. Continue to the next section and you will know how to pass parameters to the method. Set plug-ins: Many plug-ins support parameter input, such as configuration parameters and callback functions. You can pass in JS key-Value Pair objects or function parameters to provide information for methods. If your method supports more than one or two parameters, there is no more appropriate way than passing in object parameters.
(Function ($) {var methods = {init: function (options) {// execute the return this method on each element. each (function () {var $ this = $ (this); // create a default setting object var defaults = {propertyName: 'value', onSomeEvent: function () {}} // use the extend method to create a settings object var settings = $ from the options and ults objects. extend ({}, defaults, options); // execute code}) ;}}; $. fn. pluginName = function () {var method = arguments [0]; if (methods [method]) {me Thod = methods [method]; // Our method is passed in as a parameter and deleted from the parameter list, because it is not required to call the method arguments = Array. prototype. slice. call (arguments, 1);} else if (typeof (method) = 'object' |! Method) {method = methods. init;} else {$. error ('method' + Method + 'does not exist on jQuery. pluginName '); return this;} // use the apply method to call our method and input the return method parameter. apply (this, arguments) ;}} (jQuery );
As shown above, an "options" parameter is added to the method, and "arguments" is also added to the main function. If a method has been declared, the parameters that call the method will be deleted from the parameter list before the parameter is passed in to the method. I used "apply ()" to replace "call ()". "apply ()" is essentially the same job as "call, but the difference is that it allows parameter input. This structure also allows the input of multiple parameters. If you want to do this, you can also modify the parameter list for your method, for example, "init: function (arg1, arg2) {}".
If you use a JS object as a parameter, you may need to define a default object. Once the default object is declared, you can use "$. extend is used to combine the values of parameter objects and default objects to form a new parameter object (in our example, "settings ");
Here are some examples to demonstrate the above logic:
var options = { customParameter: 'Test 1', propertyName: 'Test 2'} var defaults = { propertyName: 'Test 3', onSomeEvent: 'Test 4'} var settings = $.extend({}, defaults, options);/*settings == { propertyName: 'Test 2', onSomeEvent: 'Test 4', customParameter: 'Test 1'}*/
Save settings: Add persistent data
Sometimes you want to save settings and information in your plug-in. In this case, the "data ()" function in jQuery can be used. It is very simple to use. It will try to get the data related to the element. If the data does not exist, it will create the corresponding data and add it to the element. Once you use "data ()" to add information for the element, make sure you have remembered that when you no longer need data, use "removeData ()" to delete the corresponding data.
(Function ($) {var privateFunction = function () {// Execution Code} var methods = {init: function (options) {// execute the return this method on each element. each (function () {var $ this = $ (this); // try to get settings. If it does not exist, return "undefined" var settings = $ this. data ('ininname'); // if the settings fails to be obtained, create it based on options and default. if (typeof (settings) = 'undefined') {var defaults = {propertyName: 'value', onSomeEvent: function () {}} settings = $. Extend ({}, defaults, options); // Save the newly created settings $ this. data ('ininname', settings);} else {/if we have obtained settings, merge it with options (this is not necessary, you can choose not to do so) settings = $. extend ({}, settings, options); // if you want to save options every time, you can add the following code: // $ this. data ('ininname', settings);} // execute code});}, destroy: function (options) {// execute code return $ (this) in each element ). each (function () {var $ this = $ (this); // run the code // Delete the data corresponding to the element $ This. removeData ('ininname') ;}, val: function (options) {// The Code passes. eq (0) is used to obtain the first element in the selector. We can also obtain its HTML content as our return value var someValue = this.eq(0).html (); // return value return someValue ;}}; $. fn. pluginName = function () {var method = arguments [0]; if (methods [method]) {method = methods [method]; arguments = Array. prototype. slice. call (arguments, 1);} else if (typeof (method) = 'object' |! Method) {method = methods. init;} else {$. error ('method' + Method + 'does not exist on jQuery. pluginName '); return this;} return method. apply (this, arguments) ;}} (jQuery );
In the above code, I checked whether the element data exists. If the data does not exist, "options" and "default" will be merged to form a new settings, and then saved in the element with "data.