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:

  1. two alternate implementations are defined (option_1() and option_2());
  2. a test is made (probably using isSupported(), but not necessarily) and determines which implementation should be used, assigning it to the variable func;
  3. func() is called once (since a callback needs to be executed on the first run of this extension); and
  4. the implementation is returned, redefining the callback as either option_1() or option_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 documente 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

  1. the creation of a unique class for use by the script (one that isn’t likely to be repeated);
  2. the definition of a regular expression for use in the script; and
  3. the definition of a function called classify() that runs through the document's inputs and assigns or removes the unique class based on whether or not the input 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.