HTML Tutorial · HTML5 Features · Lesson 27 of 34
HTML Templates and Data Attributes
Store custom data on elements with data-* attributes and reuse markup with the <template> element.
data-* attributes
Any attribute starting with data- is valid HTML and holds custom data for scripts and CSS. JavaScript reads it through element.dataset (with names converted to camelCase), and CSS can select on it.
<style>
[data-status="out"] { opacity: .5; text-decoration: line-through; }
</style>
<ul id="products">
<li data-sku="MUG-01" data-price-cents="1200" data-status="in">Mug</li>
<li data-sku="CAP-02" data-price-cents="1800" data-status="out">Cap</li>
</ul>
<p id="info">Click a product.</p>
<script>
document.getElementById("products").addEventListener("click", (e) => {
const { sku, priceCents, status } = e.target.dataset; // data-price-cents -> priceCents
document.getElementById("info").textContent = `${sku}: ${(priceCents / 100).toFixed(2)} USD, ${status === "in" ? "in stock" : "sold out"}`;
});
</script>
The template element
Content inside <template> is parsed but not rendered: scripts don't run, images don't load. JavaScript clones it to create elements, which is safer and clearer than building HTML strings.
<template id="card">
<article class="card"><h3></h3><p></p></article>
</template>
<div id="list"></div>
<style>.card { border: 1px solid #ddd; border-radius: 8px; padding: .5rem 1rem; margin: .5rem 0; }</style>
<script>
const people = [{ name: "Ada", role: "Engineer" }, { name: "Grace <b>Hopper</b>", role: "Admiral" }];
const tpl = document.getElementById("card");
for (const p of people) {
const card = tpl.content.cloneNode(true);
card.querySelector("h3").textContent = p.name; // text, not HTML: the <b> is shown literally
card.querySelector("p").textContent = p.role;
document.getElementById("list").append(card);
}
</script>
Using textContent to fill clones avoids injecting HTML, which protects against cross-site scripting (XSS) when the data comes from users.
Test yourself
How do you read data-price-cents="1200" in JavaScript?
element.data.price-centselement.dataset.priceCentselement.getData('price')element.priceCents
Show answer
Answer: element.dataset.priceCents
dataset converts the dashed name after data- to camelCase.