The previous article in this series, What is HTML?, covered the basics — what HTML is and why it exists. I'm not repeating that here. This article assumes you already know HTML's purpose, and it goes deep into the actual building blocks: tags, elements, the different categories of elements, attributes, and the common ways attributes get grouped in real projects.
This is a long one on purpose. Instead of just listing definitions, each concept here is broken down properly — what it is, how it works, what the syntax looks like, a working example, and the rules or mistakes that tend to trip people up. My goal is that you should be able to learn this topic from this article alone, without needing any external reference.
Table of Contents
- HTML Tags
- HTML Elements
- Tag vs Element — The Core Difference
- Predefined HTML Elements
- Paired Elements
- Void Elements
- Custom Elements
- HTML Attributes
- Categories of HTML Attributes
- Global Attributes
- Boolean Attributes
- Data Attributes
- ARIA Attributes
- Event Handler Attributes
- Element-Specific Attributes
- Practical Example: Putting It All Together
- Common Mistakes
- Complete Recap
HTML Tags
Definition: A tag is a piece of markup notation, written inside angle brackets, that marks the beginning or end of a section of content and tells the browser what role that content plays.
In simple language: think of a tag as a label. It doesn't do anything by itself — it just marks "this is a heading" or "this is a paragraph" or "this is a link." The browser reads that label and decides how to treat the content next to it.
How it works: tags come in two forms — opening tags like <p> and closing tags like </p>. Some tags (void elements, covered later) don't have a closing form at all, because they don't wrap around anything.
Syntax:
<tagname>content</tagname>
Example:
<h1>Hello World</h1>
<p>This is a paragraph.</p>
Breakdown:
-
<h1>is the opening tag,</h1>is the closing tag — together they mark a top-level heading. -
<p>and</p>do the same for a paragraph. - The tag name itself (
h1,p) tells the browser what the content means semantically, not just how to display it.
Rules:
- Tag names are not case-sensitive in HTML (
<P>and<p>behave the same), but lowercase is the universal convention and what you should always use. - A tag must be properly nested — you can't close an outer tag before an inner one.
Common mistake: writing mismatched or overlapping tags, like <p><strong>text</p></strong>. This is invalid nesting and can cause browsers to render the DOM differently than you expect.
HTML Elements
Definition: An element is the complete structure formed by a tag (or a pair of tags), its content, and any attributes attached to it.
In simple language: if a tag is a label, an element is the whole labeled package — the label, what's inside it, and any extra details attached to the label.
How it works: when the browser parses your HTML, it doesn't build a tree of tags — it builds a tree of elements. Each node in the DOM (Document Object Model) is an element object, with properties and methods you can access through JavaScript.
Syntax:
<tagname attribute="value">content</tagname>
Example:
<a href="https://amolcodes.dev">Visit my site</a>
Breakdown:
-
<a>and</a>are tags. -
href="https://amolcodes.dev"is an attribute providing extra information. -
<a href="https://amolcodes.dev">Visit my site</a>— the whole thing — is the element.
Rules: every element has a corresponding representation in the DOM once the page loads, accessible via JavaScript methods like document.querySelector().
Tag vs Element — The Core Difference
This distinction is small in wording but important in practice, especially once you start reading the HTML specification, MDN documentation, or debugging DOM behavior.
<p>Hello World</p>
-
<p>— a tag -
</p>— also a tag -
<p>Hello World</p>— the element
Why it matters: when documentation says "the <img> tag," it usually means the markup notation. When it says "the image element" or refers to HTMLImageElement, it means the actual object in the DOM, which has properties, methods, and event handlers attached to it. In casual conversation, "tag" and "element" get used interchangeably, and that's generally fine — but knowing the precise difference helps when you're reading specs or working with the DOM API directly.
Predefined HTML Elements
Definition: Predefined HTML elements are the elements that are part of the official HTML specification — recognized and rendered consistently by every standards-compliant browser without any extra setup.
In simple language: these are the "built-in" tags. You don't need to write any JavaScript or configuration to use them — the browser already knows what <div>, <button>, or <input> mean and how to render them.
How it works: browser vendors implement the HTML specification, which defines the full list of standard elements, their allowed attributes, default styling, and default behavior. This is why an <a> element is clickable and navigable by default, or why a <button> is focusable and triggers form submission inside a <form> without any extra code.
Example:
<section>
<h2>About</h2>
<p>This section uses only predefined HTML elements.</p>
<button>Learn More</button>
</section>
Breakdown: <section>, <h2>, <p>, and <button> are all predefined elements. None of them require registration or extra setup — they work the moment you write them.
Predefined elements split into two structural groups based on how they're written in markup: paired elements and void elements.
Paired Elements
Definition: Paired elements are elements that require both an opening tag and a closing tag, with content (text or other elements) nested between them.
In simple language: these are "container" tags — they wrap around something.
How it works: the browser starts applying the element's meaning at the opening tag and stops at the matching closing tag. Everything between belongs to that element.
Syntax:
<tagname>content or nested elements</tagname>
Example:
<div>
<h2>Section Title</h2>
<p>Some content inside a div.</p>
</div>
Breakdown:
-
<div>opens a container. -
<h2>and<p>are nested paired elements inside it. -
</div>closes the container, meaning everything above belongs inside it.
Rules:
- Every opening tag needs a matching closing tag.
- Nesting must be properly closed in the reverse order it was opened (last opened, first closed).
Common mistake: forgetting a closing tag. HTML parsers are forgiving and will often still render something, but the resulting DOM structure can end up different from what you intended — which shows up later as unexplained layout bugs.
Void Elements
Definition: Void elements are elements that cannot contain any content and therefore have no closing tag. This is the technically correct term — you'll also hear them called "self-closing" or "unpaired" tags informally, but "void element" is the term used in the HTML specification.
In simple language: these tags do their job entirely through their attributes, because there's no content to wrap.
How it works: the browser parses a void element as complete the moment it hits the closing angle bracket — there's no expectation of a closing tag following it.
Syntax:
<tagname attribute="value">
Example:
<img src="photo.jpg" alt="A photo">
<br>
<hr>
<input type="text">
<meta charset="UTF-8">
<link rel="stylesheet" href="styles.css">
Breakdown:
-
<img>embeds an image — all its information (src,alt) comes through attributes. -
<br>inserts a line break — it needs no content or attributes at all. -
<input>defines a form control — again, entirely attribute-driven.
Rules:
- Void elements cannot have a closing tag like
</img>— this is invalid HTML. - Writing a trailing slash, like
<img />, is allowed in HTML5 but entirely optional. It's a stylistic habit carried over from XHTML, and browsers treat<img>and<img />identically.
Common mistake: trying to nest content inside a void element, like <input>some text</input>. This is invalid — void elements simply cannot have children.
Custom Elements
Definition: Custom Elements are user-defined HTML elements that are formally registered through the Custom Elements API, part of the broader Web Components standard.
In simple language: it's tempting to think any strange, hyphenated tag you don't recognize — like <user-card> — is automatically a "custom element." That's not accurate. A tag only becomes a real, functioning custom element once JavaScript explicitly registers it.
How it works: you define a JavaScript class that extends HTMLElement, then register it with customElements.define(), associating a tag name with that class. Once registered, the browser treats instances of that tag as fully functional elements with their own internal logic, lifecycle callbacks, and rendering behavior.
Syntax:
class ClassName extends HTMLElement {
connectedCallback() {
// runs when the element is added to the DOM
}
}
customElements.define('tag-name', ClassName);
Example:
<user-card></user-card>
class UserCard extends HTMLElement {
connectedCallback() {
this.innerHTML = `<p>Custom card component</p>`;
}
}
customElements.define('user-card', UserCard);
Breakdown:
-
UserCardis a JavaScript class extendingHTMLElement. -
connectedCallback()is a lifecycle method that runs automatically when the element is inserted into the DOM. -
customElements.define('user-card', UserCard)registers the tag nameuser-cardand links it to the class. - Only after this registration does
<user-card></user-card>behave like a real element with actual behavior.
Rules:
- A custom element name must contain a hyphen (e.g.,
user-card, notusercard). This is a strict requirement in the specification, and it exists so the browser can always distinguish custom elements from any new predefined elements that might be added to HTML in the future. - Custom element names cannot be a single word.
Common mistake: assuming that any unfamiliar or hyphenated-looking tag in someone else's code is automatically a "custom element" without being registered. If it isn't registered through customElements.define(), the browser just treats it as a generic, unstyled inline element — it silently does nothing special.
HTML Attributes
Definition: Attributes are name/value pairs written inside an element's opening tag that provide additional information, configuration, or behavior for that element.
In simple language: if the element defines what something is, the attribute defines the details — extra information that the tag name alone can't express.
How it works: the browser reads each attribute on an element and applies its effect accordingly — some attributes affect rendering, some affect behavior, and some exist purely as metadata for scripts to use later.
Syntax:
<tagname attribute="value">content</tagname>
Example:
<a href="https://amolcodes.dev" target="_blank">Visit my site</a>
Breakdown:
-
hreftells the browser where the link points. -
target="_blank"tells the browser to open the link in a new tab. - Both are attributes of the
<a>element, and both change how the browser handles it.
Rules:
- Attribute values are typically wrapped in double quotes (single quotes also work, but double quotes are the convention).
- An element can have multiple attributes, separated by spaces, in any order.
Categories of HTML Attributes
HTML doesn't define one single official classification called "types of attributes" — but for learning purposes, grouping attributes into categories makes them dramatically easier to understand and remember. The categories below are the ones you'll run into constantly in real-world code.
Global Attributes
Definition: Global attributes are attributes that can be applied to virtually any HTML element, regardless of what that element is.
In simple language: these aren't tied to any one element's specific job — they add general capabilities that apply everywhere.
Syntax and example:
<div id="main-content" class="container" title="Main section" lang="en" style="color: blue;"></div>
Breakdown:
-
id— a unique identifier for the element, used for CSS targeting, JavaScript selection, or anchor links. -
class— one or more class names, most commonly used for CSS styling and JS querying. -
title— advisory text, often displayed as a tooltip on hover. -
lang— declares the language of the element's content, useful for accessibility and screen readers. -
style— inline CSS applied directly to that one element.
Rules: because global attributes work almost everywhere, they're usually the first attributes beginners learn, and they remain some of the most frequently used attributes in any codebase.
Common mistake: using id for styling purposes the way you'd use class. IDs must be unique per page, so reusing the same id on multiple elements is invalid and can break JavaScript that relies on getElementById().
Boolean Attributes
Definition: Boolean attributes represent an on/off state. Their mere presence on an element means the value is "true" — no value assignment is needed.
In simple language: you don't write checked="true" — you just write checked. If it's there, it's on. If it's not there, it's off.
Syntax and example:
<input type="checkbox" checked>
<button disabled>Submit</button>
<video controls autoplay muted></video>
Breakdown:
-
checked— the checkbox starts checked. -
disabled— the button is disabled and can't be clicked or focused. -
controls,autoplay,muted— control playback behavior of the<video>element.
Rules: the presence of the attribute is what matters, not any value assigned to it. Technically, disabled="false" still evaluates to disabled being true, because the attribute exists — the string "false" is just an arbitrary value, not a JavaScript boolean.
Common mistake: writing disabled="false" expecting the element to become enabled. It won't — to enable it, you have to remove the attribute entirely, not set it to a falsy-looking string.
Data Attributes
Definition: Data attributes let you store custom, application-specific data directly on an HTML element, using attribute names that start with data-.
In simple language: they're a standardized way to attach your own extra information to an element without inventing non-standard attribute names that could conflict with future HTML additions.
Syntax:
<tagname data-custom-name="value"></tagname>
Example:
<div data-user-id="4521" data-role="admin">Amol</div>
const el = document.querySelector('div');
console.log(el.dataset.userId); // "4521"
console.log(el.dataset.role); // "admin"
Breakdown:
-
data-user-idanddata-roleare custom data attributes. - In JavaScript, they're accessed through the
datasetproperty. - Hyphenated attribute names automatically convert to camelCase property names —
data-user-idbecomesdataset.userId.
Rules: data attribute names must start with data-, contain only lowercase letters, numbers, hyphens, periods, colons, or underscores after that prefix, and must not contain uppercase letters.
Common mistake: using camelCase directly in the HTML attribute, like data-userId. HTML attributes are case-insensitive, so this doesn't map cleanly to dataset.userId the way you'd expect — always write the HTML attribute itself in lowercase with hyphens.
ARIA Attributes
Definition: ARIA (Accessible Rich Internet Applications) attributes provide additional semantic information to assistive technologies, like screen readers, especially for elements whose purpose or state isn't obvious from standard HTML alone.
In simple language: they help people using screen readers or other assistive tools understand what a custom or complex UI component is doing.
Syntax and example:
<button aria-label="Close menu" aria-expanded="false">✕</button>
<div role="alert" aria-live="polite">
Your changes have been saved.
</div>
Breakdown:
-
aria-label— supplies an accessible name when there isn't enough visible text to describe the element. -
aria-expanded— tells assistive tech whether a collapsible section is currently open. -
role— defines what kind of widget the element represents (likealert,dialog, ornavigation). -
aria-live="polite"— tells screen readers to announce content changes without interrupting the user.
Rules: ARIA attributes should supplement semantic HTML, not replace it. The first rule of ARIA is often summarized as: if a native HTML element or attribute already provides the behavior or semantics you need, use that instead of adding ARIA.
Common mistake: using a <div> styled to look like a button, with role="button" and an onclick handler, instead of just using a real <button> element — which already gets keyboard focus, keyboard activation (Enter/Space), and correct semantics for free.
Event Handler Attributes
Definition: Event handler attributes attach JavaScript code directly to an element, to run when a specific event occurs (like a click or a form submission).
In simple language: they let you wire up behavior right inside the HTML markup, instead of writing separate JavaScript to attach it.
Syntax and example:
<button onclick="alert('Clicked!')">Click Me</button>
Breakdown: onclick="alert('Clicked!')" tells the browser to run that JavaScript expression whenever the button is clicked.
Rules: attributes like onclick, onchange, onsubmit, onmouseover, and similar ones exist for most standard DOM events.
Common mistake and recommended practice: inline event handler attributes work, and you'll see them a lot in quick tutorials, but they mix structure and behavior in the same line of markup. In most real-world projects, it's considered better practice to use addEventListener() in a separate JavaScript file:
document.querySelector('button').addEventListener('click', () => {
alert('Clicked!');
});
This keeps your HTML focused on structure and your JavaScript focused on behavior, which scales much better as a project grows.
Element-Specific Attributes
Definition: Element-specific attributes are attributes that are only valid — or only meaningful — on particular elements, because they configure something unique to that element's purpose.
In simple language: these aren't universal like global attributes; they only make sense in context.
Syntax and example:
<img src="logo.png" alt="Company logo">
<a href="https://amolcodes.dev">My site</a>
<input type="email" placeholder="you@example.com">
<label for="email">Email</label>
Breakdown:
-
srcandalt— only meaningful on elements like<img>(andsrcon a few others, like<script>or<video>). -
href— specific to<a>and<link>. -
typeandplaceholder— specific to<input>(typealso applies to a couple of other elements, but with different meaning). -
for— specific to<label>, linking it to a form control by matchingid.
Rules: there's no single master list to memorize — each predefined element has its own documented set of valid attributes, which you'll find in the HTML specification or on MDN.
Common mistake: using an attribute on the wrong element, like adding href to a <button> expecting it to navigate. href simply isn't recognized on <button>, so it's silently ignored.
Practical Example: Putting It All Together
Here's a small, complete signup form that combines nearly every concept covered above.
<form id="signup-form" class="form">
<label for="email">Email</label>
<input type="email" id="email" placeholder="you@example.com" required>
<button type="submit" data-action="signup" disabled>
Sign Up
</button>
<img src="check.svg" alt="Success checkmark" hidden>
</form>
Full breakdown:
-
<form>,<label>,<button>— paired elements, each requiring opening and closing tags. -
<input>,<img>— void elements, since neither wraps content. -
id,class— global attributes, usable on any of these elements. -
required,disabled,hidden— boolean attributes; their presence alone activates their effect. -
data-action="signup"— a data attribute, readable later viadataset.actionin JavaScript. -
type,placeholder,src,alt,for— element-specific attributes, each valid only on their respective elements.
Seeing all of these categories working together in one small example makes it far easier to recognize each one in real-world code, rather than as isolated definitions in a list.
Common Mistakes
A consolidated list of the mistakes covered throughout this article, all in one place:
- Assuming every unfamiliar or hyphenated tag is a "custom element." Only tags explicitly registered through the Custom Elements API qualify.
- Mismatched or improperly nested tags. Closing tags out of order can lead to unpredictable DOM structures.
- Forgetting closing tags on paired elements. The page may still render something, but the underlying DOM structure can end up broken.
-
Adding a closing tag to a void element, like
</img>— void elements never have one. -
Writing
disabled="false"expecting an element to become enabled. Presence alone equals true; removal is the only way to turn it off. -
Using camelCase directly in a
data-attribute name instead of lowercase-with-hyphens. - Reaching for ARIA and custom
<div>-based widgets before checking whether a native semantic element already does the job. -
Overusing inline event handler attributes instead of separating behavior into JavaScript with
addEventListener(). - Using an attribute on an element it isn't defined for, expecting it to work anyway.
Complete Recap
- A tag is the bracket notation itself; an element is the tag plus its content and attributes.
- Predefined elements are part of the HTML standard and work without any setup.
- Paired elements require opening and closing tags and can contain content; void elements cannot contain content and have no closing tag.
- Custom elements are only real once registered through the Custom Elements API, and their names must contain a hyphen.
- Attributes add configuration and metadata to elements, and while HTML has no single official "types of attributes" classification, grouping them into global, boolean, data, ARIA, event handler, and element-specific categories makes them much easier to learn and apply.
- Real HTML almost always combines several of these categories in the same piece of markup — which is exactly what the practical example above demonstrated.
This article is part of my ongoing HTML documentation series under AmolCodes. The next piece in the series will build directly on these fundamentals.












Top comments (0)