Using data selectors for JavaScript functionality

Orde Saunders' avatarUpdated: Published: by Orde Saunders

When building websites class and ID selectors are used to provide hooks for both CSS styling and JavaScript functionality. Whilst this is not normally a problem on smaller projects, on large projects it can lead to maintainability issues if the same selector is used for both styling and functionality creating a tight coupling.

This tight coupling can mean that it becomes impossible to modify code without having to refactor either the CSS or the JavaScript which can be a serious issue in organistions where they are maintained by different teams. Whilst this can be avoided with the use of a naming convention for classes and IDs, an alternative approach that provides clearer separation of responsibilities is to use data attributes as selectors exclusively for JavaScript.

What are data attributes?

Data attributes can be added to any HTML element and are intended to provide storage of arbitrary data. They are accessed via JavaScript which is what makes them ideal candidates for use as selectors for JavaScript. They take the form data-[attribute-name]="[value]" where attribute-name is lowercase and hyphenated and value is the data.

An example of a data attribute is: <div data-tab="true">...</data>. The selector for this would be: '[data-tab="true"]'.

How can we use data attributes as selectors?

An example of this is a tabbed UI element. This needs to have the tabs styled to fit in a single row and needs JavaScript to show the selected tab's content when it is selected.

We would mark up the tabs as follows:

<div class="tab" data-tab="true">Tab 1</div>
<div class="tab" data-tab="true">Tab 2</div>
<div class="tab" data-tab="true">Tab 3</div>

We use the class name to provide the hook for the CSS styling:

.tab {
  float: left;
  width: 33%;
  border: 1px solid;
}

We can then use the data attribute as a selector to bind the click action:

$('[data-tab="true"]').on('click', function () {
  // Show tab content.
});

If the value of the data attribute is not important when used as a selector then it can be simplified:

$('[data-tab]').on('click', function () {
  // Show tab content.
});

Performance

An obvious question regarding these more complex selectors is the performance overhead. In order to assess the actual impact of this I built a test case that uses querySelectorAll to select a test element 10,000 times and compared the results for selecting by ID, class and data attribute both with and without the value. This test case is replicated below and, if your browser supports querySelectorAll and addEventListener, you will be able to see the results for your browser. The test case is available in the source of this page.

BrowserID selector
(10,000 iterations)
Class selector
(10,000 iterations)
Data attribute selector - no value
(10,000 iterations)
Data attribute selector - including value
(10,000 iterations)
Chrome 28 (Linux, MacBook Pro)3ms31ms33ms37ms
Firefox 21 (Linux, MacBook Pro)21ms53ms77ms82ms
Chrome 27 (Nexus 4)66ms460ms462ms466ms
Safari (iPhone 5 - iOS 6)82ms228ms246ms261ms

These results indicate that there is a slight performance penalty for using data attribute selectors but, even for a very high number of selections, this is negligible in absolute terms.

Conclusion

This technique has the potential to offer a significant increase in maintainability at the cost of a slight performance deficit in modern browsers. As with all techniques of this nature you should asses it carefully to see if it will benefit your specific situation before adopting it.