Skip to Content
Course content

207: Custom Elements Basics

Click on the "Edit" button in the top corner of the screen to edit your slide content.

I've been building this dashboard project, and I noticed I'm repeating the same HTML structure over and over for user profile badges. You know the drill: a div, an image, a name, and a role. Every time I need one, I copy-paste a chunk of HTML and hope I don't mess up a class name. It feels clunky. I want a way to just write <user-badge> and have the browser handle the rest.

The "Wait, it's just a div" problem

My first instinct was to just start using a custom tag. I figured the browser might be smart enough to know what I mean. I tried this in my HTML:

<user-badge name="Alex" role="Lead Dev"></user-badge>

I refreshed the page and... nothing. Well, not "nothing," but the browser just treated it as a generic inline element. If I inspected it in the DevTools, it was there, but it had no styling and no logic. It's essentially just a <span> with a fancy name. The browser doesn't magically know how to render a <user-badge> because that tag isn't in the HTML specification. We have to define the behavior ourselves.

Giving the tag a brain

To make this work, I need a JavaScript class that tells the browser, "Hey, whenever you see this tag, use this logic." I started by extending the base HTMLElement class. I'll try to hook into the lifecycle of the element—specifically when it's actually added to the page.

class UserBadge extends HTMLElement {
  connectedCallback() {
    this.innerHTML = `<div class="badge">User Profile</div>`;
  }
}

I added this to my script, refreshed, and... still nothing. I forgot the most important part: the registration. Creating the class is one thing, but the browser needs a map that links the string 'user-badge' to the UserBadge class. I added this line at the bottom:

customElements.define('user-badge', UserBadge);

Now, when I refresh, the text "User Profile" finally appears on the screen. It's a start, but it's static. Every single badge says the same thing, which defeats the purpose of having a name attribute in my HTML.

Making it dynamic with attributes

I want the badge to actually use the data I passed into the tag. Since I'm inside the class, this refers to the element itself. I can use getAttribute()` to pull those values out. Let's refine the connectedCallback.

class UserBadge extends HTMLElement {
  connectedCallback() {
    const name = this.getAttribute('name') || 'Unknown User';
    const role = this.getAttribute('role') || 'Guest';
    
    this.innerHTML = `
      <div class="badge-container">
        <strong>${name}</strong>
        <small>${role}</small>
      </div>
    `;
  }
}
customElements.define('user-badge', UserBadge);

Now, when I use <user-badge name="Alex" role="Lead Dev"></user-badge>, it actually renders "Alex" and "Lead Dev". This is the core of Custom Elements. I've essentially created my own HTML tag that encapsulates its own structure and data-handling logic. I can now drop this tag anywhere in my app, and as long as the script is loaded, it'll just work.

One thing to keep in mind: custom element names must contain a hyphen. If I had tried to call it <userbadge> (no hyphen), the browser would have thrown an error. This is a safety feature to ensure that custom elements never clash with future standard HTML tags that might be introduced to the language.




📋 Practical Task

Build a Custom Product Price Tag

Create a custom element called <product-price>. This element should take two attributes: price and currency.

  • Define a class that extends HTMLElement.
  • Use the connectedCallback to set the innerHTML of the element.
  • The output should be formatted as: Price: [currency][price] (e.g., Price: $99).
  • Register the element using customElements.define.
  • Test it in your HTML by adding: <product-price price="49.99" currency="€"></product-price>.
Rating
0 0

There are no comments for now.

to be the first to leave a comment.