A table row displays an order, and the button that deletes it has to know its identifier. Where should that piece of information live without leaving the HTML?
The language provides an official place for it: data-* attributes, readable from JavaScript through a single property.
Definition
dataset is a property present on every HTML element, giving access to its attributes starting with data-. Each attribute shows up as a key there, readable and writable like the key of a plain Object.
// <li data-id="42" data-status="paid" data-sent-on="2026-03-14">
const row = document.querySelector("li");
console.log(row.dataset.id); // "42"
console.log(row.dataset.status); // "paid"
console.log(row.dataset.sentOn); // "2026-03-14"
row.dataset.status = "refunded"; // updates the attribute in the HTMLHow names convert
The prefix disappears, and each dash followed by a letter becomes a capital. The mapping is mechanical in both directions.
| HTML attribute | JavaScript key |
|---|---|
data-id | dataset.id |
data-user-id | dataset.userId |
data-price-before-tax | dataset.priceBeforeTax |
Writing dataset.totalPrice therefore creates a data-total-price attribute, visible in the browser inspector.
Everything in it is a string
An HTML attribute only stores text. The value read back is always a String, even when it looks like a number or a boolean, and that is the most common source of error.
// <div data-quantity="3" data-active="false">
const block = document.querySelector("div");
console.log(block.dataset.quantity + 1); // "31"
console.log(Number(block.dataset.quantity) + 1); // 4
if (block.dataset.active) console.log("runs anyway");The last line runs because the string "false" sits on the truthy side, as covered in Truthy and falsy. Compare explicitly, with === "true", rather than trusting the direct test.
The companion of delegation
Event delegation is where this property truly pays off: the single listener locates the element that was clicked, then reads from it the identifier to work on.
table.addEventListener("click", (event) => {
const button = event.target.closest("[data-id]");
if (!button) return;
console.log("delete order", button.dataset.id);
});Frequently asked questions
Why not invent a homemade attribute?
An attribute such as orderid makes the document invalid, never shows up in dataset, and may collide with a standardized attribute later on. The data- prefix is reserved for exactly these application-specific needs.
Can an object be stored in it?
Only as text, for instance through JSON on the way in and on the way out. That stays acceptable for a couple of fields and grows heavy beyond. To tie rich data to an element, a WeakMap or a Map kept in memory fits better.
Can the visitor see these values?
Yes, and change them. A data-* attribute sits in the HTML sent to the browser and can be edited in two clicks in the inspector. No price, no role and no permission stored there may be treated as trustworthy: the server checks again every time.