$.utility() versus $(element).method()

$.utility() versus $(element).method()

Hi All,

I'm trying to figure out the best way to organize my jQuery code. I have a simple case that I would like to discuss here to improve my, and hopefully many others', code patterns.


The case
Build a flexible 'slider (as in Slideshow)' plugin/utility, that can take multiple sliders (each having one slide visible at a time) and multiple controls (arrows, bullets). A simplified html example would be:
  1. <ul class='slider' id='main-slider'>
  2.       <li>Slide 1</li><li>Slide 2</li><li>Slide 3</li>
  3. </ul>
  4. <ul class='slider' id='slider-captions'>
  5.       <li>Caption 1</li><li>Caption 2</li><li>Caption 3</li>
  6. </ul>

  7. <div class='control-prev'>Previous</div>
  8. <div class='control-next'>Next</div>
  9. <ul class='bullets'>
  10.       <li></li><li></li><li></li>
  11. </ul>
The functionality required:
  1. Both sliders start at index 0 (showing 'Slide1' and 'Caption 1')
  2. When clicked 'previous' the previous slide is shown in both sliders (in this case 3)
  3. When clicked 'next' the next slide is shown in both sliders (in this case 2)
  4. When clicked on one of the bullets, depending on its index, the corresponding slide is shown in both sliders.
  5. Also, when a slide changes, the bullets need to be updated (so the corresponding bullet is highlighted)
  6. The markup can be anywhere in the document, so it can not be put in one container.

I would like this discussion not to be about the implementation, but only about the interface. In other words, I would like to discuss how (when this plugin is installed) you go about attaching the functionality to the DOM elements.


Consider the following three alternatives:

Option 1 - No plugin
I'm using pseudo-code to keep things clear..
  1. $('.control-prev').click(function(){
  2.       // check which slide is shown
  3.       // if the first slide, jump to the last slide (if looping is allowed)
  4.       // show the correct slide in both sliders
  5.       // highlight the correct bullet in the bullet navigation
  6.       // disable this control if looping is not allowed (e.g. hide the control)
  7. });
  8. $('.control-next').click( /* same here */ );
  9. $('.bullets li').click( function(){
  10.       // check the index of the clicked bullet
  11.       // hide the current slide
  12.       // show the correct slide
  13.       // highlight the correct bullet
  14.       // disable the 'previous' or 'next' control if the first or last slide (resp.) is shown, and looping is not allowed
  15. });
Now imagine we have some button on the page to make slide 2 show up. We would have to check if the slide exists, show the slide in both sliders, highlight the correct bullet and possibly disable some 'previous' or 'next' controls.

Isn't there a simpler way?

Option 2 - $(element).sliderPlugin()
Again, without going into detailed implementation, this is what it would look like when you assign the plugin(s) to your elements.
  1. $('.slider').slider({allowLooping:false}); // This applies to both sliders. 
  2. $('.control-next').slideControl({slider:$('.slider'), action:'next'}); 
  3. $('.control-prev').slideControl({slider:$('.slider'), action:'prev'});
  4. $('.bullets').slideNavigation({slider:$('.slider')});
That looks a lot better, don't you think? Though the functionality may be the same as option 1, we don't have to clutter our $(document).ready function with all that code.

But this still requires 3 different plugins, and raises some flexibility questions should we want to change something about our slider. How can I manually call for the 'next' slide? Which slider is then in charge of updating the controls?

Option 3 - $.makeSlider() Utility
Imagine you could install a plugin and write the following code to build your slider:
  1. var slider = $.makeSlider({loop:false})
  2.                         .addSlider($('.slider'))      // this adds all sliders with class .slider
  3.                         .addControl($('.control-prev'), 'prev')
  4.                         .addControl($('.control-next'), 'next')
  5.                         .addControl($('.bullets'), 'index')
  6. // Whenever you want, you can call:
  7. slider.next()
  8. slider.goTo(2)
  9. // Or adjust your slider's configuration:
  10. .addSlider($('.someotherslider'))
  11. .removeControl($('.control-prev'))
  12. .allowLoop(true)
Now instead of method chaining per element, the methods are chained on the controlling object (which you get from the utility $.makeSlider()).


Comparison
It shouldn't be a surprise to you that I really prefer the third option. But because I haven't seen this pattern in many places I'm looking for some confirmation or critique from you guys.

Please let me know what you think of the third option and why it should be used or not.