HTML Attributes

Article · 6:00

Attributes configure an element: where a link goes, what an image is of, whether a field is required. A handful work on almost every element, and two of them - id and class - are how CSS and JavaScript find anything at all.

This lesson has 7 runnable examples. Press Try yourself! under any of them to open it in the editor.

HTML Attributes

An element says what something is. An attribute configures it: where this link goes, what this image shows, which field this label belongs to, whether this input must be filled in. Attributes go in the start tag, as name-and-value pairs.

HTML Example 1 2 lines
<a href="https://example.com" title="The example domain">Visit example.com</a>
<img src="cat.jpg" alt="A grey cat asleep on a windowsill" width="400" height="300">

Open this example in the editor and change it. Nothing you type is saved.

Three rules cover almost everything:

Quote the value. HTML permits unquoted values in simple cases, and the day you write class=card wide you get one class called card and a mysterious attribute called wide. Quotes cost two characters and remove the question.

Attribute names are case-insensitive, values usually are not. HREF and href are the same attribute; cat.jpg and Cat.JPG are different files on most servers.

Order does not matter. Put them in whatever order reads best, consistently.

Global attributes

Most attributes belong to one element - href on a link, src on an image. A set of global attributes work on nearly all of them:

Attribute What it is for
id A unique name for this one element
class A shared label, used by CSS and scripts
title Advisory information, usually shown as a tooltip
lang The language of this element's content
dir Text direction: ltr, rtl or auto
hidden Remove the element from the page entirely
tabindex Change whether and where the element takes keyboard focus
data-* Your own data, for your own scripts

id and class

These two carry most of the weight, and the difference is simple.

An id names exactly one element in the document. It must be unique, and it is how a fragment link finds its target, how a label finds its input, and how a script grabs a single known element.

A class is a label that any number of elements can share. It is the normal hook for CSS and for scripts that act on a group.

HTML Example 2 5 lines
<h2 id="overview" class="section-title">Overview</h2>
<p class="lead">The first paragraph of the section.</p>
<p class="lead">Another paragraph with the same treatment.</p>

<a href="#overview">Jump to the overview</a>

Open this example in the editor and change it. Nothing you type is saved.

Duplicate ids are the common mistake, usually created by copying a block of markup. Nothing visibly breaks: the page renders, CSS still applies. But #overview as a fragment link scrolls to the first one, a script asking for that id gets the first one, and a label pointing at a duplicated input labels the wrong field. It is a bug that hides until the page gets long.

style, title, lang and dir

style applies CSS directly to one element. It works, it is occasionally the right tool for a value a script has calculated, and as a habit it is a trap: it scatters presentation through the document, it overrides your stylesheet in a way that is awkward to undo, and it cannot express a hover state or a media query.

title supplies advisory text, usually surfaced as a tooltip. It is worth knowing that it is weak: it does not appear on touch devices, it is inconsistently announced by screen readers, and it cannot be reached by keyboard in most browsers. Never put information in a title that is not available some other way.

lang and dir handle language. lang on the html element declares the document's language; lang on a smaller element marks a passage in a different one, which is what lets a screen reader switch pronunciation mid-sentence. dir="rtl" sets right-to-left text direction for languages such as Arabic and Hebrew.

HTML Example 3 3 lines
<p>The French call it <span lang="fr">la langue de balisage</span>.</p>
<p dir="rtl" lang="ar">مرحبا بالعالم</p>
<p>Prefer a stylesheet, but <span style="color: teal">this works</span>.</p>

Open this example in the editor and change it. Nothing you type is saved.

Boolean attributes

Some attributes are true simply by being there. required, disabled, checked, readonly, multiple, hidden - their presence is the value.

HTML Example 4 7 lines
<form>
  <label for="email">Email</label>
  <input id="email" name="email" type="email" required>

  <button type="submit">Sign up</button>
  <button type="button" disabled>Unavailable</button>
</form>

Open this example in the editor and change it. Nothing you type is saved.

There is no required="false". Writing it sets the attribute, and the attribute being present makes the field required - the string false is simply ignored. To turn a boolean attribute off, remove it.

Data attributes

data-* attributes let you attach your own information to an element without inventing attributes the browser does not know.

HTML Example 5 4 lines
<article class="course-card" data-course-id="html-101" data-level="beginner">
  <h3>HTML: From Basics to Expert</h3>
  <p>Thirty topics, every example runnable.</p>
</article>

Open this example in the editor and change it. Nothing you type is saved.

A script reads these through the element's dataset property, where data-course-id arrives as dataset.courseId. CSS can match on them too, with an attribute selector.

Two cautions. They are visible in the page source, so nothing secret goes in one. And they are for data, not for meaning: data-heading="true" on a div does not make it a heading to anything except your own code, where an h2 would have made it a heading to everything.

hidden and tabindex

Two globals in that table deserve a warning each.

hidden removes an element from the page completely: it is not rendered, not read aloud, and not reachable by keyboard. That is the right behaviour when content genuinely does not apply yet. It is the wrong tool for anything you want visually hidden but still announced - a label for a search box, say - because hidden hides it from everyone.

tabindex controls keyboard focus, and has three cases:

HTML Example 6 3 lines
<div tabindex="0" role="button">Focusable, in the normal order</div>
<div tabindex="-1" id="main">Focusable only by script, not by tabbing</div>
<p hidden>Not rendered, not announced, not reachable.</p>

Open this example in the editor and change it. Nothing you type is saved.

tabindex="0" puts an element into the natural tab order. tabindex="-1" makes it focusable by script - useful on a main element so a skip link can move focus there - without adding it to the tab order. Any positive number jumps the element to the front of the tab order ahead of everything natural, and the result on a real page is a tab sequence nobody can predict. Treat positive values as a mistake.

The deeper point is that a button is already focusable, already activated by Enter and Space, and already announced as a button. Reaching for tabindex and role on a div usually means an element that already exists was passed over.

Matching on attributes

Attributes are not only configuration; they are how CSS and scripts find things.

HTML Example 7 4 lines
<a class="external" href="https://example.com" target="_blank" rel="noopener">
  Opens in a new tab
</a>
<input id="email" name="email" type="email" required>

Open this example in the editor and change it. Nothing you type is saved.

CSS can select on any of that: a[target="_blank"] to add an icon to every link that opens a new tab, input[required] to mark required fields, [lang="fr"] to set a different font. Scripts can do the same through querySelectorAll. It means a consistent attribute is often a better hook than an extra class, because it is already carrying the meaning you would be duplicating.

Practice task

Take the page from the previous lesson and add a navigation list at the top whose links jump to each h2 on the page, using id on the headings and #fragment links in the list. Then add a data- attribute to one element and read it back in the console with document.querySelector('[data-your-attribute]').dataset. The point of the second half is to see that the browser hands your own data back to you without you having to parse anything.