27. data-*属性(JS用データの渡し方)
まず結論
data-*を使うと、HTMLに“JSが読むための値”を安全に持たせて、クリック対象や設定値を管理しやすくできます。
最小の書き方(コピペで動く最小コード)
HTMLCode
<!doctype html>
<html lang="ja">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>data-* Minimum</title>
</head>
<body>
<button type="button" data-action="open" data-target="#modal">
開く
</button>
<script>
// data-* を読む最小例(学習用)
const btn = document.querySelector('button[data-action="open"]');
console.log(btn.dataset.action); // "open"
console.log(btn.dataset.target); // "#modal"
</script>
</body>
</html>
Previewdesktop
HTMLCode
<!doctype html>
<html lang="ja">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>data-* Minimum</title>
</head>
<body>
<button type="button" data-action="open" data-target="#modal">
開く
</button>
<script>
// data-* を読む最小例(学習用)
const btn = document.querySelector('button[data-action="open"]');
console.log(btn.dataset.action); // "open"
console.log(btn.dataset.target); // "#modal"
</script>
</body>
</html>
Preview を表示
datasetで読める(data-target → dataset.target)のがポイント。
重要ポイント(ここで迷いがち)
- data-*は「自由に作れる属性」
例:data-id/data-action/data-user-nameなど。 - JSで読むときは
element.datasetが便利data-id="12"→el.dataset.idは"12"(文字列)data-user-name="taro"→el.dataset.userName(ハイフンはキャメルケース)
- 値は基本“文字列”として扱われる
数字として使うならNumber(el.dataset.count)など変換が必要。 - idやclassの乱用を減らせる
- classは見た目用
- data-*は“挙動/設定値”用
で分けると保守が楽。
- 機密情報を入れない
HTMLは誰でも見られる。トークンやパスワードなどは絶対入れない。
例で理解(よく使うパターン 5つ)
1) 一覧の「どれが押されたか」を持つ(data-id)
HTMLCode
<ul>
<li><button type="button" data-id="101">カートに入れる</button></li>
<li><button type="button" data-id="102">カートに入れる</button></li>
</ul>
Previewdesktop
HTMLCode
<ul>
<li><button type="button" data-id="101">カートに入れる</button></li>
<li><button type="button" data-id="102">カートに入れる</button></li>
</ul>
Preview を表示
2) 動作を分ける(data-action)
HTMLCode
<button type="button" data-action="increment">+</button>
<button type="button" data-action="decrement">-</button>
Previewdesktop
HTMLCode
<button type="button" data-action="increment">+</button>
<button type="button" data-action="decrement">-</button>
Preview を表示
3) ターゲットを指定する(data-target)
HTMLCode
<button type="button" data-action="toggle" data-target="#faq1">開閉</button>
<div id="faq1" hidden>...</div>
Previewdesktop
HTMLCode
<button type="button" data-action="toggle" data-target="#faq1">開閉</button>
<div id="faq1" hidden>...</div>
Preview を表示
4) しきい値など設定値をHTML側に置く(data-max)
HTMLCode
<input name="count" type="number" data-max="10" />
Previewdesktop
HTMLCode
<input name="count" type="number" data-max="10" />
Preview を表示
5) CSSでも“条件分岐っぽく”使える(属性セレクタ)
HTMLCode
<button class="btn" data-variant="ghost" type="button">ボタン</button>
Previewdesktop
HTMLCode
<button class="btn" data-variant="ghost" type="button">ボタン</button>
Preview を表示
CSSCode
.btn[data-variant="ghost"]{
border-style: dashed;
opacity: .9;
}
使い分け(class / id / aria-* との違い)
- class:主に見た目(CSS)や“同じ部品”の目印
- id:ページ内で唯一(ラベル紐付け、ページ内リンク)
- data-*:JS用の値・種類・設定・識別子(“意味はアプリ都合”)
- aria-*:アクセシビリティ(読み上げ・状態)用
→ “操作の状態”はaria-expanded等で持つのが基本(#17でも触れた)
実務のコツ(SEO/安全/アクセシビリティ)
js-class と data-* を組み合わせると事故が減る
例:.js-toggle(JSの対象) +data-target="#x"(対象ID)- 状態はdata-*だけで表さない(必要ならariaも併用)
例:開閉ならaria-expanded="true/false"を更新する(読み上げに効く)。 - 値は短く・安定に
data-action="open"のように“動詞”で揃えると読みやすい。 - 改ざんされる前提で
data-price="1000"を信じて決済…みたいなことはNG(サーバー側で検証)。
NG・禁止例(事故る書き方)
NG1)機密情報をdata-*に入れる
HTMLCode
<div data-token="SECRET">...</div>
Previewdesktop
HTMLCode
<div data-token="SECRET">...</div>
Preview を表示
✅ 正:機密はHTMLに置かない(サーバー側・安全な仕組みで扱う)
NG2)idを増やしまくってJS専用にする
HTMLCode
<button id="btn1">...</button>
<button id="btn2">...</button>
Previewdesktop
HTMLCode
<button id="btn1">...</button>
<button id="btn2">...</button>
Preview を表示
✅ 正:同じ種類はclassやdataでまとめる
HTMLCode
<button class="js-add" data-id="1">...</button>
<button class="js-add" data-id="2">...</button>
Previewdesktop
HTMLCode
<button class="js-add" data-id="1">...</button>
<button class="js-add" data-id="2">...</button>
Preview を表示
NG3)dataの命名がバラバラで読めない
HTMLCode
<button data-act="open" data-targetid="x">...</button>
Previewdesktop
HTMLCode
<button data-act="open" data-targetid="x">...</button>
Preview を表示
✅ 正:ルールを決めて揃える
HTMLCode
<button data-action="open" data-target="#x">...</button>
Previewdesktop
HTMLCode
<button data-action="open" data-target="#x">...</button>
Preview を表示
NG4)値が数字なのに文字列のまま計算してバグる
JavaScriptCode
el.dataset.count + 1 // "3" + 1 → "31"
✅ 正:数値に変換
JavaScriptCode
Number(el.dataset.count) + 1
見た目を整える(HTML+CSSセット:4例)
色は使わず、線・余白・影・動きで整えます。
例1)data-actionでボタン種類を分ける(CSS側)
例1)data-actionでボタン種類を分ける(CSS側)
HTMLCode
<div class="actions">
<button class="btn" data-action="save" type="button">保存</button>
<button class="btn" data-action="delete" type="button">削除</button>
</div>CSSCode
.actions{ display:flex; gap:.6rem; flex-wrap: wrap; }
.btn{
min-height: 44px;
padding: .65rem 1rem;
border: 1px solid currentColor;
border-radius: 14px;
background: transparent;
cursor: pointer;
box-shadow: 0 10px 22px rgba(0,0,0,.08);
transition: transform .15s ease;
}
.btn:hover{ transform: translateY(-1px); }
.btn[data-action="delete"]{ border-style: dashed; opacity: .85; }Previewdesktop
HTMLCode
<div class="actions">
<button class="btn" data-action="save" type="button">保存</button>
<button class="btn" data-action="delete" type="button">削除</button>
</div>CSSCode
.actions{ display:flex; gap:.6rem; flex-wrap: wrap; }
.btn{
min-height: 44px;
padding: .65rem 1rem;
border: 1px solid currentColor;
border-radius: 14px;
background: transparent;
cursor: pointer;
box-shadow: 0 10px 22px rgba(0,0,0,.08);
transition: transform .15s ease;
}
.btn:hover{ transform: translateY(-1px); }
.btn[data-action="delete"]{ border-style: dashed; opacity: .85; }Preview を表示
例2)カード一覧:data-idで“どれ”かを持つ
例2)カード一覧:data-idで“どれ”かを持つ
HTMLCode
<div class="grid">
<button class="card js-open" data-id="101" type="button">
<span class="card__title">商品A</span>
<span class="card__meta">ID: 101</span>
</button>
<button class="card js-open" data-id="102" type="button">
<span class="card__title">商品B</span>
<span class="card__meta">ID: 102</span>
</button>
</div>CSSCode
.grid{ display:grid; gap: 1rem; }
@media (min-width: 760px){ .grid{ grid-template-columns: 1fr 1fr; } }
.card{
text-align: left;
padding: 1rem 1.1rem;
border: 1px solid rgba(0,0,0,.18);
border-radius: 18px;
background: transparent;
cursor: pointer;
box-shadow: 0 10px 22px rgba(0,0,0,.08);
}
.card__title{ display:block; font-weight: 600; }
.card__meta{ display:block; opacity: .8; margin-top: .35rem; }Previewdesktop
HTMLCode
<div class="grid">
<button class="card js-open" data-id="101" type="button">
<span class="card__title">商品A</span>
<span class="card__meta">ID: 101</span>
</button>
<button class="card js-open" data-id="102" type="button">
<span class="card__title">商品B</span>
<span class="card__meta">ID: 102</span>
</button>
</div>CSSCode
.grid{ display:grid; gap: 1rem; }
@media (min-width: 760px){ .grid{ grid-template-columns: 1fr 1fr; } }
.card{
text-align: left;
padding: 1rem 1.1rem;
border: 1px solid rgba(0,0,0,.18);
border-radius: 18px;
background: transparent;
cursor: pointer;
box-shadow: 0 10px 22px rgba(0,0,0,.08);
}
.card__title{ display:block; font-weight: 600; }
.card__meta{ display:block; opacity: .8; margin-top: .35rem; }Preview を表示
例3)トグル(data-target + focus見え)
例3)トグル(data-target + focus見え)
HTMLCode
<button class="btn js-toggle" type="button" data-target="#faqx" aria-expanded="false">
FAQを開閉
</button>
<div id="faqx" hidden>
<p>中身</p>
</div>CSSCode
.btn{
min-height: 44px;
padding: .65rem 1rem;
border: 1px solid currentColor;
border-radius: 14px;
background: transparent;
cursor: pointer;
box-shadow: 0 10px 22px rgba(0,0,0,.08);
}
.btn:focus-visible{
outline: 3px solid currentColor;
outline-offset: 4px;
border-radius: 14px;
}
#faqx{
margin-top: .8rem;
padding: .9rem 1rem;
border: 1px solid rgba(0,0,0,.18);
border-radius: 16px;
}Previewdesktop
HTMLCode
<button class="btn js-toggle" type="button" data-target="#faqx" aria-expanded="false">
FAQを開閉
</button>
<div id="faqx" hidden>
<p>中身</p>
</div>CSSCode
.btn{
min-height: 44px;
padding: .65rem 1rem;
border: 1px solid currentColor;
border-radius: 14px;
background: transparent;
cursor: pointer;
box-shadow: 0 10px 22px rgba(0,0,0,.08);
}
.btn:focus-visible{
outline: 3px solid currentColor;
outline-offset: 4px;
border-radius: 14px;
}
#faqx{
margin-top: .8rem;
padding: .9rem 1rem;
border: 1px solid rgba(0,0,0,.18);
border-radius: 16px;
}Preview を表示
例4)入力にdata-max(UIヒントとして)
例4)入力にdata-max(UIヒントとして)
HTMLCode
<div class="field">
<label class="label" for="count">人数</label>
<input class="input" id="count" name="count" type="number" data-max="10" placeholder="最大10" />
</div>CSSCode
*{ box-sizing: border-box; }
.field{ display: grid; gap: .35rem; max-width: 420px; }
.label{ font-size: .95rem; }
.input{
padding: .75rem .9rem;
border: 1px solid rgba(0,0,0,.25);
border-radius: 14px;
box-shadow: 0 10px 22px rgba(0,0,0,.06);
}Previewdesktop
HTMLCode
<div class="field">
<label class="label" for="count">人数</label>
<input class="input" id="count" name="count" type="number" data-max="10" placeholder="最大10" />
</div>CSSCode
*{ box-sizing: border-box; }
.field{ display: grid; gap: .35rem; max-width: 420px; }
.label{ font-size: .95rem; }
.input{
padding: .75rem .9rem;
border: 1px solid rgba(0,0,0,.25);
border-radius: 14px;
box-shadow: 0 10px 22px rgba(0,0,0,.06);
}Preview を表示
理解チェック(3問・答え付き)
Q. data-user-name は dataset では何になる?
A. dataset.userName。
Q. datasetで取れる値の型は基本何?
A. 文字列。
Q. data-*に入れてはいけないものは?
A. 機密情報(トークン/パスワード等)。
ミニ演習(すぐ試せる小課題 2つ)
-
ボタンを3つ作って、
data-action="open" / "close" / "reset"を付けてください。
DevToolsのConsoleでdocument.querySelector('[data-action="open"]').dataset.actionを試す。 -
記事カードを2つ作り、それぞれに
data-idを付けてください。
“どれを押したか”を扱えるイメージを言語化できたらOK(JSは書かなくてもOK)。