Command Palette

Search for a command to run...

Hectal
PHASE 2Beginner ~26 min· topic 7 of 7

Topic 2.7

Global Attributes

In one line

Global attributes work on every HTML element: id and class name elements, title, lang and dir describe them, hidden, tabindex and contenteditable change behaviour, and data-* stores your own data.

Think of it like this

Labels you can stick on anything in a house. A name tag with a unique name (id), colour stickers shared by many things ("all blue stickers are kitchen items": class), a note that appears when you hover (title), a "written in Hindi" label (lang), a "put away for now" sticker (hidden), and blank labels where you write anything you want to remember (data-*). Any sticker fits any object.

Words you'll meet

New words in this topic, in plain English. Come back here whenever one feels fuzzy.

Attribute
Extra information inside an element's start tag, written name="value".
Global attribute
An attribute that is allowed on every HTML element, like id or class.
id
A unique name for one element on the page. No two elements may share it.
class
A name (or several, separated by spaces) shared by any number of elements, used for styling and scripting.
Tooltip
A small box of text that appears when you hold the mouse over something.
Text direction
Whether text runs left to right (English) or right to left (Arabic, Hebrew, Urdu).
Focusable
Able to receive keyboard focus, so it can be reached with Tab or .focus().
data-* attribute
Your own attribute starting with data-, for storing small pieces of information on an element.
camelCase
Writing joined words with capitals for each new word, like userId. Used for dataset names.

Step by step

01Name one element with id, many with class

The contact section gets a unique id so links can jump to it. Every card shares the class card so one CSS rule styles them all; the special one has a second class.

index.htmlwhole filehtml
<a href="#contact">Contact us</a>
...
<div class="card">Basic</div>
<div class="card featured">Pro</div>
<section id="contact">
  <h2>Contact</h2>
</section>

02Describe language and direction

Mark a phrase in another language with lang. For right-to-left scripts, add dir="rtl"; for text typed by users where you can't know the language, use dir="auto".

index.htmlwhole filehtml
<p>The French say <span lang="fr">bon appétit</span>.</p>
<p lang="ar" dir="rtl">مرحبا بالعالم</p>
<p dir="auto">{{ user comment goes here }}</p>

03Hide and show

hidden removes an element from the page and from screen readers. Remove the attribute (in JavaScript, el.hidden = false) to show it again. Be careful with CSS that sets display, which can win over hidden.

index.htmlwhole filehtml
<p id="msg" hidden>Saved!</p>

04Control keyboard focus

Links, buttons and form fields are focusable already. Only add tabindex="0" to a custom element that really acts like a control, and tabindex="-1" to something you'll focus from code, like an error summary.

index.htmlwhole filehtml
<div id="errors" tabindex="-1">Please fix 2 errors.</div>
<!-- later, in JavaScript: errors.focus() -->
Control keyboard focusdiagram
Rendering diagram…

05Store data for scripts

Put data the page's JavaScript needs on the element itself. JavaScript reads it through dataset. Remember every value comes back as a string: convert numbers with Number().

index.htmlwhole filehtml
<li data-price="250" data-in-stock="true">Notebook</li>
<script>
  const li = document.querySelector("li");
  console.log(li.dataset.price);    // "250" (a string)
  console.log(li.dataset.inStock);  // "true" (a string, not a boolean)
</script>

06Let users edit content

contenteditable turns an element into an editable area. The browser handles typing, selection, undo and paste. Saving the result is up to your JavaScript.

index.htmlwhole filehtml
<div contenteditable="true">Click and type here.</div>

Try it yourself

  1. 1

    Add a product

    In the first example, add a fourth li with class="item" and data-price="60". Predict the new total, then run: Total: 470.

  2. 2

    Fix the hidden bug

    In the second example, add this CSS rule: [hidden] { display: none !important; }. Predict the second console line, then run. The red paragraph disappears and it prints none. Many CSS resets include this rule for that reason.

  3. 3

    Walk the tab order

    In the third example, click the preview's empty space, then press Tab repeatedly. Add tabindex="0" to the plain div and try again: it joins the order.

Code & diagrams

id, class, data-* and dataset New tab

CSS styled the out-of-stock pencil from its data attribute. dataset turned data-in-stock into inStock.

Preview

Press Run to see the page.

Console

Nothing logged yet.

Expected console output

Items with class item: 3
DOMStringMap { price: '120', inStock: 'true' }
string 120
Total: 410
Classes of pencil: 2 item sale
hidden, title, lang, dir and contenteditable New tab

The red paragraph has hidden, but the CSS display: block beat the browser's default hidden style. A classic bug.

Preview

Press Run to see the page.

Console

Nothing logged yet.

Expected console output

msg hidden: true none
forced hidden: true block
Direction of the Arabic line: rtl
Editable: true
tabindex: who can get keyboard focus New tab

Click the preview and press Tab: focus visits the button and the tabindex=0 div only. The plain div can't take focus at all.

Preview

Press Run to see the page.

Console

Nothing logged yet.

Expected console output

button: tabIndex 0
plain: tabIndex -1
zero: tabIndex 0
minus: tabIndex -1
Focused now: minus
After focusing the plain div: minus

Break it on purpose

Errors are the best teachers. Make each change, read the error, guess what went wrong, then reveal the answer.

Break #1

Reuse an id

Give two elements id="pen" in the first example.

terminal
$ W3C validator
── what you'll see ──
Error: Duplicate ID pen.

Break #2

Treat a data value as a number

In the first example, replace Number(li.dataset.price) with li.dataset.price.

terminal
$ Console
── what you'll see ──
Total: 025040120

Break #3

Use positive tabindex

Add tabindex="1" to an element near the bottom of a page.

terminal
$ keyboard
── what you'll see ──
The first Tab press jumps to the bottom of the page, then focus returns to the top and continues normally.

Myth vs fact

Myth

id and class are the same, just used differently by CSS.

Fact

An id must be unique and is used for links, labels and ARIA references; a class is meant to be shared. They also have very different CSS specificity (CSS course).

Myth

hidden always hides an element.

Fact

It hides it through default CSS, which your own display rule can override. Add [hidden] { display: none !important; } if your CSS sets display on things that might be hidden.

Myth

title is a good place for helpful information.

Fact

Tooltips only appear on mouse hover. Touch, keyboard and many screen-reader users never get them, so put important information in visible text.

Myth

data-* values can be numbers or booleans.

Fact

Attribute values are always strings. data-count="3" reads as "3"; convert it yourself.

Pro corner

Extra depth for experienced readers. New to this? Skip it for now and come back later.

  • ▸

    An element with an id also becomes a property of window ("named access"): <p id="msg"> is reachable as window.msg unless a real global with that name exists. It's handy in quick demos but fragile in real code; use getElementById.

  • ▸

    dataset converts names by rule: data-user-id → userId, and setting el.dataset.lastSeen = "x" creates data-last-seen. Names must not contain uppercase ASCII letters in HTML (they're lowercased anyway).

  • ▸

    dir="auto" picks the direction from the first strong directional character (the first letter that belongs to a left-to-right or right-to-left script). For user-generated content, also consider the bdi element, which isolates a piece of text so it can't flip the surrounding line.

  • ▸

    inert (Baseline 2023) makes a whole subtree non-interactive and hidden from assistive tech, which is what modal dialogs do to the page behind them. contenteditable="plaintext-only" reached all major engines in 2025; check support for older browsers.

Remember this

  1. 1

    An attribute is extra information written inside an element's start tag, like href on a. Most attributes belong to particular elements, but global attributes are allowed on every element. You'll use them on nearly every page you write.

  2. 2

    **id** gives an element a unique name: no two elements on a page may share an id. It's used by link fragments (href="#contact", Topic 1.6), by label for (Phase 4), by aria-labelledby, popovertarget and JavaScript's getElementById. Ids are case-sensitive and must not contain spaces. **class** is a space-separated list of names shared by any number of elements, mainly for CSS and JavaScript: class="card featured" has two classes.

  3. 3

    **title shows a tooltip on mouse hover. Don't put important information only there: touch-screen and keyboard users never see it, and screen readers read it inconsistently. lang** sets the language of an element's content (overriding the page's lang), so a French quote on an English page is pronounced and hyphenated correctly. **dir** sets the text direction: ltr (left to right), rtl (right to left, for Arabic, Hebrew, Urdu) or auto (let the browser decide from the text, useful for user-written content).

  4. 4

    **hidden** hides an element completely: not shown, not read by screen readers, not focusable. It works through the browser's default CSS (display: none), so a CSS rule like display: block on the same element can override it and accidentally show it again. **tabindex** controls keyboard focus: 0 makes a non-focusable element focusable in the normal Tab order, -1 makes it focusable only from JavaScript (el.focus()), and positive numbers (1, 2…) force a custom order, which is almost always a mistake.

  5. 5

    **contenteditable** makes an element's content editable by the user, like a mini word processor (true, false, or plaintext-only for text without formatting). It's how rich text editors are built. **data-*** attributes store your own information on an element: data-price="250", data-user-id="7". They don't change anything on the page; JavaScript reads them through element.dataset (with the name converted to camelCase: data-user-id becomes dataset.userId) and CSS can select them ([data-status="done"]). Values are always strings.

  6. 6

    Other global attributes you'll meet later: style (inline CSS, in the CSS course), aria-* and role (accessibility, Phase 7), inert (makes a region unusable while e.g. a panel is open), draggable, spellcheck, translate="no" (tells translators to leave a brand name alone), autofocus, popover (Topic 2.6), accesskey and inputmode / enterkeyhint (Phase 4).

Explain it without notes

01

What's the difference between id and class? When would you use each?

02

What are data-* attributes for, and how do JavaScript and CSS read them?

03

Explain the three kinds of tabindex values and which one to avoid.

04

Why can an element with hidden still be visible?

Practice

01

Write a list of three books, each with data-year. Add a script that logs the oldest year.

02

Mark up an English page sentence that contains the Hindi word नमस्ते, and a user comment of unknown language.

03

Make a "Saved!" message that starts hidden, and a script that shows it.

Trade-offs

  • ↔

    data-* attributes keep data close to the element that uses it, but large or sensitive data doesn't belong in the HTML: it's public to anyone who views the source, and big values bloat the page.

  • ↔

    contenteditable gives editing for free, but its output HTML differs between browsers and can contain unexpected markup; real editors sanitise and normalise it, and anything users type must be treated as untrusted before you store or display it.

Done when you can

  • I can use id for unique names and class for shared ones.

  • I can set lang and dir on part of a page.

  • I can hide content with hidden and know the CSS trap.

  • I can use tabindex 0 and -1 correctly and avoid positive values.

  • I can store data in data-* and read it with dataset, converting types.