HTML Data Attributes

Store extra information on elements with data-* attributes, read and change it with JavaScript's dataset, and style elements by their data values with CSS attribute selectors.

Lesson 25 of 44Intermediate15 min read

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

HTML
<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, not data-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:

JavaScript
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 HTML

You 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:

CSS
[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.dataset reads 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.

Example 1

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>
ResultEdit the code, then press Run or Ctrl+Enter
Line-by-line explanation
  1. Line 3An attribute selector fades out cards that are out of stock.
  2. Line 4attr() shows each card's price after its name, straight from the data.
  3. Lines 6 to 8Each card stores its id, price and stock status in data attributes.
Example 2

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>
ResultEdit the code, then press Run or Ctrl+Enter
Line-by-line explanation
  1. Line 1Two pieces of state live on the button itself.
  2. Line 6Data values are strings, so compare with the string "true".
  3. Line 7Number() converts the count before adding.
  4. Lines 8 to 9Writing to dataset updates the HTML attributes.
Example 3

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>
ResultEdit the code, then press Run or Ctrl+Enter
Line-by-line explanation
  1. Lines 6 to 8Each button says which category it shows.
  2. Lines 11 to 13Each item says which category it belongs to.
  3. Line 17The clicked button's filter value is read from dataset.
  4. Line 19Items whose category doesn't match are hidden.

Common HTML Data Attributes Mistakes

Uppercase letters in the name

Don't
<div data-userId="7"></div>
Do
<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

Don't
<script> total = el.dataset.price + 1; </script>   <!-- "8.501" -->
Do
<script> total = Number(el.dataset.price) + 1; </script>   <!-- 9.5 -->

Hiding important content in data attributes

Don't
<h2 data-title="Spring sale: 20% off"></h2>
Do
<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.

Fill in the blankseasy

Complete the custom attribute name.

<li category="books">The Hobbit</li>
Multiple choiceeasy

How does JavaScript read data-user-id from an element?

Multiple choicemedium

What type is el.dataset.price when the HTML says data-price="12"?

Multiple choicemedium

Which CSS selector matches <div data-state="open">?

Fix the bugmedium

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>
ResultEdit the code, then press Run or Ctrl+Enter
Checks
  • dataset.productId is "55"
  • dataset.colour is "blue"

HTML Data Attributes Quiz

5 questions to check what you learned.

  1. What must a custom data attribute name start with?

    • x-
    • custom-
    • data-
    • my-
  2. What does el.dataset.state = 'open' do?

    • Adds or updates data-state="open" on the element
    • Nothing
    • Creates a new element
    • Changes the class
  3. 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
  4. 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
  5. 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:

  1. Three <li> elements with class task
  2. Each has a data-priority of high, medium or low
  3. CSS makes tasks with data-priority="high" red (color: red)
ResultEdit the code, then press Run or Ctrl+Enter
Checks
  • Three tasks
  • Each task has a priority
  • High priority tasks are red