Her yerde kod tabanlarında gördüğüm bir şablon var: geliştiriciler bir DOM öğesine özel veri eklemek ister, bu yüzden bunu sınıf adlarına kodlarlar (class="item item-42 status-active"), gizli girdilerde depolarlar ya da bir ID ile anahtarlanan bir JavaScript nesnesi eklerler. Bunların hepsi geçici çözümlerdir. data-* öznitelikleri bu iş için doğru araçtır ve doğrudan HTML Living Standard'a yerleştirilmiştir.
data-* Öznitelikleri Nedir
data-* öznitelik spesifikasyonu, herhangi bir HTML öğesine özel anahtar-değer verisi eklemenizi sağlar. Ad, data- ile başlamalı ve ardından en az bir karakter gelmelidir. Değer her zaman bir dizedir.
<!-- data-* attributes: custom data right on the element -->
<button
data-action="delete"
data-item-id="284"
data-item-name="Summer Newsletter"
data-confirm="true"
>
Delete
</button>
<li
data-product-id="SKU-9921"
data-price="29.99"
data-in-stock="true"
data-category="electronics"
>
Wireless Keyboard
</li>Görüntülemeyi hiç etkilemezler. Sınıf, stil veya düzen davranışı eklemezler. Tamamen meta veridir; JavaScript ve CSS tarafından okunabilir, tarayıcının render motoru tarafından göz ardı edilir.
Adlandırma Kuralları
HTML'de veri öznitelik adları büyük/küçük harf duyarsızdır (spesifikasyon bunları küçük harfe dönüştürür) ve JavaScript'in dataset API'sinde camelCase'e dönüşürler. Dönüşüm otomatiktir:
<!-- HTML attribute names use kebab-case -->
<div
data-user-id="42"
data-first-name="Alice"
data-account-type="premium"
data-last-login-at="2026-04-16T09:00:00Z"
>
</div>// JavaScript dataset uses camelCase (automatic conversion)
const el = document.querySelector('div');
el.dataset.userId; // "42" (from data-user-id)
el.dataset.firstName; // "Alice" (from data-first-name)
el.dataset.accountType; // "premium" (from data-account-type)
el.dataset.lastLoginAt; // "2026-04-16T09:00:00Z" (from data-last-login-at)Kural: HTML'deki tireler JavaScript'te camelCase'e dönüşür. data-item-count → dataset.itemCount. Bu dönüşüm çift yönlüdür — dataset.itemCount'a yazmak DOM'daki data-item-count'u günceller.
dataset ile Okuma ve Yazma
dataset özelliği bir DOMStringMap'tir — okuma ve yazma için normal bir JavaScript nesnesi gibi davranır ve DOM canlı olarak güncellenir:
const card = document.querySelector('.product-card');
// Reading
const productId = card.dataset.productId; // "SKU-9921"
const price = parseFloat(card.dataset.price); // 29.99 (remember: always a string, parse as needed)
const inStock = card.dataset.inStock === 'true'; // convert string to boolean
// Writing (updates the DOM attribute in real time)
card.dataset.price = '24.99'; // sets data-price="24.99"
card.dataset.inStock = 'false'; // sets data-in-stock="false"
card.dataset.badgeText = 'Sale'; // adds data-badge-text="Sale" (new attribute)
// Deleting
delete card.dataset.badgeText; // removes data-badge-text from the element
// Checking existence
if ('productId' in card.dataset) {
console.log('This is a product card');
}data-* değerleri dizelerdir. Sayıları her zaman parseInt() veya parseFloat() ile ayrıştırın, boole değerlerini ise 'true' dizesiyle karşılaştırarak elde edin. Onları ayrıştırmadan orijinal türleri gibi işlemek yaygın bir hata kaynağıdır.data-* Öznitelikleriyle Olay Delegasyonu
Veri özniteliklerinin en pratik kullanımlarından biri: olay delegasyonu. Onlarca düğmeye ayrı ayrı olay dinleyicileri eklemek yerine, bir üst öğeye tek bir dinleyici ekleyin ve eylemi öğenin dataset'inden okuyun:
<ul id="task-list">
<li data-task-id="101" data-status="pending">
Write article draft
<button data-action="complete" data-task-id="101">Complete</button>
<button data-action="delete" data-task-id="101">Delete</button>
</li>
<li data-task-id="102" data-status="pending">
Review pull request
<button data-action="complete" data-task-id="102">Complete</button>
<button data-action="delete" data-task-id="102">Delete</button>
</li>
</ul>const taskList = document.getElementById('task-list');
// One listener handles all buttons — even ones added dynamically later
taskList.addEventListener('click', (event) => {
const button = event.target.closest('button[data-action]');
if (!button) return; // click was somewhere else in the list
const action = button.dataset.action;
const taskId = button.dataset.taskId;
switch (action) {
case 'complete':
markTaskComplete(taskId);
break;
case 'delete':
deleteTask(taskId);
break;
}
});
function markTaskComplete(id) {
const item = taskList.querySelector(`li[data-task-id="${id}"]`);
item.dataset.status = 'complete'; // CSS can react to this change
console.log(`Task ${id} marked complete`);
}
function deleteTask(id) {
const item = taskList.querySelector(`li[data-task-id="${id}"]`);
item.remove();
console.log(`Task ${id} deleted`);
}Bu şablon istediğiniz sayıda öğeye ölçeklenir. event.target.closest(), tıklanan öğeden başlayarak DOM'da yukarı doğru yürür ve en yakın eşleşen atayı bulur — bu nedenle düğme metnine (bir alt metin düğümü) tıklamak yine de düğmeyi bulur. event.target.tagName karşılaştırmasından daha temiz ve tam öğe eşleşmelerini kontrol etmekten daha güvenilirdir.
CSS attr() ile data-* Kullanımı
CSS, content bildirimlerinde attr() fonksiyonunu kullanarak veri öznitelik değerlerini okuyabilir. Bu, işaretlemeden değil veriden gelen araç ipuçları, rozetler ve etiketler için kullanışlıdır:
<!-- Tooltip text from data attribute -->
<span class="tooltip" data-tip="Saves automatically every 30 seconds">
Auto-save enabled
</span>
<!-- Status badge text from data attribute -->
<li class="task" data-status="in-progress">Review PR #412</li>
<li class="task" data-status="complete">Write unit tests</li>/* Tooltip using attr() in CSS */
.tooltip {
position: relative;
cursor: help;
text-decoration: underline dotted;
}
.tooltip::after {
content: attr(data-tip);
position: absolute;
bottom: 100%;
left: 0;
background: #1a1a2e;
color: white;
padding: 4px 8px;
border-radius: 4px;
font-size: 0.85rem;
white-space: nowrap;
opacity: 0;
pointer-events: none;
transition: opacity 0.2s;
}
.tooltip:hover::after,
.tooltip:focus::after {
opacity: 1;
}
/* Status badge via attr() */
.task::before {
content: '[' attr(data-status) '] ';
font-size: 0.75rem;
font-weight: bold;
text-transform: uppercase;
}
.task[data-status="complete"] { text-decoration: line-through; color: #6b7280; }
.task[data-status="in-progress"] { color: #d97706; }attr()'nin şu anda yalnızca content özelliklerinde çalıştığını unutmayın — width, color veya diğer CSS özellikleri için kullanamassınız (ancak CSS Values Level 5 spesifikasyonu bunu genişletmek için çalışmaktadır). Şimdilik, öznitelik seçicileri ([data-status="complete"]), veri öznitelik değerlerine göre stilleri değiştirmenin pratik yoludur.
Eksiksiz Etkileşimli Örnek — data-* ile Accordion
İşte durum için tamamen veri öznitelikleri tarafından yönetilen gerçek dünyadan bir accordion bileşeni. Davranış için sınıf geçişi yok, sadece veri öznitelikleri — sınıflar yalnızca stil için kalır:
<div id="faq-accordion" role="list">
<div class="accordion-item" role="listitem" data-open="false">
<button
class="accordion-trigger"
data-target="faq-1"
aria-expanded="false"
aria-controls="faq-1"
>
What is the difference between article and section?
</button>
<div id="faq-1" class="accordion-panel" data-panel hidden>
<p>An <code>article</code> is self-contained content that could stand alone.
A <code>section</code> is a thematic grouping within a larger context.</p>
</div>
</div>
<div class="accordion-item" role="listitem" data-open="false">
<button
class="accordion-trigger"
data-target="faq-2"
aria-expanded="false"
aria-controls="faq-2"
>
When should I use ARIA roles?
</button>
<div id="faq-2" class="accordion-panel" data-panel hidden>
<p>Only when no native HTML element conveys the same semantics.
Native elements should always be preferred.</p>
</div>
</div>
</div>const accordion = document.getElementById('faq-accordion');
accordion.addEventListener('click', (event) => {
const trigger = event.target.closest('.accordion-trigger');
if (!trigger) return;
const targetId = trigger.dataset.target;
const panel = document.getElementById(targetId);
const item = trigger.closest('.accordion-item');
const isOpen = item.dataset.open === 'true';
// Toggle this item
item.dataset.open = isOpen ? 'false' : 'true';
trigger.ariaExpanded = isOpen ? 'false' : 'true';
panel.hidden = isOpen;
});.accordion-trigger {
width: 100%;
text-align: left;
background: none;
border: none;
padding: 1rem;
font-size: 1rem;
cursor: pointer;
display: flex;
justify-content: space-between;
align-items: center;
}
/* Arrow rotates based on data-open state */
.accordion-item[data-open="false"] .accordion-trigger::after {
content: '▼';
transition: transform 0.2s;
}
.accordion-item[data-open="true"] .accordion-trigger::after {
content: '▲';
}
.accordion-panel {
padding: 0 1rem 1rem;
}
.accordion-panel[hidden] {
display: none;
}Durum, kapsayıcıdaki data-open içinde yaşar. CSS ok göstergesini döndürmek için bu özniteliği okur. JavaScript özniteliği değiştirir ve aria-expanded ile hidden'ı yönetir. Her katman tam olarak bir iş yapar ve birlikte temiz bir şekilde çalışırlar.
Performans ve Erişilebilirlik Notları
- Performans.
data-*öznitelikleri DOM'un bir parçasıdır ve bunları okumak son derece hızlıdır — özünde diğer öznitelikleri okumakla aynıdır. Son derece sık çalışan kod yollarında (kare başına binlerce okuma), öğe referansına göre anahtarlanmış bir JavaScript Map daha hızlıdır. Ancak tipik UI kodu içindatasetuygundur. - Data-* betikler içindir, semantik için değil. Bu öznitelikler, onları ARIA durumlarına açıkça bağlamadıkça ekran okuyucular tarafından görünmezdir. Semantik iletmek için
data-roleveyadata-labelkullanmayın — bunun için uygun HTML öğelerini ve WAI-ARIA özniteliklerini kullanın. - Güvenlik. Veri özniteliklerine hiçbir zaman hassas veri (token, parola, kişisel veriler) koymayın. Bunlar tarayıcının öğe denetçisinde görünür ve üçüncü taraf betikler dahil sayfadaki herhangi bir JavaScript tarafından okunabilir.
- Sorgulama. CSS öznitelik seçicileriyle öğeleri veri özniteliklerine göre seçebilirsiniz:
document.querySelectorAll('[data-action="delete"]')— bu tam olarak beklediğiniz gibi çalışır ve toplu işlemler için çok kullanışlıdır.
HTML Geliştirme Araçları
HTML ile çalışırken, HTML Formatter öznitelik ağırlıklı işaretlemenizi okunabilir tutar ve HTML Validator, aria-controls ilişkilendirmelerini bozabilecek yinelenen ID'ler gibi hataları yakalar. Yukarıdaki accordion gibi bileşen kalıplarını canlı düzenlemek ve önizlemek için HTML Editörü, derleme adımı olmadan değişiklikleri gerçek zamanlı olarak görmenizi sağlar.
Sonuç
Veri öznitelikleri, DOM öğelerine özel meta veri eklemenin temiz ve resmi yoludur. Çoğu kullanım durumunda sınıf adı hilelerini, gizli girdileri ve harici JavaScript haritalarını geride bırakırlar. dataset API'si onları okumayı ve yazmayı kolaylaştırır, camelCase dönüşümü otomatiktir ve CSS öznitelik seçicileri ile olay delegasyonu kalıplarıyla doğal olarak birleşirler. Sadece şunu unutmayın: veri öznitelikleri betikle okunabilir meta veri içindir, erişilebilirlik semantiği için değil — bunun için ARIA vardır.