Creating jQuery Plugins: As seen on the World Wide Web

Spread the word
Tweet about this on TwitterShare on Google+Share on FacebookPin on PinterestShare on RedditShare on TumblrEmail this to someonePrint this page

In our last post we created a simple plugin that changes the text of a span tag and then a bit more complex one that forces a case for all letters typed into an input field. This time around we’re going to create a plugin that utilizes JavaScript objects to further encapsulate our code so it remains independent of any other code we may have for our website or web application. In the last post I mentioned that we would be using the JavaScript class object, it should be noted that JS doesn’t actually have classes per se, but everyone fakes it using Object literals and/or JS functions. I’ll explain more on that later.

To begin with… if you wish to get the code and follow along you can either download it from here or run the following command in your terminal:

  $ git clone https://github.com/Sparkmasterflex/takei-plugin.git <project-name>
  $ cd <project-name>

Also if you want to view the final product; go here and type the following key combination:

  Scorpion - Toasty 2 fatality:
  Down arrow, Down arrow, Up arrow, Up arrow, p

Using the JavaScript <airquotes>Class</airquotes>

JavaScript Classes

So where do we start with these magical classes that don’t really exist? Actually we don’t start with them, we start as we did in the last post.

  $.fn.takei_it = (options) ->
    console.log 'do something!'

This is always where we will start when we’re doing a jQuery plugin, but lets now move on to the wonderful realm of OOJS or Object-Oriented JavaScript if you will. We’ll start by having our plugin create a new instance of our class we are going to build. Please note that I will be calling it a class from here on out and just picture me doing the air quotes each time you see the word class in relation to the JS code. So our plugin will now look like this:

  $.fn.takei_it = (options) ->
    window.takei = new TakeiIt this, options

Of course trying to run this in a browser will break, because we haven’t declared our TakeiIt class. So let’s go ahead with the next step.

  TakeiIt = (el, options) ->
    # which will break as well, so I'll continue programing till it does not break
    this.initialize el, options 

  $.fn.takei_it = (options) ->
    window.takei = new TakeiIt this, options

  $.extend TakeiIt.prototype,
    initialize: ($el, options) ->
      console.log $el
      console.log options

So above we have declared our TakeiIt class which is actually a function being passed options by our plugin. To be clear our plugin, from this point forward really is only instantiating our TakeiIt class and from there the class does all the heavy lifting. But now we have our self contained class that feels oh so right and oh so Object Oriented. Inside our TakeiIt function we call this.initialize() passing el and options.

This is where the magic really starts. $.extend takes two arguments; an object/function TakeiIt.prototype and an another object which we build as we go, creating any methods that we may need.

You Want More Than Logging?!

We’ve already created our initialize() method but all it does is log out our two parameters passed. Let’s get some useful code written so this plugin starts to take shape. Below I have written out some more functionality for our plugin, this is also in the example1 folder. Go ahead and take a look:

  TakeiIt = (el, options) ->
    # which will break as well, so I'll continue programing till it does not break
    this.initialize el, options

  $.fn.takei_it = (options) ->
    window.takei = new TakeiIt this, options

  $.extend TakeiIt.prototype,
    key_combo:  [40, 40, 38, 38, 80]
    typed:      []
    image:      "george-takei.png"
    sound_clip: "george-takei-oh-my.mp3"
    start_x:    "-300px"
    start_y:    "-100px"
    end_x:      "0px"
    end_y:      "0px"

    initialize: ($el, options) ->
      $(window).on 'keyup', (e) => @test_key_combo(e)

    test_key_combo: (e) ->
      if this.next_in_combo e.keyCode
        this.typed.push e.keyCode
      else
        this.typed = []

      if this.combo_entered()
        this.add_george()
        this.typed = []

    next_in_combo: (kc) ->
      this.key_combo[this.typed.length] is kc

    combo_entered: ->
      $(this.typed).not(this.key_combo).length is 0 and $(this.key_combo).not(this.typed).length is 0

    add_george: ->
      this.oh_my ?= new Audio("/javascripts/#{this.sound_clip}")
      this.$img = if $('img.takei-it-image').length
      then $('img.takei-it-image')
      else $("<img class='takei-it-image' src='/javascripts/#{this.image}' alt='GeorgeTakei' />")

      $('body').append this.$img
      this.$img.css
        position: 'fixed'
        bottom: this.start_y
        right: this.start_x
      this.animate_to 'end', callback: () =>
        @oh_my.play()
        @animate_to 'start', {delay: 1200}

    animate_to: (pos, options) ->
      dlay = options.delay or 0
      spd = options.speed or 400
      to = if pos is 'start' then {bottom: this.start_y, right: this.start_x} else {bottom: this.end_y, right: this.end_x}
      this.$img.delay(dlay).animate to, spd, 'swing', () -> options.callback?()

class attributes

To begin with we have some variables or attributes for our class. These variables are set as you would a key/value pair in any JavaScript object, but will be called later in our class using:

this.<var_name>

.

These variables are:

  • key_combo: keyCode(s) in the order we want the user to press
  • typed: empty array
  • image: filename of our George Takei image
  • sound_clip: filename of our George Takei mp3
  • start_x/y: x and y start position
  • end_x/y: x and y end position

initialize()

I’ve replaced our console.log() with jQuery listener for keyup and this runs a method test_key_combo()

test_key_combo()

Here we check if the key pressed is this.next_in_combo(), this method will validate that the user has begun or is continuing to type the correct key combination to trigger our surprise.

If this.next_in_combo() which we pass the keyCode attribute in the event created from the keyup returns true then we push e.keyCode to this.typed variable. Otherwise it empties this.typed

Next we check if this.code_entered() returns true, or in layman’s terms: that the user has successfully completed the key combination to trigger the event. If so, then we run this.add_george() method and reset this.typed.

Testing Methods

Both this.next_in_combo() and this.combo_entered() simply return true/false values and test exactly what they’re named.

add_george()

This method is the really nuts and bolts of the plugin. The other methods mostly set up and test whether or not to fire this method. Let’s focus on this method for a sec:

  add_george: ->
    this.oh_my ?= new Audio("/javascripts/#{this.sound_clip}")
    this.$img = if $('img.takei-it-image').length
    then $('img.takei-it-image')
    else $("<img class='takei-it-image' src='/javascripts/#{this.image}' alt='GeorgeTakei' />")

    $('body').append this.$img unless $('img.takei-it-image').length
    this.$img.css
      position: 'fixed'
      bottom: this.start_y
      right: this.start_x
    this.animate_to 'end', callback: () =>
      @oh_my.play()
      @animate_to 'start', {delay: 1200}

We set this.oh_my to a new Audio() using our sound_clip attribute and also create this.$img variable which is either an <img> tag with the class of takei-it-image already in the DOM or we create one.

Next we append our this.$img to the body element, again if not already in the DOM and then we set some CSS attributes. From here we call the this.animate_to() method passing start string and an object with callback.

The callback here calls play() on the Audio instance and runs this.animate_to() again with end string and delay value for the options.

animate_to()

This method is really just the jQuery animate() method, but I made this its own method to DRY it up a bit.

At this point we have a working plugin and it does one specific thing one specific way. Yet a lot of jQuery plugins you may have used often give you the ability to set options that change the behavior or appearance of the plugin.

How do we do this? Let’s find out.

Adding Options

In our plugin file we’re going to extend a defaults attribute to our plugin. (If you’ve downloaded or cloned the project you will be working in example2 now) Extending defaults to the plugin is done like so:

  $.fn.takei_it = (options) ->
    window.takei = new TakeiIt this, options
    this

  $.fn.takei_it.defaults =
    key_combo:  [40, 40, 38, 38, 80]
    image:      "javascripts/george-takei.png"
    sound_clip: "javascripts/george-takei-oh-my.mp3"
    position:   'bottom-right'

Pretty simple stuff. Its very similar to how we had our set them directly on our TakeiIt class in the previous example, but this way we can overwrite them with any options we pass to the plugin method.

  $(window).takei_it
    key_combo: [40, 40, 39, 39, 80]
    position: 'bottom-left'

Now our the keys that our plugin is expecting are going to be overwritten by our new key_combo and we’re setting the position from where George pops out from. Now we just need to handle the possibility of these option changes.

I added a start() and end() method to handle starting from the bottom-left corner instead of our default bottom-right. Then I altered our animate_to() to use these methods rather than start_y/x and end_y/x.

  animate_to: (pos, options) ->
    dlay = options.delay or 0
    spd = options.speed or 400
    to = if pos is 'start' then this.start() else this.end()
    this.$img.delay(dlay).animate to, spd, 'swing', () -> options.callback?()

  start: ->
    switch this.position
      when 'bottom-right' then {right: "-300px", bottom: "-100px"}
      when 'bottom-left' then {left: "-300px", bottom: "-100px"}

  end: ->
    switch this.position
      when 'bottom-right' then {right: "0px", bottom: "0px"}
      when 'bottom-left' then {left: "0px", bottom: "0px"}

At this point the only dependencies of the plugin are jQuery, of course and the two assets, george-takei-oh-my.mp3 and george-takei.png. If you wanted to use another image or sound bite from George or anyone else for that matter you could. You just need to pass the path of those assets to the plugin like so:

  $(window).takei_it
    image: "/images/my-other-takei-image.png"
    sound_clip: "/images/different-clip.mp3"

Wrapping It Up

That’s pretty much all there is to creating a jQuery plugin, they obviously can get a lot more complex than this but they can also be extremely simple as well. Basically if you’re finding yourself reusing the same jQuery/JavaScript code in multiple projects or even multiple times in one project it may be worth putting that code into a plugin.

For example, a number of my projects use a flash message, or a notification that sits at the top of the page to notify the user that something has occurred, gone wrong or just needs to be noted and I often needed these messages to be on the page for a few seconds and then go away once the user has been alerted.

I found myself using the same code repeatedly here and there $(p.flash-msg).delay(3000).slideUp 'fast', () -> $(p.flash-msg).remove(), so I made are quick plugin and shortened it to $(p.flash-msg).delay_remove(). Even one-liner bits of code can be made into plugins for easier re-use.

Spread the word
Tweet about this on TwitterShare on Google+Share on FacebookPin on PinterestShare on RedditShare on TumblrEmail this to someonePrint this page