Dynamic Extension Redefinition
As part of eCSStender 1.2.6.1, we now have the ability to dynamically redefine an extension’s callback function. This makes it possible to optimize extensions that require forking based on the environment or to trigger an initialization that later extensions will need in order to work.
Using this technique to manage code forking is dead simple. Consider the following as a callback function:
function ( selector, properties, medium )
{
function option_1( selector, properties, medium )
{
// code for one implementation
}
function option_2( selector, properties, medium )
{
// code for another, completely different, implementation
}
var func = eCSStender.isSupported( /* test conditions */ ) ? option_1
: option_2;
func( selector, properties, medium );
return func;
}
While non-functional, this callback function takes you through a real example of how you can use callback redefinition to optimize your code execution. Here’s what’s happening:
- two alternate implementations are defined (
option_1()
andoption_2()
); - a test is made (probably using
isSupported()
, but not necessarily) and determines which implementation should be used, assigning it to the variablefunc
; func()
is called once (since a callback needs to be executed on the first run of this extension); and- the implementation is returned, redefining the callback as either
option_1()
oroption_2()
for any remaining executions.
The one really important thing to keep in mind is that you’ll need to make sure your alternate implementations are set to accept any of the arguments you need from the extension (selector
, etc.). If you forget to define them within the new callbacks, they won't be available.
Using this technique to manage initialization actions is pretty easy as well. Consider this snippet implementing :checked
(from the current version of the CSS3 Selectors bundle). Note: There are some aliases in this sample that reference variables in the extension bundle, such as DOC
which is a pointer to document
, e
which is an alias for eCSStender
, etc.
(function(){
var
the_class = e.makeUniqueClass(),
the_regex = /:checked/,
classify = function()
{
var
inputs = DOC.getElementsByTagName('input'),
j = inputs.length;
while ( j-- )
{
if ( inputs[j].checked )
{
e.addClass( inputs[j], the_class );
}
else
{
e.removeClass( inputs[j], the_class );
}
}
};
e.register(
{ fingerprint: EASY + 'checked',
selector: the_regex,
test: function(){
var
d = div.cloneNode(TRUE),
i;
d.innerHTML = '<input checked="checked" type="checkbox" />';
i = d.getElementsByTagName('input')[0];
return ! supported( SELECTOR, 'div input:checked', d, i );
}
},
EVERYTHING,
function( selector, properties, medium, specificity ){
// initialize
classify();
// only add the event once
addEvent( DOC.body, CLICK, function( e ){
var el = e.target;
if ( el.nodeName.toLowerCase() == 'input' &&
( el.getAttribute('type') == 'radio' ||
el.getAttribute('type') == 'checkbox' ) )
{
classify();
}
});
// then switch to embed a modified selector
function modify( selector, properties, medium, specificity )
{
selector = selector.replace( the_regex, DOT + the_class );
embed( selector, properties, medium );
}
modify( selector, properties, medium, specificity );
return modify;
});
})();
Walking through this example, we see
- the creation of a unique
class
for use by the script (one that isn’t likely to be repeated); - the definition of a regular expression for use in the script; and
- the definition of a function called
classify()
that runs through the document'sinputs
and assigns or removes the uniqueclass
based on whether or not theinput
is currently checked.
The remainder of this snippet is devoted to the extension registration itself. Skipping down to the callback function, you can see it runs classify()
immediately (to establish a baseline setup) and then adds an event listener to the body
that picks up click events and acts upon them, by calling classify()
again, if the target element is an input
of the checkbox or radio variety. Then a function called modify
is created that converts the selector for :checked
into a class
-based selector utilizing the unique class
created at the beginning of the snippet. Finally that function is called once and then returned, making modify()
the new callback function.
What this all amounts to is that we can have certain setup actions take place only once (the first time the extension is run) and then adjust how the extension behaves for all subsequent calls. It’s a powerful technique that really helps optimize code execution because the setup actions are only run if an when the extension is needed and then, only once.