data-* attributes let you store custom, private data on any HTML element. They're a standard way to attach metadata to the DOM that JavaScript can read, without abusing non-standard attributes or classes.
Edit
datasetconst btn = document.querySelector("button");
btn.dataset.id; // "42"
btn.dataset.userRole; // "admin" ← kebab-case becomes camelCase
btn.dataset.isActive; // "true" ← always a STRING
// writing updates the attribute too
btn.dataset.id = "99"; // sets data-id="99" in the DOM
Two key rules: data-user-role is accessed as dataset.userRole (the dashes turn into camelCase), and every value is a string — convert as needed (Number(btn.dataset.id), btn.dataset.isActive === "true").
button[data-is-active="true"] { font-weight: bold; }
button::after { content: attr(data-id); } /* display the value */
list.addEventListener("click", (e) => {
const id = e.target.dataset.id; // know which item was clicked
if (id) deleteItem(id);
});
Storing the id on each element means one listener on the parent can handle all items.
data-* is the clean, standards-compliant way to pass server data into the DOM for client-side JS (without an extra API call), to wire up event delegation, and to drive CSS state — without inventing non-standard attributes that would make the HTML invalid.
Don't store large or sensitive data there, though; it's visible in the markup.