Sometimes an element needs to carry a bit of extra information that isn't shown on the page: a product's ID, the category a card belongs to, the price of an item, or which tab a button controls. You could cram it into a class name, but classes are meant for styling. HTML has a proper place for this: custom data attributes.
Any attribute that starts with data- is yours to define: data-id="42", data-category="kitchen", data-price="8.50". Browsers ignore them for display, the page stays valid HTML, and both JavaScript and CSS can read them. In JavaScript, the dataset property turns every data attribute into a convenient object.
Data attributes are one of the most practical tools for connecting HTML to interactive behaviour. In this lesson you'll learn the naming rules, read and update values with dataset, select and style elements by their data values, and see the patterns real sites use for filters, tabs and tracking.
Writing data attributes
<article class="product" data-id="1042" data-category="kitchen" data-price="8.50" data-in-stock="true">
<h3>Mug</h3>
</article>- The name must start with
data-followed by at least one character. - Use lowercase letters, numbers and hyphens:
data-in-stock, notdata-inStock. - Values are always strings, even when they look like numbers or booleans.
- Don't put anything secret or important for search engines in data attributes; they're visible in the page source and not treated as content.
Reading data with JavaScript
The dataset property gives every data attribute as an object, converting hyphenated names to camelCase:
const card = document.querySelector('.product');
card.dataset.id; // "1042"
card.dataset.inStock; // "true" (from data-in-stock)
Number(card.dataset.price); // 8.5
card.dataset.category = 'home'; // changes data-category in the HTMLYou can also use getAttribute('data-price') and setAttribute(...). Remember to convert strings with Number() before doing maths.
Styling with data attributes
CSS attribute selectors can match data attributes:
[data-in-stock="false"] { opacity: 0.5; }
.tag[data-colour="red"] { background: crimson; }
.badge::after { content: attr(data-label); }[data-state="open"]matches an exact value;[data-tags~="sale"]matches a word in a space-separated list.attr(data-label)can show a data value as text in a pseudo-element.- Changing a data attribute from JavaScript immediately updates any styles that depend on it, which makes them perfect for states like open, active or loading.
HTML Data Attributes Syntax
Data attribute syntax
<button data-action="delete" data-item-id="7">Delete</button>
<script>
const btn = document.querySelector('[data-action="delete"]');
console.log(btn.dataset.itemId); // "7"
btn.dataset.state = 'busy'; // adds data-state="busy"
</script>
<style>
[data-state="busy"] { cursor: progress; opacity: 0.6; }
</style>HTML Data Attributes Summary
- Custom attributes start with
data-and hold string values. - Use lowercase names with hyphens; JavaScript sees them in camelCase.
element.datasetreads and writes data attributes.- CSS attribute selectors such as
[data-state="open"]style elements by their data. - Use data attributes for information and state, not for secrets or visible content.
HTML Data Attributes Examples
Each example runs in the page. Edit the code and press Run to see what changes.
Product cards with data
<style>
.card { display: inline-block; padding: 10px 14px; margin: 4px; border: 1px solid #ddd; border-radius: 8px; font-family: sans-serif; }
.card[data-in-stock="false"] { opacity: 0.45; text-decoration: line-through; }
.card::after { content: " £" attr(data-price); color: #15803d; font-weight: bold; }
</style>
<div class="card" data-id="1" data-price="8.50" data-in-stock="true">Mug</div>
<div class="card" data-id="2" data-price="24.00" data-in-stock="false">Lamp</div>
<div class="card" data-id="3" data-price="1.20" data-in-stock="true">Pen</div>Line-by-line explanation
- Line 3An attribute selector fades out cards that are out of stock.
- Line 4
attr()shows each card's price after its name, straight from the data. - Lines 6 to 8Each card stores its id, price and stock status in data attributes.
Reading and changing dataset
<button id="like" data-likes="3" data-liked="false">Like (3)</button>
<p id="log"></p>
<script>
const btn = document.getElementById('like');
btn.addEventListener('click', () => {
const liked = btn.dataset.liked === 'true';
const likes = Number(btn.dataset.likes) + (liked ? -1 : 1);
btn.dataset.likes = likes;
btn.dataset.liked = String(!liked);
btn.textContent = `${liked ? 'Like' : 'Liked'} (${likes})`;
document.getElementById('log').textContent = `data-likes is now "${btn.dataset.likes}"`;
});
</script>Line-by-line explanation
- Line 1Two pieces of state live on the button itself.
- Line 6Data values are strings, so compare with the string "true".
- Line 7
Number()converts the count before adding. - Lines 8 to 9Writing to
datasetupdates the HTML attributes.
A category filter
<style>
.item[hidden] { display: none; }
.filters button[aria-pressed="true"] { background: #1f2937; color: white; }
</style>
<div class="filters">
<button data-filter="all" aria-pressed="true">All</button>
<button data-filter="kitchen" aria-pressed="false">Kitchen</button>
<button data-filter="home" aria-pressed="false">Home</button>
</div>
<ul>
<li class="item" data-category="kitchen">Mug</li>
<li class="item" data-category="home">Lamp</li>
<li class="item" data-category="kitchen">Kettle</li>
</ul>
<script>
document.querySelectorAll('[data-filter]').forEach((b) => b.addEventListener('click', () => {
const f = b.dataset.filter;
document.querySelectorAll('[data-filter]').forEach((x) => x.setAttribute('aria-pressed', x === b));
document.querySelectorAll('.item').forEach((li) => { li.hidden = f !== 'all' && li.dataset.category !== f; });
}));
</script>Line-by-line explanation
- Lines 6 to 8Each button says which category it shows.
- Lines 11 to 13Each item says which category it belongs to.
- Line 17The clicked button's filter value is read from
dataset. - Line 19Items whose category doesn't match are hidden.
Common HTML Data Attributes Mistakes
Uppercase letters in the name
<div data-userId="7"></div><div data-user-id="7"></div>HTML lowercases attribute names, so dataset.userId wouldn't find data-userId as written. Use hyphens.
Doing maths on strings
<script> total = el.dataset.price + 1; </script> <!-- "8.501" --><script> total = Number(el.dataset.price) + 1; </script> <!-- 9.5 -->Hiding important content in data attributes
<h2 data-title="Spring sale: 20% off"></h2><h2>Spring sale: 20% off</h2>Screen readers and search engines read text content, not data attributes.
HTML Data Attributes in Real Projects
Data attributes in the wild
Shops store product ids and variants on buttons for "Add to basket" scripts. Analytics tools read attributes like data-track="basket-button". Frameworks and libraries such as Bootstrap use data-bs-toggle="modal" to wire up components without extra JavaScript, and testing tools find elements with data-testid.
HTML Data Attributes Exercises
Answer each one and check it instantly. If you get stuck, open the hint before the answer.
Complete the custom attribute name.
<li category="books">The Hobbit</li>
data-.How does JavaScript read data-user-id from an element?
What type is el.dataset.price when the HTML says data-price="12"?
Which CSS selector matches <div data-state="open">?
The attributes aren't valid data attributes. Rename them so they work with dataset: the product id should be readable as dataset.productId and the colour as dataset.colour.
<button class="add" productId="55" colour="blue">Add to basket</button>- dataset.productId is "55"
- dataset.colour is "blue"
HTML Data Attributes Quiz
5 questions to check what you learned.
What must a custom data attribute name start with?
- x-
- custom-
- data-
- my-
What does
el.dataset.state = 'open'do?- Adds or updates data-state="open" on the element
- Nothing
- Creates a new element
- Changes the class
Which is a good use of a data attribute?
- The page's main heading text
- A password
- The alt text of an image
- Storing which category a product belongs to
What does
content: attr(data-label)do in CSS?- Sets a variable
- Hides the element
- Validates the attribute
- Shows the data-label value as generated text
Why should data attribute names use hyphens instead of capitals?
- HTML lowercases attribute names, so camelCase names don't map to dataset as expected
- Capitals are slower
- Hyphens are required by CSS
- Capitals aren't allowed in HTML at all
HTML Data Attributes Challenge
Tagged tasks
Create a list of tasks:
- Three
<li>elements with classtask - Each has a
data-priorityofhigh,mediumorlow - CSS makes tasks with
data-priority="high"red (color: red)
- Three tasks
- Each task has a priority
- High priority tasks are red
[data-priority="high"].