The tech behind the Internet evolves pretty quickly these days and HTML5 is the latest acronym you should have in your toolbox. I keep it right next to the latest trusty version of jQuery, my favorite “do more, write less” JavaScript library.
Some folks have a hard time seeing how the two complement each other. Sometimes it seems like HTML5 was designed to make tools like jQuery unnecessary. Not so, courageous reader! Let me show how HTML5 gives jQuery a whole new layer of awesome.
So how does it work?
HTML5 allows us to create our own custom attributes to store data. Custom attributes can be called anything we like, like variable names, but they need to be prepended with the word ‘data’, and words are separated by dashes. You could add “foo”, “bar”, and “foo bar” data attributes to a paragraph like this:
<p id="para" data-foo="bar" data-bar="baz" data-foo-bar="true"> This is dummy text, dummy. </p>
With just JavaScript to fetch the values, use either the getAttribute()
method or the dataset property, which inflects dashed-names into camelCase:
<script type="text/javascript"> var para = document.getElementByID('para'); console.log(para.getAttribute('data-foo')); // Expect string "bar" console.log(para.dataset.bar); // Grabs "data-bar" // Expect string "baz" console.log(para.dataset.fooBar); // Grabs "data-foo-bar" ↑↑↑↑↑↑ Here be magic! // Expect string "true" </script>
jQuery’s data()
method allows us to store and retrieve arbitrary data about an element in a memory-efficient manner. Like getAttribute()
, jQuery’s data()
method can read HTML5 data attributes, but it also does something that getAttribute()
doesn’t:
<section data-role="page" data-page-num="42" data-hidden="true"> <!-- Imagine a bunch of page-type content here... --> </section>
<script type="text/javascript"> console.log($('section').data('role')); // Expect string "page" console.log($('section').data('pageNum'); // Expect 42, an integer...! ↑↑↑↑↑↑↑↑ Here be magic! console.log($('section').data('hidden'); // Expect the boolean TRUE! </script>
Yes, our good friend jQuery inflects the name of the data attribute – a la dataset
– and converts the value into the appropriate type. This even works for more complex types, like arrays and objects:
<a href="#datepicker" data-options="{'hidden': true}"> <em>March 15, 2012</em> <span data-icons="['calendar', 'arrow-dn']"></span> </a>
<script type="text/javascript"> console.log($('a').data('options')); // Expect {'hidden': true} console.log($('a > span').data('icons')); // Expect ['calendar', 'arrow-dn']; </script>
A Tale of Two Drop-downs: A Real-World Example
On my last freelancing assignment, I was working for a company whose primary business was staffing and recruiting. I was leading the development to replace their current business software with something shiny and new, and what could be shinier or newer than HTML5?
It wasn’t long before we ran into one of those “problem areas” that all the in-house folks had struggled with. They were using two standard select
elements to produce drop-down menus for selecting personnel and positions. Those select
elements were probably sufficient two or three years ago when the application was first built and the drop-downs had fewer options, but several thousand active personnel and positions later…
We decided to replace those horribly dysfunctional drop-downs with autocomplete widgets courtesy of jQuery UI. That way, the users pick from a much shorter list by typing a person’s name or employee number. We wired up some routes for personnel and positions that would accept a search term and return matching results as JSON. After that, we just had to add the widgets:
<dl> <dt><label for="personnel_id">Personnel</label></dt> <dd> <input type="text" name="personnel_id" id="personnel_id" class="autocomplete" /> </dd> <dt><label for="position_id">Position</label></dt> <dd> <input type="text" name="position_id" id="position_id" class="autocomplete" /> </dd> </dl>
<script type="text/javascript"> (function($){ // closure $(function(){ /** * First, store the appropriate "source" url * on each element with data() for use below. */ $('input#personnel_id').data( 'autocompleteSource', '/personnel.json' ); $('input#position_id').data( 'autocompleteSource', '/positions.json' ); /** * Using each() allows us access to the <input> * element as "this", which we need below... */ $('input.autocomplete').each(function(){ /** * The initialization for each element is * basically identical, except for the * "source" option, which we'll fetch from * the element with the data() method. */ $(this).autocomplete({ source:$(this).data('autocompleteSource') }); }); // END each }); // END document.ready })(jQuery); // END closure </script>
Let’s Make Better Mistakes Tomorrow
Well, that looks good enough. It certainly works, but I’m one of those compulsive, perfectionist Agilistas who Refactor Mercilessly. Instead of tacking the source URLs on with jQuery, why not put them on the element directly? Without changing the Javascript, we can just throw some data attributes onto the input
elements:
<dl> <dt><label for="personnel_id">Personnel</label></dt> <dd> <input type="text" name="personnel_id" id="personnel_id" class="autocomplete" data-autocomplete-source="/personnel.json" /> </dd> <dt><label for="position_id">Position</label></dt> <dd> <input type="text" name="position_id" id="position_id" class="autocomplete" data-autocomplete-source="/positions.json" /> </dd> </dl>
Finishing Touches
Since jQuery will happily convert those complex data types for us, we can get away with even more magic. The autocomplete widget is expecting an object packed with options, so let’s give it one:
<!-- This is just an abbreviated example... --> <input type="text" name="personnel_id" id="personnel_id" class="autocomplete" data-autocomplete-options="{'source':'/personnel.json','delay':1000}" />
<script type="text/javascript"> (function($){ // closure $(function(){ // document.ready $('input.autocomplete').each(function(){ /** * Initialize the autocomplete widget * with the options from the element, * defaulting to an empty object if * the data attribute isn't set. */ $(this).autocomplete($(this).data( 'autocompleteOptions' ) || { }); }); // END each }); // END document.ready )(jQuery); // END closure </script>
Well, it’s not really magic; it’s just good technique. Just keep in mind that without HTML5 data attributes, all this convenience wouldn’t be possible and we’d have to resort to our sloppier first pass or use an HTML5 shim.
What have we learned?
There are a ton of new features baked into the HTML5 spec and current browser technology, but that doesn’t render our “oldies but goodies” – like jQuery – useless. If anything, jQuery has become even more useful with the addition of all those new tricks. Using jQuery to leverage HTML5 data attributes, you can “do more, write less” and keep your code nicely organized and clean.