Web Components introductory example tutorial

Web Components introductory example tutorial

Components are the development direction of the front-end. Now the popular React and Vue are both component frameworks.

Google has been pushing the browser’s native components, the Web Components API , since it mastered the Chrome browser . Compared with third-party frameworks, native components are simple, straightforward, intuitive, do not need to load any external modules, and have a small amount of code. Currently, it is still in development, but is already available for production environments.

There is a lot of content in the Web Components API. This article is not a comprehensive tutorial, but a simple demonstration. Let everyone see how to use it to develop components.

custom element

The image below is a user card.

This article demonstrates how to write this card as a Web Components component, here is the final complete code .

Simply insert the following code into the web page and the user card will be displayed.


<user-card></user-card>


This custom HTML tag is called a custom element. According to the specification, the name of a custom element must contain a hyphen to distinguish it from a native HTML element. Therefore, it <user-card>cannot be written <usercard>.

customElements.define()

Custom elements need to define a class using JavaScript, and all <user-card>will be instances of this class.


class UserCard extends HTMLElement {
  constructor() {
    super();
  }
}


In the above code, it UserCardis the class of the custom element. Note that the parent class of this class is HTMLElement, and thus inherits the properties of the HTML element.

Next, using the browser’s native customElements.define()methods, tell the browser that <user-card>the element is associated with this class.


window.customElements.define('user-card', UserCard);


Contents of custom elements

The custom element <user-card>is currently empty, and the content of this element is given in the class below.


class UserCard extends HTMLElement {
  constructor() {
    super();

    var image = document.createElement('img');
    image.src = 'https://semantic-ui.com/images/avatar2/large/kristy.png';
    image.classList.add('image');

    var container = document.createElement('div');
    container.classList.add('container');

    var name = document.createElement('p');
    name.classList.add('name');
    name.innerText = 'User Name';

    var email = document.createElement('p');
    email.classList.add('email');
    email.innerText = '[email protected]';

    var button = document.createElement('button');
    button.classList.add('button');
    button.innerText = 'Follow';

    container.append(name, email, button);
    this.append(image, container);
  }
}


In the last line of this.append()the code above, , thisrepresents a custom element instance.

After this step, the DOM structure inside the custom element has been generated.

<template>Label

Writing the DOM structure from the previous section in JavaScript was cumbersome, and the Web Components API provides <template>tags in which the DOM can be defined using HTML.


<template id="userCardTemplate">
  <img src="https://semantic-ui.com/images/avatar2/large/kristy.png" class="image">
  <div class="container">
    <p class="name">User Name</p>
    <p class="email">[email protected]</p>
    <button class="button">Follow</button>
  </div>
</template>


Then, rewrite the class of the custom element to load for the custom element <template>.


class UserCard extends HTMLElement {
  constructor() {
    super();

    var templateElem = document.getElementById('userCardTemplate');
    var content = templateElem.content.cloneNode(true);
    this.appendChild(content);
  }
}  


In the above code, <template>after the node is obtained, all its child elements are cloned. This is because there may be multiple instances of custom elements, and this template is reserved for other instances, so its child elements cannot be moved directly.

So far, the complete code is as follows.


<body>
  <user-card></user-card>
  <template>...</template>

  <script>
    class UserCard extends HTMLElement {
      constructor() {
        super();

        var templateElem = document.getElementById('userCardTemplate');
        var content = templateElem.content.cloneNode(true);
        this.appendChild(content);
      }
    }
    window.customElements.define('user-card', UserCard);    
  </script>
</body>


add style

A custom element does not yet have a style, you can assign a global style to it, such as the following.


user-card {
  /* ... */
}


However, the style of the component should be encapsulated with the code, which only takes effect on the custom element and does not affect the external global style. So, you can write styles in <template>it.


<template id="userCardTemplate">
  <style>
   :host {
     display: flex;
     align-items: center;
     width: 450px;
     height: 180px;
     background-color: #d4d4d4;
     border: 1px solid #d5d5d5;
     box-shadow: 1px 1px 5px rgba(0, 0, 0, 0.1);
     border-radius: 3px;
     overflow: hidden;
     padding: 10px;
     box-sizing: border-box;
     font-family: 'Poppins', sans-serif;
   }
   .image {
     flex: 0 0 auto;
     width: 160px;
     height: 160px;
     vertical-align: middle;
     border-radius: 5px;
   }
   .container {
     box-sizing: border-box;
     padding: 20px;
     height: 160px;
   }
   .container .name {
     font-size: 20px;
     font-weight: 600;
     line-height: 1;
     margin: 0;
     margin-bottom: 5px;
   }
   .container .email {
     font-size: 12px;
     opacity: 0.75;
     line-height: 1;
     margin: 0;
     margin-bottom: 15px;
   }
   .container .button {
     padding: 10px 25px;
     font-size: 12px;
     border-radius: 5px;
     text-transform: uppercase;
   }
  </style>

  <img src="https://semantic-ui.com/images/avatar2/large/kristy.png" class="image">
  <div class="container">
    <p class="name">User Name</p>
    <p class="email">[email protected]</p>
    <button class="button">Follow</button>
  </div>
</template>


In the above code, <template>the pseudo-class in the style :hostrefers to the custom element itself.

Parameters for custom elements

<user-card>The content is now <template>set in it. For convenience, change it to a parameter.


<user-card
  image="https://semantic-ui.com/images/avatar2/large/kristy.png"
  name="User Name"
  email="[email protected]"
></user-card>


<template>The code is also modified accordingly.


<template id="userCardTemplate">
  <style>...</style>

  <img class="image">
  <div class="container">
    <p class="name"></p>
    <p class="email"></p>
    <button class="button">Follow John</button>
  </div>
</template>


Finally, change the code of the class and add the parameter to the custom element.


class UserCard extends HTMLElement {
  constructor() {
    super();

    var templateElem = document.getElementById('userCardTemplate');
    var content = templateElem.content.cloneNode(true);
    content.querySelector('img').setAttribute('src', this.getAttribute('image'));
    content.querySelector('.container>.name').innerText = this.getAttribute('name');
    content.querySelector('.container>.email').innerText = this.getAttribute('email');
    this.appendChild(content);
  }
}
window.customElements.define('user-card', UserCard);    


Shadow DOM

We don’t want users to be able to see <user-card>the internal code, Web Component allows the internal code to be hidden, which is called Shadow DOM, that is, this part of the DOM is isolated from the external DOM by default, and any internal code cannot affect the external.

The method of custom element this.attachShadow()opens Shadow DOM, see the code below for details.


class UserCard extends HTMLElement {
  constructor() {
    super();
    var shadow = this.attachShadow( { mode: 'closed' } );

    var templateElem = document.getElementById('userCardTemplate');
    var content = templateElem.content.cloneNode(true);
    content.querySelector('img').setAttribute('src', this.getAttribute('image'));
    content.querySelector('.container>.name').innerText = this.getAttribute('name');
    content.querySelector('.container>.email').innerText = this.getAttribute('email');

    shadow.appendChild(content);
  }
}
window.customElements.define('user-card', UserCard);


In the above code, this.attachShadow()the parameters { mode: 'closed' }of the method indicate that the Shadow DOM is closed and does not allow external access.

At this point, the Web Component component is complete, and the complete code can be accessed here . As you can see, the whole process is still very simple, unlike third-party frameworks that have complex APIs.

extension of components

On the previous basis, components can be extended.

(1) Interact with users

The user card is a static component. If you want to interact with the user, it is very simple to listen to various events in the class.


this.$button = shadow.querySelector('button');
this.$button.addEventListener('click', () ={
  // do something
});


(2) Package of components

In the above example, <template>together with the web page code, it can actually be <template>injected into the web page with a script. In this way, the JavaScript script <template>can be encapsulated into a JS file and become an independent component file. As long as the web page loads this script, the <user-card>component can be used.

It will not be expanded here. For more advanced usage of Web Components, you can learn the following two articles.

Reference link