HTML lets you attach your own information to any element with attributes that start with data-.
<button data-id="42" data-action="delete" data-item-name="Notebook">Delete</button>
In JavaScript, they appear on the element’s dataset object.
<button id="b" data-id="42" data-action="delete" data-item-name="Notebook">Delete</button>
<script>
const button = document.querySelector("#b");
console.log(button.dataset.id); // → 42
console.log(button.dataset.action); // → delete
console.log(button.dataset.itemName); // → Notebook
</script>
Naming rules
- The HTML name
data-item-namebecomesdataset.itemName(kebab-case becomes camelCase). - Values are always strings. Convert with
Number(...)if needed.
const id = Number(button.dataset.id);
Writing
<div id="box"></div>
<script>
const box = document.querySelector("#box");
box.dataset.state = "open";
box.dataset.count = 3;
console.log(box.outerHTML); // → <div id="box" data-state="open" data-count="3"></div>
delete box.dataset.count;
</script>
Selecting by data attribute
<button data-action="save">Save</button>
<button data-action="cancel">Cancel</button>
<script>
console.log(document.querySelector("[data-action='cancel']").textContent); // → Cancel
</script>
Why they are useful
They connect your HTML data to your JavaScript without hidden global variables.
<ul id="products">
<li data-id="1" data-price="4.5">Notebook</li>
<li data-id="2" data-price="12">Backpack</li>
</ul>
<script>
const total = [...document.querySelectorAll("#products li")]
.reduce((sum, li) => sum + Number(li.dataset.price), 0);
console.log(total); // → 16.5
</script>
They also drive event delegation: one listener on a list reads event.target.dataset.id to know which item was clicked (Event Delegation).
And in CSS
CSS can select them too: button[data-state="open"] { ... }, which is a clean way to style a state that JavaScript sets.
Not for secrets
data- attributes.