In-Depth

Working with the HTML5 Data Attributes Using jQuery

jQuery support for the new HTML5 data attributes may not be everything a developer could want -- but it's very close. And, more important, it's the perfect solution for handling transactional data.

You've always been able to add your own attributes to HTML elements (browsers simply ignore any attributes they don't recognize). HTML5 includes a standard for regularizing these custom attributes by prefixing them with the string "data-" -- a feature that lets you store data about an entity with the part of the entity being displayed. For instance, in displaying a set of employees, I might not want to display the employee ID, status or salary on the page. However, I can still keep that data with the rest of employee data by inventing attributes called data-empid, data-salary and data-status, as in this example:

<table>
  <tr data-empid="A123" data-salary="120" data-status="drone">
    <td>Peter</td><td>Vogel</td><td>Principal</td>
  </tr>
  <tr data-empid="B456" data-salary="130" data-status="worker">
    <td>Jan</td><td>Vogel</td><td>General Manager</td>
  </tr>
  <tr data-empid="C789" data-salary="110" data-status="worker">
    <td>Jason</td><td>van de Velde</td><td>Company nurse</td>
  </tr>
</table>

I'll start with the bad news. It would be great if jQuery gave you a dedicated selector for accessing elements by the value of these data attributes. However, that's not the case -- to extract the row for a particular employee using data-empid, I'd have to use the jQuery generic attribute selector (the square brackets, []). This example, for instance, finds the first row whose data-empid is set to B456:

var emp = $('tr[data-empid="B456"]').first();

That being the case, I might as well use the id attribute to define my table's rows because jQuery selectors do support the id attribute. That gives me a table like this:

<table>
  <tr id="A123" data-salary="120" data-status="drone">
    <td>Peter</td><td>Vogel</td><td>Principal</td>
  </tr>
  <tr id="B456" data-salary="130" data-status="worker">
    <td>Jan</td><td>Vogel</td><td>General Manager</td>
  </tr>
  <tr id="C789" data-salary="110" data-status="worker">
    <td>Jason</td><td>van de Velde</td><td>Company Nurse</td>
  </tr>
</table>

And now I can find my employee with this selector:

var emp = $('#B456');

But other than that piece of wishful thinking (and I've seen a couple of jQuery extensions that do support selecting by data values), jQuery provides extensive -- and useful! -- support for data elements through its data function.

Adding and Removing Data
For instance, in retrieving an element, I can retrieve the value of data attributes on it without using the "data-" prefix: I just pass the name of the data attribute to the jQuery data function. This example retrieves the value of the data-salary attribute for employee B456, assuming that the data-empid attribute is still in place:

var empSalary = $('tr[data-empid="B456"]').data("salary");
alert(empSalary);

There's a key difference between this code and my previous example that also used the attribute selector with data-empid: the first function is gone from this example. While the attribute selector ([data-empid="B456"]) returns a collection, the data function retrieves the value of only the first item in the collection (a jQuery convention). There would be nothing wrong with continuing to use the first function (and it might even make the code more obvious), but it wouldn't gain you anything, either.

As is also common with jQuery functions, if you pass the data function two parameters, the second parameter is used to update the data attribute. I can give employee B456 a raise with this code (and I'm assuming that I've done the right thing and moved the employee ID out of the data-empid attribute and into the id attribute):

$('#B456').data("salary",150);

However, the behavior of the data function is different during updates: The data function updates every item that the selector retrieves, not just the first item (again, a jQuery convention). Fortunately, this code uses the "#" selector, which ensures that I retrieve only the first element with a matching id attribute. However, if I did want to give a raise to all the employees with a status of "worker," I could use this code:

$('tr[data-status="worker"]').data("salary",150);

You can also use the data function to add new data attributes to an element without defining those attributes in your HTML. This example, for instance, effectively adds an attribute called data-transactionStatus to the employee's element and sets it to the string "approved":

$('#B456').data("transactionStatus", "approved");

Working with Objects
As the number of attributes starts to grow -- and especially if your data attributes represent several different kinds of data -- you might want to organize those attributes into groups. For that, you can store JSON objects in your data attributes.

Rewriting my table to store a JSON object with multiple properties in a data attribute called emp gives me this table:

<table>
  <tr id="A123" data-emp='{"salary":120, "status":"drone"}' >
    <td>Peter</td><td>Vogel</td><td>Principal</td>
  </tr>
  <tr id="B456" data-emp='{"salary":120, "status":"worker"}'>
    <td>Jan</td><td>Vogel</td><td>General Manager</td>
  </tr>
  <tr id="C789" data-emp='{"salary":120, "status":"worker"}'>
    <td>Jason</td><td>van de Velde</td><td>Nurse</td>
  </tr>
</table>

I can now retrieve the salary property with code like this:

var empSalary = $('#B456').data("emp").salary;

To update a JSON object you must use the JSON syntax (though with fewer quotation marks than required in the HTML). Where your JSON code references a property that already exists, the property is updated; where you reference a property that doesn't already exist, the property is added with the new value.

This example updates the existing salary property on employee B456 and adds the raised property, setting the property to true:

$('#B456').data("emp", {salary:200, raised:true});

Three things to watch out for: jQuery itself stores data under the names "events" and "handles"; jQuery also keeps the right to use data attribute names beginning with an underscore. You should avoid those names. And two bonuses: You can store anything you want in a data attribute, including functions (though I haven't done so, so I'm taking that feature on faith); you can also wire up read and set events to have code execute when the data function is used (again, not something I've needed to do).

Using the Data Function
I'm a big fan of the Model-View-ViewModel (MVVM) pattern and, unless my client tells me otherwise, lately I've been using Knockout to implement the pattern in JavaScript. In that scenario, I've ended up with three sets of data: the data in the objects that I retrieve from the server, the subset of that data that I bind to elements in my page, and "transactional" data -- temporary data that only exists while the user is working with the page.

I can further divide that transactional data. Some of that data is associated with the page as a whole, but some of it's associated with individual objects that I've retrieved. I've found the data function most useful as a way to store that object-related, transactional data: I store it as data on the elements to which my retrieved objects are bound. It really is the perfect solution for that kind of data.

About the Author

Peter Vogel is a system architect and principal in PH&V Information Services. PH&V provides full-stack consulting from UX design through object modeling to database design. Peter tweets about his VSM columns with the hashtag #vogelarticles. His blog posts on user experience design can be found at http://blog.learningtree.com/tag/ui/.

comments powered by Disqus

Featured

  • Mastering Blazor Authentication and Authorization

    At the Visual Studio Live! @ Microsoft HQ developer conference set for August, Rockford Lhotka will explain the ins and outs of authentication across Blazor Server, WebAssembly, and .NET MAUI Hybrid apps, and show how to use identity and claims to customize application behavior through fine-grained authorization.

  • Linear Support Vector Regression from Scratch Using C# with Evolutionary Training

    Dr. James McCaffrey from Microsoft Research presents a complete end-to-end demonstration of the linear support vector regression (linear SVR) technique, where the goal is to predict a single numeric value. A linear SVR model uses an unusual error/loss function and cannot be trained using standard simple techniques, and so evolutionary optimization training is used.

  • Low-Code Report Says AI Will Enhance, Not Replace DIY Dev Tools

    Along with replacing software developers and possibly killing humanity, advanced AI is seen by many as a death knell for the do-it-yourself, low-code/no-code tooling industry, but a new report belies that notion.

  • Vibe Coding with Latest Visual Studio Preview

    Microsoft's latest Visual Studio preview facilitates "vibe coding," where developers mainly use GitHub Copilot AI to do all the programming in accordance with spoken or typed instructions.

  • Steve Sanderson Previews AI App Dev: Small Models, Agents and a Blazor Voice Assistant

    Blazor creator Steve Sanderson presented a keynote at the recent NDC London 2025 conference where he previewed the future of .NET application development with smaller AI models and autonomous agents, along with showcasing a new Blazor voice assistant project demonstrating cutting-edge functionality.

Subscribe on YouTube