Suppose a stockroom page shows a pack of labels priced at $2.50. Its HTML can carry the price in cents while the visible text explains the price to the person ordering:
<button id="labels" type="button"
data-sku="LABEL-25"
data-unit-price-cents="250"
data-available="false">
Labels — $2.50 per pack — unavailable
</button>
The attributes help the script identify this item. They do not replace its visible name, availability message, or an accessible control state.
Convert values at the point you read them
const item = document.querySelector('#labels');
const rawPrice = item.dataset.unitPriceCents;
const priceCents = rawPrice === undefined || rawPrice.trim() === ''
? NaN
: Number(rawPrice);
if (!Number.isSafeInteger(priceCents) || priceCents < 0) {
throw new Error('Missing or invalid unit price');
}
const available = item.dataset.available === 'true';
const threePacksCents = priceCents * 3;
console.log(available, threePacksCents); // false, 750
Boolean(item.dataset.available) would produce true for the string 'false', because it is a nonempty string. Compare the exact agreed value. If your data contract also permits 'unknown', handle that state explicitly instead of turning it into a boolean.
Likewise, item.dataset.unitPriceCents + 50 produces '25050'. Convert the price first, then calculate 250 + 50. The empty-value check matters because Number('') is zero; an omitted price should not quietly become a free item.
Match names across HTML and JavaScript
| HTML attribute | Dataset property |
|---|---|
data-sku |
dataset.sku |
data-unit-price-cents |
dataset.unitPriceCents |
data-order-id |
dataset.orderId |
Use simple lowercase words separated by hyphens. The browser removes data- and turns a hyphen followed by a lowercase ASCII letter into that letter's uppercase form. Bracket access, such as item.dataset['unitPriceCents'], uses the converted name too. Custom data attributes
A class can describe styling while a data attribute identifies the application record. Avoid coupling data-sku to a color or a position in the current list; reordering the list should not change which item it represents. The DOM guide covers selecting and updating the element itself.
Distinguish empty, absent, and removed
These states differ:
<div id="order" data-note=""></div>
const order = document.querySelector('#order');
console.log(order.dataset.note); // ''
console.log(order.dataset.owner); // undefined
order.dataset.owner = 'sam';
delete order.dataset.note;
After deletion, data-note is absent. Assigning null or undefined instead writes the string 'null' or 'undefined'. Use delete when absence is the intended result. For a direct own-property presence check, Object.hasOwn(order.dataset, 'owner') avoids searching the dataset object's prototype chain. Dataset value and removal rules
Choose which value owns the current state
An application can accidentally keep a price in three places: the dataset, a JavaScript object, and the displayed label. Changing one does not automatically update the others.
For a small static page, read the dataset when handling an action and deliberately update the visible wording whenever the underlying attribute changes. For an application that already has a central stock record, let that record own price and availability; render the attributes and text from it. Do not independently edit each copy.
Data attributes are inspectable and editable in the browser. Store neither secrets nor an authorization decision in them. A server receiving an order must identify the SKU and determine the current price and availability itself. The local 750 calculation is useful feedback, not proof of a billable total.
Finish the stockroom check by changing the fixture to data-available="true" and confirming that the visible availability wording and the intended control state follow the same record. Then remove the price attribute and verify that the page refuses the calculation instead of displaying $0.00.