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.

Reading data-* attributes with datasetTry it yourself
<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.

Cloning a template safelyTry it yourself
<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?

  1. element.data.price-cents
  2. element.dataset.priceCents
  3. element.getData('price')
  4. element.priceCents
Show answer

Answer: element.dataset.priceCents

dataset converts the dashed name after data- to camelCase.