Skip to content

The data-* Attributes (Custom Data Attributes)

Global Attribute

Custom data-* attributes allow you to store extra information on HTML elements without using non-standard attributes or extra DOM properties. They provide a standardized way to attach custom data to elements that can be easily accessed via JavaScript and CSS.

<element data-name="value">Content</element>
<element data-custom-attribute="value">Content</element>
RuleDescriptionExample
Start with data-Required prefixdata-id, data-user
LowercaseUse lowercase lettersdata-userid (good), data-userID (avoid)
Hyphens for multi-wordUse hyphens, not camelCasedata-user-id, not data-userId
No uppercaseXML/HTML compatibilitydata-name, not data-Name
Alphanumeric + hyphensOnly letters, numbers, hyphensdata-item-123
Result
Result
Result
Result
const element = document.getElementById('myElement');
// Get data attributes
const userId = element.dataset.userId; // data-user-id
const userName = element.dataset.userName; // data-user-name
const isActive = element.dataset.active; // data-active
// Set data attributes
element.dataset.userId = '12345';
element.dataset.userName = 'johndoe';
element.dataset.status = 'online';
// Delete data attributes
delete element.dataset.userId;
// Check if data attribute exists
if ('userId' in element.dataset) {
console.log('User ID exists');
}

HTML attributes with hyphens convert to camelCase in JavaScript:

<div
data-user-id="123"
data-first-name="John"
data-last-name="Doe"
data-is-admin="true">
</div>
const el = document.querySelector('div');
// Hyphenated in HTML → camelCase in JavaScript
console.log(el.dataset.userId); // "123"
console.log(el.dataset.firstName); // "John"
console.log(el.dataset.lastName); // "Doe"
console.log(el.dataset.isAdmin); // "true" (string!)
<div
id="config"
data-settings='{"theme":"dark","lang":"en"}'>
</div>
const config = document.getElementById('config');
const settings = JSON.parse(config.dataset.settings);
console.log(settings.theme); // "dark"
console.log(settings.lang); // "en"
// Update JSON data
settings.theme = 'light';
config.dataset.settings = JSON.stringify(settings);
/* Exact match */
[data-status="active"] {
color: green;
}
/* Contains word */
[data-tags~="featured"] {
font-weight: bold;
}
/* Starts with */
[data-id^="user-"] {
background: #f0f0f0;
}
/* Ends with */
[data-url$=".pdf"] {
color: red;
}
/* Contains substring */
[data-description*="important"] {
border: 2px solid orange;
}
/* Display data attribute content */
.tooltip::after {
content: attr(data-tooltip);
position: absolute;
background: black;
color: white;
padding: 5px;
}
/* Use in URL */
.icon {
background-image: url(attr(data-icon));
}
<!-- Good: Track UI state -->
<button data-loading="false">Submit</button>
<div data-expanded="true">Collapsible content</div>
<input data-valid="false" data-error-message="Required field">
<!-- Good: Reference other elements or API IDs -->
<button data-target-id="modal-1">Open Modal</button>
<article data-post-id="12345" data-author-id="789">...</article>
<!-- BAD: Never do this -->
<div data-password="secret123"></div>
<div data-credit-card="4111111111111111"></div>
<div data-api-key="sk_live_abc123"></div>
<!-- GOOD: Store references only -->
<div data-user-id="12345"></div>
<!-- Good: Simple values -->
<div data-count="5"></div>
<div data-status="active"></div>
<!-- Okay: JSON for complex data -->
<div data-config='{"max":10,"min":0}'></div>
<!-- Bad: Too complex -->
<div data-all-user-data="...megabytes of data..."></div>
<!-- Good: Clear, descriptive names -->
<button data-action="delete" data-confirm-message="Are you sure?">
<div data-sort-order="asc" data-sort-field="name">
<!-- Bad: Unclear abbreviations -->
<button data-a="d" data-cm="R u sure?">

UI state: Loading, expanded, selected, active ✓ Configuration: Settings, options, parameters ✓ Identifiers: IDs, references, keys (non-sensitive) ✓ Metadata: Author, date, category, tags ✓ Binding: Connect HTML elements to JavaScript logic

<!-- UI State -->
<div data-state="loading" data-progress="75">...</div>
<!-- Configuration -->
<div data-auto-refresh="true" data-interval="5000">...</div>
<!-- Metadata -->
<article data-published="2024-01-15" data-category="tech">...</article>

Standard attributes exist: Use id, class, title instead ✗ Accessibility: Use ARIA attributes for accessibility ✗ Semantics: Use semantic HTML elements when possible ✗ Large data: Use JavaScript variables for large datasets ✗ Frequently changing data: Use JavaScript state management

<!-- Bad: Standard attributes exist -->
<div data-id="unique-id">
<!-- Good: Use id attribute -->
<div id="unique-id">
<!-- Bad: Accessibility -->
<button data-role="button" data-label="Close">
<!-- Good: Use ARIA -->
<button role="button" aria-label="Close">
// Fast: Cache dataset reference
const dataset = element.dataset;
const id = dataset.userId;
const name = dataset.userName;
// Slower: Multiple DOM queries
const id = element.dataset.userId;
const name = element.dataset.userName;
// Fastest: Read once, store in variable
const data = { ...element.dataset };
// Bad: Store large arrays in data attributes
element.dataset.items = JSON.stringify(largeArray); // Slow!
// Good: Store in JavaScript, reference by ID
const dataStore = {
'123': largeArray
};
element.dataset.dataId = '123';
const data = dataStore[element.dataset.dataId];
// All dataset values are strings!
element.dataset.count = 5;
console.log(typeof element.dataset.count); // "string"
console.log(element.dataset.count === 5); // false
console.log(element.dataset.count === "5"); // true
// Convert types explicitly
const count = parseInt(element.dataset.count);
const price = parseFloat(element.dataset.price);
const active = element.dataset.active === 'true';

Universal support across all modern browsers:

FeatureChromeFirefoxSafariEdge
data-* attributesAllAllAllAll
dataset API8+6+5.1+10+
  • getAttribute() - Generic attribute getter
  • setAttribute() - Generic attribute setter
  • hasAttribute() - Check if attribute exists
  • removeAttribute() - Remove attribute
// Using dataset API (preferred)
element.dataset.userId = '123';
// Using attribute methods (also works)
element.setAttribute('data-user-id', '123');
element.getAttribute('data-user-id');
element.hasAttribute('data-user-id');
element.removeAttribute('data-user-id');