In this blog post we’re going to explore differences between an HTML class
and id
attribute! From how this affects browser behaviour all the way through to styling tips with CSS.
First we’ll explore the id
attribute and how we can use it, as well as when to use it. This answers many questions newcomers have such as “Should I use a class or id in HTML?” and “Should I style the class or id in CSS?”. Let’s find out!
When to use an id
If you’re new to HTML, an id
is a unique identity you can give to an HTML element. We can use as many id
attributes as we like, however there are some sensible rules:
- One
id
per element - Multiple
id
attributes cannot be the same value (it’s invalid HTML to have duplicate ids)
Anchors and hyperlinking an id
An id
creates special behaviour, you’ll find them in this very blog! Each heading (such as h3
we use so that we can include the id
value as part of a URL, which will automatically scroll the user to that part of the web page upon loading.
Imagine a blog post which includes a title “All About JavaScript Closures”, the generated blog heading could look like so:
<h2 id="all-about-javascript-closures">
All About JavaScript Closures
</h2>
Our URL could then automatically link to that part of the document via the URL:
someblog.com/javascript-closures#all-about-javascript-closures
Styling ids
You can style an id
via a CSS selector, interestingly enough we use the same syntax as in the URL with a #
:
#all-about-javascript-closures {
background: red;
}
Just because you can, doesn’t mean you should. I’d recommend avoiding styling the id
as it’s considered unique. Unless there’s a specific hack you’re planning on making, I’d avoid styling your IDs as you’re only limiting your CSS reuse and subsequently bloating the stylesheet.
Free eBook
Directives, simple right? Wrong! On the outside they look simple, but even skilled Angular devs haven’t grasped every concept in this eBook.
- Observables and Async Pipe
- Identity Checking and Performance
- Web Components <ng-template> syntax
- <ng-container> and Observable Composition
- Advanced Rendering Patterns
- Setters and Getters for Styles and Class Bindings
Selecting an id with JavaScript
Interestingly enough, any id
properties become JavaScript properties on the global window
object.
So if we did this, we would in fact create a global variable with the same name:
<aside id="author">...</aside>
<script>
window.author; // <aside id="author"></aside>
</script>
But global variables aren’t great or reliable, so really we should avoid them when architecting our HTML.
It’s most likely that with your JavaScript you’re better off selecting and changing a class
- but should you need it, you know the id
is there.
Now there is also a classList
API which was created directly for working with your class
attribute, so there is no need to use an id
in reality.
When to use a class
A class
is our preferred way of identifying elements.
Once we’ve bound a class
to an element, it becomes targetable through CSS and JavaScript.
Styling a class
We should use a class for styling purposes and optionally reflecting state onto the DOM tree (i.e. for adding behaviour or simply a static style).
The DOM tree is a live representation of our HTML page and is what we use with JavaScript. For example,
document.querySelector
is a DOM API and not “JavaScript”.
Classes enable us to specify a description for a shared style, whilst providing some meaning behind the element:
<button class="btn">Small button</button>
<button class="btn">Medium button</button>
<button class="btn">Large button</button>
We could then use .btn { background: blue }
in our stylesheet to then provide a single style rule to all of these elements! Unlike an id
, we can declare a class
as many times as we want - to become as efficient and reusable as we can.
Classes are also composable, and we can specify multiple classes per element, giving us the ability to not only inherit a single shared style but also provide an adaptation, such as small, medium and large as below:
<button class="btn btn--small">Small button</button>
<button class="btn btn--medium">Medium button</button>
<button class="btn btn--large">Large button</button>
It could be that we expect use the BEM Methodology and supply a modifier class, denoted with a --suffix
:
.btn--small { font-size: 12px }
.btn--medium { font-size: 14px }
.btn--large { font-size: 16px }
In this case, the --small
would describe a modified version from the base of .btn
. It’s been my preferred CSS methodology for many years.
🕵️♀️ With the switch to component-based architecture with web applications, I favour as few styles as possible in my
style.css
and would look to supply a style per component. This is a better component architecture pattern as it also provides full encapsulation.
With this in mind, we should also use a class
to dynamically change values that relate to styling. For instance, we could have an 'active'
class that needs adding or removing based on a certain condition. How do we do that?
Free eBook
Directives, simple right? Wrong! On the outside they look simple, but even skilled Angular devs haven’t grasped every concept in this eBook.
- Observables and Async Pipe
- Identity Checking and Performance
- Web Components <ng-template> syntax
- <ng-container> and Observable Composition
- Advanced Rendering Patterns
- Setters and Getters for Styles and Class Bindings
Using the classList API
There happens to be a built-in new-ish API, that will require a polyfill for older browsers, called classList
which is available to us on every Element
DOM Node.
When we log .classList
our on an element, we are returned a DOMTokenList
for us to work with.
<button class="btn btn--small">Small button</button>
<script>
const button = document.querySelector('.btn .btn--small');
// ▶ DOMTokenList(2) ["btn", "btn--small", value: "btn btn--small"]
console.log(button.classList);
</script>
You can see classList
contains a value
property that reflects the current class
string value.
It also looks like an array, as ["btn", "btn--small"]
look like array elements. It’s almost an array, but because it’s providing value
and other classList
behaviours in the prototype
- it’s array-like with extra behaviours. If you need to use the values as an array you could use Array.from(button.classList)
.
The classList
feature also ships with some great prototype
methods to add/remove/toggle and more with a class
:
<button class="btn btn--small">Small button</button>
<script>
const button = document.querySelector('.btn .btn--small');
// add + remove a class
button.classList.add('btn--large');
button.classList.remove('btn--small');
// toggle class on a click event
button.addEventListener('click', () => button.classList.toggle('btn--clicked'));
</script>
There’s also much more in the classList documentation I’d encourage you to check out as well.
Summary
We’ve covered a whole lot of ground in this article! From a simple HTML element id
or class
through to how it affects our global variables in JavaScript and finally exploring APIs such as classList
- and how they fit into the ecosystem.
Summing up, here’s a recap of what we’ve learned:
- Use a unique
id
once per page - Use one
id
per element - Avoid styling
id
elements, prefer aclass
- An
id
attribute also creates a global JavaScript property onwindow
- Use a
class
for styling purposes and maximise composable classes - Use as many classes as you like per element, but don’t go overboard
- Use the built-in
classList
API to manipulate classes - If you need to support older browsers, check out the Can I Use ClassList page which will highlight current support and whether you need to polyfill
- With component architecture and modern web practices, an
id
could be considered a thing of the past unless we want to use some of its features
If you are serious about your HTML and CSS skills, your next step is to take a look at our HTML + CSS Basics course that will teach you the full language basics in detail as well as many use cases you’ll need in daily front-end development!
Thanks for reading!