HTML5 Features
Canvas, video and audio, data-* attributes, the template element, and semantic input types like tel and url.
<video> and <audio>
HTML5 added native media playback — no Flash plugin, no third-party player required:
<video controls width="640" poster="preview.jpg">
<source src="lesson.mp4" type="video/mp4">
<source src="lesson.webm" type="video/webm">
Your browser doesn't support HTML5 video.
</video>
<audio controls>
<source src="podcast.mp3" type="audio/mpeg">
Your browser doesn't support HTML5 audio.
</audio>
controls— shows the browser's built-in play/pause/volume/seek UI.- Multiple
<source>elements let the browser pick the first format it supports (useful since not every browser supports every video codec). - The text between the tags is a fallback shown only in browsers old enough not to support the element at all.
- Useful boolean attributes:
autoplay(generally blocked by browsers unless alsomuted),loop,muted,preload.
<video autoplay muted loop>
<source src="background.mp4" type="video/mp4">
</video>
<canvas>
<canvas> is a blank, scriptable drawing surface — HTML provides the element, but all the actual drawing happens through JavaScript (the Canvas 2D API, or WebGL for 3D):
<canvas id="chart" width="400" height="200"></canvas>
const canvas = document.getElementById('chart');
const ctx = canvas.getContext('2d');
ctx.fillStyle = '#3b82f6';
ctx.fillRect(20, 20, 150, 80); // x, y, width, height
<canvas> is used for things markup and CSS can't express well: charts, image editors, games, generative graphics. It's a big topic on its own — the key thing to know here is that the element itself is just a pixel surface with no built-in shapes or scene graph; everything is imperative drawing calls.
data-* attributes
Custom data attributes let you attach arbitrary data to an element without inventing a non-standard attribute or abusing class:
<button data-user-id="482" data-role="admin">Delete user</button>
const button = document.querySelector('button');
console.log(button.dataset.userId); // "482" — camelCase in JS, kebab-case in HTML
console.log(button.dataset.role); // "admin"
Any attribute prefixed with data- is guaranteed by the spec to never collide with a future standard HTML attribute, and is automatically exposed through JavaScript's element.dataset object (with data-user-id becoming dataset.userId). This is the standard way to embed small pieces of state directly in markup for JavaScript (or CSS attribute selectors) to read later.
<template>
<template> holds markup that is parsed by the browser but not rendered and not executed until JavaScript explicitly clones it into the document. It's the standard way to define a reusable chunk of HTML for repeated UI (list items, cards) without writing string concatenation in JavaScript:
<template id="card-template">
<li class="card">
<h3 class="card-title"></h3>
<p class="card-body"></p>
</li>
</template>
<ul id="card-list"></ul>
const template = document.getElementById('card-template');
const list = document.getElementById('card-list');
function addCard(title, body) {
const clone = template.content.cloneNode(true);
clone.querySelector('.card-title').textContent = title;
clone.querySelector('.card-body').textContent = body;
list.appendChild(clone);
}
addCard('Flexbox', 'A one-dimensional layout model.');
Any <img>, <script>, or <video> inside a <template> doesn't load or run until the content is cloned out — the browser treats everything inside as inert until that point.
Semantic input types: tel and url
These were mentioned briefly under forms, but they're worth calling out as part of HTML5's broader theme: letting the browser understand what kind of data an input expects, rather than treating every field as a generic text box.
<input type="tel" name="phone" placeholder="+1 555 123 4567">
<input type="url" name="website" placeholder="https://example.com">
On mobile, type="tel" brings up a numeric phone keypad, and type="url" brings up a keyboard with . and / easily accessible — small details, but they meaningfully improve the experience of filling out a form on a phone, and they cost nothing beyond picking the right attribute value.
Common mistakes
- Using
<canvas>for content that's actually static and better expressed in HTML/CSS (a card layout, an icon) — canvas content isn't selectable text, isn't accessible to screen readers by default, and isn't indexed by search engines. - Forgetting a fallback message inside
<video>/<audio>for the rare case a browser doesn't support the element at all. - Reading
data-*attributes withgetAttribute('data-user-id')whenelement.dataset.userIdis simpler and handles the kebab-case-to-camelCase conversion automatically. - Putting live, already-running content inside
<template>and expecting it to behave normally — scripts and media inside a template don't execute until the content is cloned into the actual document.