Saturday, August 20, 2011

A JavaScript break - Custom Data Attribute use

OK, so this one isn't Python at all, but the idea intrigued me enough to hammer out some code, and see what I could come up with.

HTML5 (and possibly earlier versions of HTML, though browser support may be spotty) allows for the creation and use of custom data-attributes in page-markup. I agree with John Resig that it seems somewhat bizarre a thing to allow/provide, but when I'd first heard about them, I noted it and set it aside for future consideration.

Then, this week, I was tasked with rewriting/converting some of the legacy content and templating in Tridion at work. One of the main things that needed to be converted was the established (and functional, but not optimal) mechanism for generating ads on pages. The current mechanisms we have in place across the site are different from one application language to another (we have 2 different languages we're using at present), somewhat less than optimal (or so I gather), and not really conducive to the current stated goal(s) for the CMS.

So I started looking at ways that presentations for these ads (basically just snippets of JavaScript) could be generated in an HTML-friendly fashion (making them language- and platform-agnostic), relying only on the end-user's browser's capabilities. it occurred to me that if every ad on every page had some sort of surrounding element already anyway (a convention we'd established for CMS content early on), that if only there was some way to store the data for that ad as part of the markup being generated, it would probably be pretty easy to whack out a JavaScript that would go back through the page-DOM after the page was done loading, and populate those ads.

Something like:

<div data-adsource="[URL for ad-script]"></div>

...and there is a use for a custom data attribute...

Then, all that's left is to create some script that would facilitate reading those data-attribute values, and generate the "real" script-reference to pull them in. Here's what I came up with to actually read the data-attributes:

document.dataElements = null;
document.getDataElements = function( dataElementName )
{
  /*
    Returns a *non-distinct* list of all elements in the DOM that have a "data-[name]" attribute, or an empty list if none exist.

    dataElementName ...... [string, optional] The the complete data-attribute name that will be used for the search. May be supplied as a partial name, ending with "*" to wildcard-match multiple elements with similar data-names.
  */
  if( document.dataElements == null )
  {
    //  Build the document's collection of elements with "data-*" attributes first
    document.dataElements = {};
    allElements = document.getElementsByTagName( "*" );
    //  With each element...
    for( el=0; el < allElements.length; el++ )
    {
      element = allElements[ el ];
      //  check each attribute...
      for( at=0; at < element.attributes.length; at++ )
      {
        attribute = element.attributes.item( at );
        if( attribute.specified && attribute.name.indexOf( "data-" ) == 0 )
        {
          //  the element has a data-attribute, so add it to the document.dataElements collection
          if( document.dataElements[ attribute.name ] == null )
          {
            //  The array doesn't exist, so create it
            document.dataElements[ attribute.name ] = [];
          }
          document.dataElements[ attribute.name ][ document.dataElements[ attribute.name ].length ] = element;
        }
      }
    }
  }
  //  Get the data-elements
  if( dataElementName != null )
  {
    if ( dataElementName.substr( dataElementName.length-1, 1 ) != "*" )
    {
      return document.dataElements[ dataElementName ];
    }
    else
    {
      keyName = dataElementName.substr( 0, dataElementName.length-1 );
      results = new Array();
      for( dataName in document.dataElements )
      {
        if ( dataName.substr( 0, keyName.length ) == keyName )
        {
          results = results.concat( document.dataElements[ keyName ] );
        }
      }
      return results;
    }
  }
  else
  {
    return [];
  }
}

This provides a dataElements property on the document - a named collection of data-attribute names that each resolve to a (non-distinct) list of elements on the page that have that data-attribute. It also allows (as the comment in the script notes) a "wildcard" search and retrieval, for scenarios like "get every element on the page that has a data-attribute that starts with "data-ad" (document.getDataElements( "data-ad*" )).

No comments:

Post a Comment