One Plugin to end all jQuery Plugins



Well, I could not resist this catchy title, could I? Perhaps I could. In any case I have this idea and implementation where one jQuery plugin is sort-of-a “socket” (not a network socket, but a socket in the wall) into which you can plug-in your “plugs” which will do standard jQuery related stuff.

Where “standard jQuery related stuff” is either changing the state of the DOM behind, or either using the information from the DOM to do something with it.

Meta Plugin
Meta Plugin

Example of the former is much loved “animation” stuff one can do with jQuery. Example of the latter, could be collecting some information from the DOM entities selected with the jQuery and doing something with that information. Sending it to some server or just displaying it would qualify as “doing something with the information”.

As it turns out it is easier to use jQuery for changing the state of the dom, then it is to use information that it can provide from the DOM, of the current page. This is because the latter involves your code which has nothing to do with the stuff that jQuery does. Strictly speaking this is not “difficult” , it is just not “naturally flowing” as jQuery chains of commands do.

// while we are chaning the state of the dom 
// that jQuery instance is referencing 
// all is nicely flowing
// including having the plugins in the chain, 
// which changes
// the state of the elements referenced
var $JQ = $("some selector")
       .some_font_plugin() ; 
// ad infinitum. But.
// getting the stuff from the elements 
// through jQuery , breaks the chain
var name = $JQ.find(":last").
      .data("name") ; // have to stop here
var guts = $JQ.html() ; // and here
var msg = $JQ.text() ; // and here

Perhaps this looks a bit trivial, and you are perhaps wondering what am I going on about here, and why are you reading this post? You are demanding it,  and now I have to find an convincing use-case , to er… convince you. Ok, here we go. Consider writing a code that will “blanket” any user selected number of elements on the page, by placing one div “over” it. This div has to be just big enough to cover all the elements selected. We can make it transparent and/or write some text on it etc … Convincing enough for You? Good. So we have this clever algorithm which given set of overlapping squares, returns one square position and dimensions that “blankets” them all. Obviously we can imagine an plugin that does all of this. Walks through the current “stack” of selected elements, collects positions and dimensions of each , etc … And that is the point: plugin is required.

Each time you want to take something “out” form the jQuery object it is best to write a plugin that will take that “thing out” and then return it, or do something with it, and then return the jQuery instance (doing: return this;) so that Your beloved chaining is not broken.

// a general plugin structure for taking "things out" 
// without breaking a chain
jQuery.fn.extend ( {
use_and_proceed : function ( 
) {
// somehow obtain the information
var the_information = 
      (description_of_a_information_to_take_out) ;
// use the information obtained
return this; /* so that chain can proceed */
} ) ;
// usage
       "length", alert 
  ).hide("slow").css("top", 123).hide(500) ;
      ":last[id*='something']", alert 
 ).hide("slow").css("top", 123).hide(500) ;
  ":last[~width]", collect_widths ).hide("slow"
  ).css("top", 123).hide(500) ;
// Nice. Our application logic is satisfied and jQuery 
// chains are nice and clean.... ?
// Oops ?! Can we write generic 
// somehow_obtain_the_information ()
// , inside the use_and_proceed()
// plugin that will serve all the above ?
// And everything else that can be asked for ?

If we could do the above we could have one (very) nice framework that would allow us to re use the plugin above for virtually “anything”. The actual solution will be in the callback given to the plugin. For example we could try solve the “blanketing” ?

function blanketing ( position, dimensions ) {}
$("whatever").use_and_proceed (
   "blanketing position and dimensions", blanketing 
  ) ;
// Hm .. what information do we get out and how ?
// use_and_proceed has to be generic ... 
// Maybe we can change it and give "this" to callbacks ?
// for even more usefulness we can optionally 
// select a subset form the current stack 
// referenced by jQuery object
jQuery.fn.extend ( {
use_and_proceed : function 
( subset_selector, callback ) 
if ( subset_selector ) {
// find and use the subset
callback( this.find( subset_selector ) ) ;
} else {
// or use the current jQuery instance
return this; /* so that chain can proceed */
} ) ;
// this will work for every callback 
// that understands jQuery object
// Hurah ?! We have generic "mother of all plugins" ! 
// Just give it different callbacks 
// for different tasks ...
function blanketing ( jq_object ) {}
$("whatever").use_and_proceed ( blanketing ) ;

Lovely … But. Will this work ? For starters, call to the callback() above may block the plugin or throw an exception. Fine. We can solve that.

// Appache 2.0 (c) 2010-2018
//  by
jQuery.fn.extend ( {
use : function ( 
) {
var THAT = null ;
error_handler || ( error_handler 
    = (window.console || window.alert) ) ;
if ( subset_selector ) {
// find and use the subset
THAT = this.find( subset_selector ) ;
} else {
// or use the current jQuery instance
THAT = this ;
var tid = setTimeout( function () {
try {
callback.apply(THAT) ;
} catch(x) {
} , 1 ) ;
return this; 
/* so that chain can proceed */
} ) ;

Renamed it to “use”. This will execute the callback on a different call-path (not thread), and will also handle the exception thrown , if any. error_handler is either user defined or console or alert.

function c1 ( ) { alert( this.html() ); }
// use c1 on first element from the current stack
$("#toolbar").use(c1,":first") ;
// use c1 on the current stack
$("#toolbar").use(c1) ;