My jQuery plugin to end all plugins :)
Concept is similar to power “multi sockets” that every one has seen. The plugin I have created is conceptually an “multi socket”. Where you (the user) are writing the “plugs”, to be inserted in this “multi socket” in order to connect to the jQuery power!
Another (useful) way of looking at it is ‘anonymous plugins’. You do not have to ‘pollute’ jQuery space just to use this simple plugins that you often need. Using the Plug concept you can write a code like you are ‘inside’ the plugin, while not creating a plugin. jQuery plugin is often too heavy for what you want to do.
I guess this (light plugin) concept, probably best suits small to medium-small plugins. Or simply ad-hoc plugins, or code that is best packaged as a simple jQ plugin.
First example is one trivial Plug that changes the css ‘right’ of the element selected. And that is a very good example of the reason of existence of jqSocket: anonymous Plugs that operate by connecting to the power of jQuery. Here is the jsFiddle of this very simple Plug.
[jsfiddle url=”http://jsfiddle.net/dbjdbj/XCvs9/” width=”100%” height”200px” include=”result,html,js,css” font-color=”39464E” menu-background-color=”FFFFFF” code-background-color=”f3f5f6″ accent-color=”1C90F3″]
By using Sizzle extensions explained shortly, we first select the button with id ‘#specimen’ and we crucially mention the css property ‘right’. Then in the Plug we give value ‘400’ to the property ‘right’ and we return true. Which moves the button.Clear? Perhaps better and bit more involved Plug is the second one.[wp-js-fiddle url=”http://jsfiddle.net/dbjdbj/GfnZU/” style=”width:100%;height:400px;border:solid #4173A0 1px;”]Here, first we collect() what we have selected with jQuyery, using our special extensions so that we can select on css properties. Then we transform the result object into the string which we alert(). A bit more explanation on this example follows shortly.
Use Sizzle to select on css, data, metadata and events
To improve (considerably) the usefulness of the Plug mechanism, I have also, used the extensions to the Sizzle attribute selectors, written by one Mr Balazs Endresz. Users of these extensions have more detailed level of granularity, available when constructing jQuery selectors. To explain it, it is best to show here the core of this Sizzle extender.
1 2 3 4 5 6 7 8 9 |
(function($) { //Sizzle.selectors.attrPrefix extensions $.extend($.expr.attrPrefix, { ':': function(e, val) { return $(e).data(val); }, '~': function(e, val) { return $.curCSS(e, val); }, '&': function(e, val) { var d = $.data(e, 'events'); return d && d[val]; }, '::': function(e, val) { var d = $.data(e, 'metadata'); return d && d[val]; } }) })(jQuery); |
This attribute selection extensions are giving us the ability to use four more attribute prefixes for creating fine granularity selection stacks.
- ‘:’ — get data on the element selected, $(“[:’mydata’]”, button)
- ‘~’ — css property selection, $(“[~width]”, button)
- ‘&’ — ‘events’ selection, $(“[&click]”, button)
- ‘::’ — ‘metadata’ data selection, $(“[::’myKey’]”, button)
In essence this is attribute selection extended to include css properties, data, events and metadata. That is why we are able to construct the kind of selector bellow, to be used for this Plugs . By selecting on exact css properties we are looking for:
1 2 3 4 5 6 7 |
/* select all dom elements that have attributes 'id', 'bottom', 'width' and 'height' where 'width' and 'height' are not having a value: 'auto' */ $('[id][~bottom][~width!=auto][~height!=auto]', document.body ) |
This is important concept to understand. What we are asking for is what jqSocket latter uses to make and send Packet of arguments to the Plug we have in mind to write. In the second example we have written a simple Collector Plug. What are we collecting with this Plug, is what we have carefully selected before that, on the same instance of jQuery. In this case we are interested to collect ‘bottom’, ‘width’ and ‘height’ of all elements selected with previous jQ selection:
1 2 |
/* collector() returns a Plug function */ .S( collector('bottom','width','height') ) ; |
collector() is actually a factory method which produces (returns) the Plug function itself. Through collector() we first state what do we want to collect, and then we produce a Plug method which will do the actual collection. Plug is nothing more but a visitor, that is made to visit the jQuery selection stack and collect from each element whatever it is made to collect during its making. Perhaps too complicated and unrelated to jqSOCKET but still nice and generic implementation, useful to some.
The Plug
the ‘Plug’ is simply a function with a single argument (‘P’ argument is for ‘Packet’ in samples ). Plugin has to return ‘false’ or ‘true’. If it returns true. jqSocket will change the state of the element (or elements) selected by the original selection.
1 2 3 4 5 6 7 8 |
/* the Plugin footprint */ function ( Packet ) { return true ; /* signal to jqSocket to change the selection stack by using the new values of the properties in the Packet return false, of course signals the opposite. */ } |
The Packet
Note that Packet argument of the Plug has only those properties that are mentioned in the selector.
Properties, attributes, data, metadata, events, which are not in the current selector will not exist in the Packet, and this Plugs will not see them and will not have them available. The Packet given to the Plug is the objectified jQuery selector. Nothing more. Packet structure and format is this:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
Packet:= { /* first is set of nvo's where each n.v.obj from the selector string is parsed and stored by it's name nvo is "Name Value Object" */ "nvo_name" = { parsed : { /* parsed selector of only this nvo */ prefix: /* or '' if not used */ , name : /* of the nvo */ , operator : /* '' if not used */, operand : /* '' if not used */ } , value : /* of this nvo */, unit : /* px,em,% etc, or '' if not used */ } , /* repeat the above for each attribute, css prop, data, metadata or event data used in a selector */ select : /* the jQ selector part, left of the first '[' in it, see the example bellow */ , where : /* the WHERE part of the selector */ version : /* DBJ*jqSOCKET V:13.1 */ } |
Example makes all of the above clear :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
/* For this call :*/ $("[status][~top!=auto]",".person") /* jQuery makes this */ $.selector := ".person [status][~top!=auto]" /* jqSOCKET creates this Packet, as a single argument for all Plugs created */ Packet := { /* the 'status' attribute from the selector */ status : { parsed : { prefix: '', name: 'status', operator: '', operand: '' } , value : 'done' , unit : '' }, /* the 'top' css property from the selector */ top : { parsed : { prefix: "~", name: 'top', operator: '!=', operand: 'auto' } , value : '100' ,unit : 'px' }, select : ".person" , where : "[status][~top!=auto]", version : "DBJ*jqSOCKET V:13.1" } |
Please remember that for this instance of jQuery, all the Plug’s will have this one Packet. And Packet is made from selectors. And most importantly only attribute selectors (with or without extensions) are considered as Name-Value-Objects (aka NVO’s). So, once more :
1 2 3 4 5 6 7 8 9 10 11 12 13 |
$("#specimen [~ right!=auto]") /* Plug 1 */ .S(Packet) { /* css property 'right' is the NVO available */ Packet.let("right", 200); return true; /* change css 'right' for whatever is on the selection stack */ }) /* Plug 2 */ .S(Packet) { /* css property 'right' is the NVO available */ alert(Packet.right.value); return false; /* change nothing */ }) ; |
Here is a little test bed for you to try it. It contains both examples used above. Admittedly this looks complex, but it really is not. As soon as you understand you are writing the plug, not the plugin.
Feel free to think of your Plug and try it in here. If it can be done in here it is meant to be a Plug, and you got the concept right. If not, then it is meant to be a full Plugin.