How to Create Your Own jQuery Plugin
jQuery is one of the most popular JavaScript frameworks ever. At first, it was just a library to provide CSS selectors for JavaScript but after Dean Edwards released his excellent cssQuery library, jQuery's development shifted in a different direction. The author of the jQuery library is John Resig.
But enough with jQuery theories, let's go programming now. As the title of
this article suggests, we're going to write our own jQuery plug-in. You have
surely already stumbled across these plug-ins. We call them as any other
function in this library, for example like this:
$(element).function()
.
There are plenty of plug-ins on the Internet, maybe that's why the framework is so successful. However, it may happen that you won't be happy with any of the available plug-ins, perhaps because of its license. The license doesn't have to be open source by all means, so you can see paid plug-ins as well. You can also develop your own paid plug-ins of course. This article is an introduction to creating custom jQuery add-ons. The article is based on the development of a real plug-in - a simple content slider.
HTML & CSS structure
When writing our own plug-in, we need to come up with an HTML structure and
style it. We want to make a content slider, so the best for us would be to use a
list, i.e. the <ul>
element. We'll style it using classes
which will assign the plug-in by itself. Here is a small example of the HTML
structure with basic CSS styles:
The HTML structure
<ul> <li>The content of the first slide</li> <li>The content of the second slide</li> </ul>
CSS styles
/** * All slides */ ul.easySlider-content { padding: 20px; margin-bottom: 25px; position: relative; height: 250px; z-index: 100; -webkit-box-shadow: 0 0 10px #aaa; -moz-box-shadow: 0 0 10px #aaa; box-shadow: 0 0 10px #aaa; border: 1px solid #dcdcdc; background: #ffffff; background: url(data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiA/Pgo8c3ZnIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgdmlld0JveD0iMCAwIDEgMSIgcHJlc2VydmVBc3BlY3RSYXRpbz0ibm9uZSI+CiAgPGxpbmVhckdyYWRpZW50IGlkPSJncmFkLXVjZ2ctZ2VuZXJhdGVkIiBncmFkaWVudFVuaXRzPSJ1c2VyU3BhY2VPblVzZSIgeDE9IjAlIiB5MT0iMCUiIHgyPSIwJSIgeTI9IjEwMCUiPgogICAgPHN0b3Agb2Zmc2V0PSIxJSIgc3RvcC1jb2xvcj0iI2ZmZmZmZiIgc3RvcC1vcGFjaXR5PSIxIi8+CiAgICA8c3RvcCBvZmZzZXQ9IjEwMCUiIHN0b3AtY29sb3I9IiNlZWVlZWUiIHN0b3Atb3BhY2l0eT0iMSIvPgogIDwvbGluZWFyR3JhZGllbnQ+CiAgPHJlY3QgeD0iMCIgeT0iMCIgd2lkdGg9IjEiIGhlaWdodD0iMSIgZmlsbD0idXJsKCNncmFkLXVjZ2ctZ2VuZXJhdGVkKSIgLz4KPC9zdmc+); background: -moz-linear-gradient(top, #ffffff 1%, #eeeeee 100%); background: -webkit-gradient(linear, left top, left bottom, color-stop(1%,#ffffff), color-stop(100%,#eeeeee)); background: -webkit-linear-gradient(top, #ffffff 1%,#eeeeee 100%); background: -o-linear-gradient(top, #ffffff 1%,#eeeeee 100%); background: -ms-linear-gradient(top, #ffffff 1%,#eeeeee 100%); background: linear-gradient(top, #ffffff 1%,#eeeeee 100%); filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#ffffff', endColorstr='#eeeeee',GradientType=0 ); } ul.easySlider-content li { margin-right: 20px; } /** * Active slide */ ul li.active { display: block !important; } /** * Navigation arrows */ a.arrowLeft { display: block; width: 70px; height: 70px; background: #fff url('../images/arrow_left.png') 3px center no-repeat; position: absolute; margin: 140px -48px; border-radius: 50px; border: 5px solid #dcdcdc; } a.arrowRight { display: block; width: 70px; height: 70px; background: #fff url('../images/arrow_right.png') 18px center no-repeat; position: absolute; margin: 140px 770px; border: 5px solid #dcdcdc; border-radius: 50px; } a.arrowLeft:hover, a.arrowRight:hover { background-color: #dcdcdc; }
jQuery.fn
jQuery.fn
is, you could say, a namespace for your plug-ins. All
functions placed in this object can be called directly from jQuery. The context
of these functions is the currently selected element. But what is the functions'
context? Each function in JavaScript is processed in some context. Simply put,
it's the this
keyword. Therefore, if we put a function into
jQuery.fn
and call our plug-in, for example like this:
$('#content').myFunctionName()
, the this
keyword will
contain the element with id="content"
. However, it'll be accessible
as an HTMLElement
object, not as a jQuery object. If we'd like to
use it as a jQuery object, we'd simply pass this
as the jQuery
object's parameter: jQuery(this)
or $(this)
(both
notations do the same).
Now that we know where to put our plug-in, we can start coding. In general,
it's best to wrap our plug-in in an anonymous function to prevent variables from
being overwritten and so. It's also better because the $
character
is used by other libraries (not only by jQuery) as a wildcard, and we don't need
to care about this in anonymous functions. We should also consider how to call
additional functions within the plug-in and how to handle user settings. I will
now give a simple example of a basic plug-in with a function calling logi. We're
gonna explain everything in a moment:
// Our anonymous function - the only parameter is $ which represents the jQuery object (function( $ ) { // The methods that can also be called by the user will be placed here var methods = { // Initialization method - is called from the main function init: function( o ) {} }; // Input function of the plug-in $.fn.easySlider = function( method ) { // If we have the name of the function we want to call if ( methods[method] ) { // We call the given function with all the other plug-in arguments passed return methods[method].apply(this, Array.prototype.slice.call(arguments, 1)); } else if ( typeof method == 'object' || !method ) { // If not, we call the initialization method return methods.init.apply(this, arguments); } else { // If the method doesn't exist, we print an error $.error('Method ' + method + ' doesn\'t exist in jQuery.easySlider plugin'); } }; // Default settings $.fn.easySlider.defaults = { }; })( jQuery ); // we call the functions with the jQuery parameter
We created an easySlider()
function in the
jQuery.fn
object. Therefore, we can call it like e.g.:
$('ul#slider').easySlider()
.
But what if we'll need to set something up or to call an internal function of
the plug-in? It's very impractical to change the plug-in's code itself. Such
codes are also often minimized so it might not be even possible. However, the
example above takes this into account as well. As an argument, we can specify
either an object with all the plug-in settings, or a name of one of the
methods
object functions, or nothing and the plugin calls
methods.init()
by itself. This is just one way to make the function
calling logic. It could have been done differently too. For us, however, this is
very practical, so we don't have to deal with it anymore.
Lastly, we have the defaults
object. The default settings are
stored in it. This is for the user so they won't have to set everything, just
what they want differently.
Before we start with the plug-in itself, I'd like to clarify what the
apply()
function does. Every function in JavaScript has some
context in which it's being processed, as I've already mentioned above. The
apply()
function calls a given function within a context we choose.
It takes two arguments, the first is the context. We set it to
this
, which is the currently selected element. The second argument
is an array through which we pass all the function's arguments.
We use the arguments
variable, which is an array of all the
function arguments.
Initialization method
We have the basis so let's initialize each element. The only argument will be
an object carrying the user settings. However, to ensure that all the settings
are in this object, we'll extend defaults
by this object. Again, I
will introduce the entire code of the function and then describe it:
// Initialization method - is called from the main function init: function( o ) { // Gets the plug-in settings o = $.extend({}, $.fn.easySlider.defaults, o); // Saves the current context of the function (which is the plug-in element) var $that = $(this); // Functions called once the arrows are clicked var left_click = function( e ) { e = $.event.fix(e); e.preventDefault(); $that.easySlider("prev"); }, right_click = function( e ) { e = $.event.fix(e); e.preventDefault(); $that.easySlider("next"); }; // We go through all the elements passed and initialize the plug-in for them return this.each(function() { // Finds all images in the slider var $items = $(this).find('li'), count = $items.length, $self = $(this); // Hides them all and positions them absolutely $items.css({ display: 'none', position: 'absolute' }); // Assigns a class to the list to make it easier to manipulate with from CSS $self.addClass('easySlider-content').data('speed', o.speed); // Makes the first element active $($items.get(o.active)).addClass("active"); // Creates the arrows on the sides for the slider navigation var $arrowLeft = $('<a />').attr('href', '#left').addClass('arrowLeft'); var $arrowRight = $('<a />').attr('href', '#right').addClass('arrowRight'); // Sets the onclick callback $arrowLeft.bind('click', left_click); $arrowRight.bind('click', right_click); // Inserts the arrows before the slide list $self.before( $arrowRight ); $self.before( $arrowLeft ); }); }
Right on the first line there's the jQuery.extend()
function
used. This function extends any number of objects where the lastly provided
objects have a higher priority. The first argument is the target object, where
we want to store the result (for more information see the official documentation of
jQuery.extend). And now in English. We take the variables and functions from
the defaults
object and if there is a function of the same name or
a variable in the o
object, we overwrite its value with the value
from the o
object. This ensures that we don't have to determine
whether the user has specified all the options, that would be too complicated.
Then there's a $that
auxiliary variable. Except it makes the code
more readable and probably saves us fractions of a second to write it, there are
also other functions defined in methods.init()
with a different
context. And in these functions, $(this)
would mean something
totally different. Therefore, we need to store it in a variable that is also
accessible from these functions.
The whole function returns this
, the selected elements again,
and performs the plug-in settings for each element. So let's discuss the code in
detail. Since we assume that the slider's structure will be a list
(<ul>
and <li>
in HTML), we find all the
<li>
elements and store them in the $items
variable. Then there's an auxiliary variable holding the number of the
<li>
elements (i.e. the slide count) and again, the
$self
auxiliary variable, in which is the context of the function
we're working within. That's because the context has changed by using
jQuery.each
as you might have noticed.
We've already loaded it all into variables, so we set them up. First of all,
we hide all the contents and set the absolute position to it. This is to keep
the contents together during the transition. Additionally, we add the
.easySlider-content
class to the <ul>
element
and set data-speed
to it to the value specified by the user. We do
that so we can read it further in another function, but we'll get there
later.
Now we create buttons to switch to the next or previous slide. We assign the
class to them and place them before the slider. I've intentionally skipped
bind('click')
. We have to tell the script what to do if the arrows
are clicked. We use the left_click()
and right_click()
functions we defined earlier. We defined these function before
return
so we don't have to define them a hundred times, which could
slow the whole script down. In this function you should define only the most
necessary code which must really be done for each element. What I would like to
emphasize in these functions is using $.event.fix(e)
. As you may
know, in some browsers you have to access event variables differently or the
parameters are named differently, so we have to be careful. However, only few
people know that this jQuery function deals with this for us so we don't have to
worry about it anymore. Then we just use e.preventDefault()
to
prevent the default event from occurring (links would normally redirect to their
href
addresses). At the end of the function, we call
methods.next()
through our calling logic. A synonym for this call
is methods.next.apply(that);
, where that
is the
context of the init()
function.
Public and private functions
As already mentioned, all the functions in the methods
object
are callable from the outside of our plug-in as
$(element).easySlider("function_name", ["first parameter", "second parameter", ...]);
(the brackets indicate only that these arguments are optional - we don't write
them in our script!). Therefore, the functions are public. We can also create
private functions by storing them outside the methods
object, in
the same anonymous function. Such functions would be accessible only within our
anonymous function and would not be accessible from the outside. In our case, we
don't use private methods, but these can be very useful sometimes.
Now there are only three functions left. One sets the active layer and
simultaneously performs the animation (sliding) based on the index passed. Let's
call it active()
. The next two are prev()
and
next()
which calculate the index of the previous, resp. the next
slide and pass it to the active()
function.
// This function sets the active slide (The index is zero-based and corresponds with the number of gallery images) active: function( index, direction ) { // Sets the speed speed = $(this).data('speed') || 800; // Sets the effect directions directionHide = direction || 'left'; directionShow = directionHide == 'left' ? 'right' : 'left'; // Hides the active item $(this).find('li.active').hide('slide', { direction: directionHide }, speed); // Removes the .active class from all the items $(this).find('li').removeClass('active'); // Loads the active slide var slide = $(this).find('li').get(index) || false; // Displays it $(slide).addClass('active').show('slide', { direction: directionShow }, speed); // Returns the active element return $(this).find('li').get(index) || false; }, // Moves to the next slide next: function() { // Finds the next element and its index to which we add +1 var index = ($(this).find('li.active').index()+1); // Activates the slide if exists. If not, automatically moves to the first one (with index 0) return $(this).easySlider("active", ($(this).find('li').get(index) ? index : 0)); }, // Moves to the previous slide prev: function() { var index = $(this).find('li.active').index()-1 < 0 ?$(this).find('li').length-1 : $(this).find('li.active').index()-1; // Activates the slide with this index return $(this).easySlider("active", index, 'right'); }, /** The init method... */
$(element).easySlider(active, index, direction)
All we have to do is to describe these three methods and our plug-in is done.
The first active()
function activates the element we find by the
passed index (see the first parameter of the function). The second parameter,
direction
, is optional (it's either 'left'
or
'right'
), defining the direction in which the slider moves. The
function to display and hide the element uses
jquery.effects.core.js
from the jQuery UI library. Therefore, this
plug-in depends on it. If you want to use the pure jQuery only, you can replace
them with fadeIn()
and fadeOut()
or by
slideDown()
and slideUp()
or simply by
animate()
.
$(element).easySlider(next)
Moves the slider to the next element. If there isn't one, it moves it to the
beginning. Calls the active()
function. The prev()
function is the same except that it moves to the previous or the last
element.
Final tips
- Minimize your code, but offer normal versions as well (if you don't want to create commercial plug-ins). This will allow your plug-ins to be extended and modified by others unless it conflicts with your license. To minimize the code, I recommend the Dean Edwards' script - Packer
- Put as many things as possible into the custom settings. Then write detailed documentation for these settings.
- Try not to set CSS styles much. Leave it on external files. Keep only the necessary styles in the script.
- Write unobtrusive code. You don't want the users to have to add HTML, JavaScript can do that too
- Before releasing, test your work in as many browsers as possible. If some browser doesn't support it, include this information in your documentation.
Download
By downloading the following file, you agree to the license terms
Downloaded 12x (113.35 kB)
Application includes source codes in language JavaScript