HTML 側に書いた値を JavaScript で受け取りたい、あるいは要素ごとに状態や ID を持たせて処理を分けたい。そんなときに活躍するのが data-* 属性と、それを読み書きするための dataset プロパティです。data-* 属性は HTML の正式な仕様で、好きな名前のカスタムデータを要素に持たせられます。この記事では、data-* 属性の書き方と dataset を使った読み書きの方法、属性名の変換ルール、値が必ず文字列になる点まで、コーディング初心者〜中級者の方に向けて MDN の仕様に沿って解説します。
目次
data-* 属性と dataset とは何か
data-* 属性は、HTML 要素に独自のデータを埋め込むためのグローバル属性です。data- で始まる名前を付ければ、data-user-id や data-state のように自由な名前で値を持たせられます。クラスや id のように見た目や識別のためではなく、「この要素に紐づく追加情報」を保持しておく目的で使います。
この data-* 属性に JavaScript からアクセスする入口が dataset です。dataset は HTMLElement が持つ読み取り専用プロパティで、その要素に付いたすべての data-* 属性を DOMStringMap というオブジェクトとしてまとめて返します。element.dataset.userId のようにプロパティ経由で値を読み書きでき、専用のメソッドを呼ばずに属性を扱えるのが特徴です。
HTML に data-* 属性を書く
まずは HTML 側の書き方です。属性名は data- に続けて任意の名前を付けます。名前には英小文字・数字・ハイフンなどが使え、値はダブルクォートで囲んだ文字列で指定します。複数の data-* 属性を1つの要素に付けても構いません。
<button
id="btn"
data-user-id="42"
data-user-name="Taro"
data-role="admin">
会員情報を表示
</button>
この例では、ボタン1つに会員の ID・名前・権限という3つの情報を data-* 属性として持たせています。これらの値を JavaScript から取り出すのが、次に紹介する dataset です。
dataset で値を読み取る
読み取りは element.dataset.プロパティ名 の形で行います。先ほどのボタンであれば、btn.dataset.userId で data-user-id の値を取得できます。下のデモでは、ボタンをクリックすると dataset から3つの値を読み取り、その内容を画面に表示します。コードを書き換えながら動きを確かめてみてください。
<div class="demo">
<p class="result" id="result">ボタンを押すと、data-* 属性の値を読み取って表示します。</p>
<button
id="btn"
data-user-id="42"
data-user-name="Taro"
data-role="admin">
この会員の情報を表示
</button>
</div>
.demo {
font-family: sans-serif;
padding: 8px;
}
.result {
margin: 0 0 16px;
padding: 12px;
background: #f1f3f5;
border-radius: 8px;
min-height: 1.5em;
}
button {
padding: 10px 18px;
border: none;
border-radius: 8px;
background: #007bff;
color: #fff;
font-size: 15px;
cursor: pointer;
}
button:hover {
background: #0069d9;
}
const btn = document.getElementById("btn");
const result = document.getElementById("result");
btn.addEventListener("click", () => {
// data-user-id -> dataset.userId
// data-user-name -> dataset.userName
// data-role -> dataset.role
const id = btn.dataset.userId;
const name = btn.dataset.userName;
const role = btn.dataset.role;
result.textContent =
"ID: " + id + " / 名前: " + name + " / 権限: " + role;
});
このように、要素自身がデータを持っているので、クリックされた要素から直接情報を取り出せます。クリックイベントの中で event.currentTarget.dataset を参照すれば、同じ処理を多数の要素で共有しつつ、それぞれに固有の値を扱う、といった設計もしやすくなります。
属性名のケバブケースは dataset でキャメルケースになる
dataset を使ううえで最初につまずきやすいのが、属性名の表記の違いです。HTML の data-* 属性はハイフン区切り(ケバブケース)で書きますが、dataset でアクセスするときはキャメルケースに変換した名前を使います。具体的には、data- の接頭辞を取り除き、ハイフンに続く文字を大文字にした名前になります。data-user-id なら dataset.userId、data-role ならハイフンがないのでそのまま dataset.role です。
主な対応関係を表にまとめると、次のようになります。HTML 側はすべて小文字+ハイフン、JavaScript 側はキャメルケース、という対応を押さえておけば迷いません。
| HTML の属性名 | dataset でのアクセス名 |
|---|---|
data-id | dataset.id |
data-user-id | dataset.userId |
data-user-name | dataset.userName |
data-role | dataset.role |
data-is-active | dataset.isActive |
逆方向も同じルールです。dataset.userName = "Hanako" のようにキャメルケースのプロパティへ書き込むと、HTML 側では data-user-name という属性として反映されます。JavaScript ではハイフンを含むプロパティ名がそのままでは書けないため、この変換が用意されている、と理解しておくと自然です。
dataset で値を書き込んで状態を切り替える
dataset は読み取りだけでなく書き込みもできます。element.dataset.state = "open" のように代入すると、対応する data-state 属性が要素に追加・更新されます。この性質を使うと、要素に「現在の状態」を data-* 属性として持たせ、JavaScript で書き換えながら CSS の属性セレクタ([data-state="open"] など)で見た目を切り替える、という連携ができます。
下のデモは、パネルの開閉状態を data-state 属性で管理する例です。ボタンを押すたびに dataset.state の値を open と closed で反転させ、その値に応じて CSS が中身の表示・非表示を切り替えています。状態を JavaScript の変数ではなく DOM 自体に持たせている点に注目してください。
<div class="panel" id="panel" data-state="closed">
<button id="toggle">開閉する</button>
<p class="status" id="status">現在の状態: closed</p>
<div class="body">
<p>data-state 属性に状態を持たせ、JS で書き換えています。</p>
<p>属性の値は CSS のセレクタからも参照できます。</p>
</div>
</div>
.panel {
font-family: sans-serif;
max-width: 360px;
padding: 16px;
border: 1px solid #ddd;
border-radius: 8px;
}
button {
padding: 8px 16px;
border: none;
border-radius: 6px;
background: #007bff;
color: #fff;
cursor: pointer;
}
.status {
margin: 12px 0;
color: #555;
}
/* data-state の値に応じて表示を切り替える */
.panel[data-state="closed"] .body {
display: none;
}
.panel[data-state="open"] .body {
display: block;
padding: 12px;
background: #e3f2fd;
border-radius: 6px;
}
const panel = document.getElementById("panel");
const toggle = document.getElementById("toggle");
const status = document.getElementById("status");
toggle.addEventListener("click", () => {
// 現在の状態を dataset から読む(data-state -> dataset.state)
const current = panel.dataset.state;
// 状態を反転して書き込む。これで data-state 属性自体が書き換わる
const next = current === "open" ? "closed" : "open";
panel.dataset.state = next;
status.textContent = "現在の状態: " + next;
});
属性を削除したいときは、delete element.dataset.state のように delete 演算子を使います。これで対応する data-state 属性が要素から取り除かれます。状態を「無し」に戻したい場面で使うと便利です。
dataset の値は必ず文字列になる
dataset を使ううえで必ず覚えておきたいのが、取得できる値は常に文字列だという点です。HTML の属性値はもともと文字列なので、data-user-id="42" を dataset.userId で読んでも、得られるのは数値の 42 ではなく文字列の "42" です。同じく data-is-active="true" を読んでも、真偽値の true ではなく文字列の "true" が返ります。
そのため、数値として計算したいときは Number(btn.dataset.userId) や parseInt(btn.dataset.userId, 10) のように明示的に変換します。真偽値として扱いたいときも、btn.dataset.isActive === "true" のように文字列との比較で判定するのが安全です。書き込むときも同様で、dataset.count = 3 のように数値を代入しても、内部では文字列 "3" に変換されて属性に保存されます。
// data-user-id="42" を読むと文字列 "42"
const raw = btn.dataset.userId;
console.log(typeof raw); // "string"
// 数値として使うときは変換する
const id = Number(btn.dataset.userId);
console.log(id + 1); // 43
// 真偽値は文字列との比較で判定する
const active = btn.dataset.isActive === "true";
getAttribute・setAttribute との関係
data-* 属性は通常の属性でもあるため、dataset を使わずに getAttribute() や setAttribute() で読み書きすることもできます。たとえば btn.getAttribute("data-user-id") と btn.dataset.userId は同じ値を返し、btn.setAttribute("data-user-id", "99") と btn.dataset.userId = "99" は同じ結果になります。どちらも同じ属性を操作しているだけなので、片方で書き換えればもう片方からも変更後の値が読めます。
大きな違いは名前の指定方法です。getAttribute() 系では "data-user-id" のように data- を含むケバブケースの完全な属性名を文字列で渡します。一方 dataset では data- を除いたキャメルケースのプロパティ名でアクセスします。data-* 属性を専門に扱うなら、変換ルールさえ押さえれば dataset の方が短く書けて読みやすいです。属性名を動的に組み立てたい、あるいは data-* 以外の属性も同じコードで扱いたい、といった場合は getAttribute() 系が向いています。
まとめ
data-* 属性は HTML 要素に独自データを持たせるための仕組みで、JavaScript からは HTMLElement.dataset を通じて読み書きできます。HTML 側のケバブケース(data-user-id)は、dataset ではキャメルケース(dataset.userId)になる、という変換ルールがポイントです。読み取りも代入も dataset.プロパティ名 の形で書け、delete で属性を削除できます。取得できる値は常に文字列なので、数値や真偽値として使うときは Number() や文字列比較で変換しましょう。getAttribute() や setAttribute() と同じ属性を扱っているため、状況に応じて使い分けると、HTML と JavaScript の値の受け渡しをすっきり書けます。