Skip to main content Accessibility Feedback

What can you do with data attributes?

Data attributes are a way to add data in HTML that you can access with JavaScript.

They start with data-, and can be used on their own or have a string as their value.

<button data-click>Click Me</button>
<button data-name="Merlin">Say Hi</button>

You can modify data attributes using the .getAttribute(), .setAttribute(), .hasAttribute(), and .removeAttribute() methods.

let btn = document.querySelector('button');
let name = btn.getAttribute('data-name');

So, what can you actually do with data attributes?

👋 Pssst! This article is one of the bonus Q&A videos/articles from the new JavaScript Essentials workshop. Registration is open now (and closes in a few weeks, so don’t miss out).

JavaScript Selectors

One of the challenges with using classes as JavaScript selectors is that it muddies the waters between CSS and JavaScript.

<div class="expand">
	Some content that can be collapsed or expanded.
</div>

Is the .expand class in the above example used for styling, for targeting that element in JavaScript, or both? There’s no easy way to tell from looking at it.

If it’s used only for targeting in JS, what happens if a well meaning developer sees that the class isn’t used in their CSS file and removes it to reduce some weight from the markup? If it’s used for both, what happens if someone decides to rename the class to fit a different CSS naming convention?

To solve this challenge, a few years ago a convention of prefixing classes used only by JavaScript with .js-, making it more explicit what it was used for.

<div class="js-expand">
	Some content that can be collapsed or expanded.
</div>

If you wanted to be really dogmatic about it, you would include both a .js- prefixed and non-prefixed version, one used solely for styling, the other only for targeting with JS.

<div class="expand js-expand">
	Some content that can be collapsed or expanded.
</div>

This approach totally works, but I think data attributes are actually a better JavaScript-specific solution here.

<div class="expand" data-expand>
	Some content that can be collapsed or expanded.
</div>
let accordion = document.querySelector('[data-expand]');

Variable data

If you have a bunch of interactive elements that do the same thing, but with small differences from one to the other, you can store the information in a data attribute.

For example, let’s say you have a few button elements. When you click one, it logs a person’s name into the console.

You can save the name for each button as a [data-name] attribute, and access it in your JavaScript.

<button data-name="Merlin">Say Hi</button>
<button data-name="Ursula">Say Hello</button>
<button data-name="Gandalf">Say Hey</button>
document.addEventListener('click', function (event) {

	let name = event.target.getAttribute('data-greet');
	if (!name) return;

	console.log(`Hey there, ${name}!`);

});

Action triggers

You can also combine using data attributes as a selector with variable data as a way to handle different event types.

With this technique, you use a [data-*] naming convention, where * is the event type.

<button data-click="sayHi">Say Hello!</button>
<button data-click="expand">Show More</button>

<form data-submit="login">
	<!-- ... -->
</form>

In your JavaScript, you can use the attribute and value as a unique selector…

let helloBtn = document.querySelector('[data-click="sayHi"]');
helloBtn.addEventListener('click', function () {
	console.log('Hello there!');
});

Or, you can pair them with event delegation…

document.addEventListener('click', function (event) {

	// Get the data-click value
	let type = event.target.getAttribute('data-click');
	if (!type) return;

	// Run value-specific code
	if (type === 'sayHi') {
		console.log('Hello there!');
	}

});

If you have a handful of different different methods that run on the same event, you can put them into an object.

Include the function name as the data attribute value, and use that to run on a bunch of functions from a single event handler without having to modify the code every time you update something.

let handlers = {
	sayHi (event) {
		console.log('Hello there!');
	},
	expand (event) {
		// Do stuff...
	},
	login (event) {
		// Also do stuff...
	}
};

document.addEventListener('click', function (event) {

	// Get the data-click value
	let type = event.target.getAttribute('data-click');
	if (!type || !handlers[type]) return;

	// Run value-specific code
	handlers[type](event);

});