A Getting Started Guide to Web Components

A Getting Started Guide to Web Components

[[383293]]

Today's front-end development is basically inseparable from the support of the two frameworks of React and Vue, and many custom component libraries have been derived from these two frameworks:

  • Element(Vue)
  • Ant Design(React)

The emergence of these component libraries allows us to directly use the packaged components, and with the help of the open source community, many template projects (vue-element-admin, Ant Design Pro) have emerged, allowing us to quickly start a project.

Although React and Vue provide convenience for our component development, the two have different ideas for component development. One is the self-created JSX syntax, and the other is the unique single-file template syntax. Both aim to provide a component encapsulation method. After all, they both have their own original things in them, which are somewhat different from the basic HTML, CSS, and JS methods of the Web that we just came into contact with. Today, we will introduce how to implement custom components through HTML, CSS, and JS, which is also the solution currently provided by the native browser: Web Components.

What are Web Components?

Web Components itself is not a separate specification, but consists of a set of DOM APIs and HTML specifications for creating reusable HTML tags with custom names that can be used directly in your web applications.

Code reuse has always been our goal. In JS, we can encapsulate reusable code into a function, but for complex HTML (including related styles and interactive logic), we have never had a better way to reuse it. Either we rely on the backend template engine or the existing framework to encapsulate the DOM API twice. The emergence of Web Components is to complement the browser's capabilities in this regard.

How to use Web Components?

Several specifications included in Web Components have been standardized in W3C and HTML standards and are mainly composed of three parts:

  • Custom elements: A set of JavaScript APIs for creating custom HTML tags and allowing some operations to be performed when the tags are created or destroyed;
  • Shadow DOM: A set of JavaScript APIs for inserting a created DOM Tree into an existing element. The DOM Tree cannot be modified externally, so there is no need to worry about the element being affected by other places.
  • HTML templates: Write templates directly in HTML files through <template> and <slot>, and then obtain them through DOM API.

Custom elements

The browser provides a method: customElements.define() to define custom tags. This method accepts three parameters:

  • The name of the custom element, a DOMString standard string. To prevent conflicts between custom elements, it must be a name connected by a hyphen (eg custom-tag).
  • Define some behaviors of custom elements, similar to the life cycle in React and Vue.
  • Extension parameter (optional), the parameter type is an object and needs to contain the extends attribute, which is used to specify which built-in element the created element inherits from (eg { extends: 'p' }).

The following are some examples to demonstrate its usage, and the complete code is on JS Bin.

Create a new HTML tag

Let's first look at how to create a brand new custom element.

  1. class HelloUser extends HTMLElement {
  2. constructor() {
  3. //Must call super method
  4. super();
  5.  
  6. // Create a div tag
  7. const $box = document.createElement( "p" );
  8. let userName = "User Name" ;
  9. if (this.hasAttribute( "name" )) {
  10. // If the name attribute exists, read the value of the name attribute
  11. userName = this.getAttribute( "name" );
  12. }
  13. // Set the text content of the div tag
  14. $box.innerText = `Hello ${userName}`;
  15.  
  16. // Create a shadow node, other elements created should be attached to this node
  17. const shadow = this.attachShadow({ mode: "open" });
  18. shadow.appendChild($box);
  19. }
  20. }
  21.  
  22. // Define an element named <hello- user />
  23. customElements.define( "hello-user" , HelloUser);

  1. <hello- user   name = "Shenfq" ></hello- user >

At this time, a <p> tag will be generated on the page, and its text content is: Hello Shenfq. This form of custom element is called: Autonomous custom elements, which is an independent element and can be used directly in HTML.

Extending existing HTML tags

In addition to defining a new HTML tag, we can also extend the existing HTML tags. For example, if we need to encapsulate a component with similar capabilities to the <ul> tag, we can use the following method:

  1. class SkillList extends HTMLUListElement {
  2. constructor() {
  3. //Must call super method
  4. super();
  5.  
  6. if (
  7. this.hasAttribute( "skills" ) &&
  8. this.getAttribute( "skills" ).includes( ',' )
  9. ) {
  10. // Read the value of the skills attribute
  11. const skills = this.getAttribute( "skills" ).split( ',' );
  12. skills.forEach(skill => {
  13. const item = document.createElement( "li" );
  14. item.innerText = skill;
  15. this.appendChild(item);
  16. })
  17. }
  18. }
  19. }
  20.  
  21. // Expand the <ul> tag
  22. customElements.define( "skill-list" , SkillList, { extends: "ul" });

  1. <ul is = "skill-list" skills= "js,css,html" ></ul>

To extend an existing tag, you need to use the third parameter of the customElements.define method, and the class of the second parameter also needs to inherit the corresponding class of the tag to be extended. When using it, you only need to add the is attribute to the tag, and the attribute value is the name defined by the first parameter.

life cycle

The life cycle of a custom element is relatively simple, providing only four callback methods:

  • connectedCallback: Called when the custom element is inserted into the page's DOM document.
  • disconnectedCallback: Called when the custom element is removed from the DOM document.
  • adoptedCallback: Called when the custom element is moved.
  • attributeChangedCallback: Called when a custom element adds, deletes, or modifies its own attributes.

The following demonstrates how to use it:

  1. class HelloUser extends HTMLElement {
  2. constructor() {
  3. //Must call super method
  4. super();
  5.  
  6. // Create a div tag
  7. const $box = document.createElement( "p" );
  8. let userName = "User Name" ;
  9. if (this.hasAttribute( "name" )) {
  10. // If the name attribute exists, read the value of the name attribute
  11. userName = this.getAttribute( "name" );
  12. }
  13. // Set the text content of the div tag
  14. $box.innerText = `Hello ${userName}`;
  15.  
  16. // Create a shadow node, other elements created should be attached to this node
  17. const shadow = this.attachShadow({ mode: "open" });
  18. shadow.appendChild($box);
  19. }
  20. connectedCallback() {
  21. console.log( 'Create element' )
  22. // Move the element to the iframe after 5 seconds
  23. setTimeout(() => {
  24. const iframe = document.getElementsByTagName( "iframe" )[0]
  25. iframe.contentWindow.document.adoptNode(this)
  26. }, 5e3)
  27. }
  28. disconnectedCallback() {
  29. console.log( 'delete element' )
  30. }
  31. adoptedCallback() {
  32. console.log( 'Move element' )
  33. }
  34. }

  1. <! -- Insert an iframe into the page and move the custom element into it -->  
  2. <iframe width= "0" height= "0" ></iframe>
  3. <hello- user   name = "Shenfq" ></hello- user >

After the element is created, wait for 5 seconds, and then move the custom element to the iframe document. At this time, you can see that the console will simultaneously show logs of deleting the element and moving the element.

Console

Shadow DOM

When introducing custom elements earlier, Shadow DOM has been used. The function of Shadow DOM is to isolate internal elements from the outside world, so that the structure, style, and behavior of custom elements are not affected by the outside world.

We can see that the <hello-user> tag defined earlier has a shadow-root displayed in the Elements of the console, indicating that there is a Shadow DOM inside.

Shadow DOM

In fact, before Web Components were proposed, the browser used Shadow DOM to encapsulate some internal elements, such as tags. We need to turn on the Show user agent ashdow DOM switch in the console configuration.

set up

Then in the Elements of the console, you can see that there is actually a shadow-root in the <video> tag.

video tag

Creating Shadow DOM

We can create a Shadow DOM inside any node. After getting the element instance, we call the Element.attachShadow() method to attach a new shadow-root to the element.

This method accepts an object with only one mode attribute, whose value is open or closed, indicating whether the nodes in the Shadow DOM can be accessed externally.

  1. <div id= "root" ></div>
  2. <script>
  3. // Get the page
  4. const $root = document.getElementById( 'root' );
  5. const $p = document.createElement( 'p' );
  6. $p.innerText = 'Create a shadow node' ;
  7. const shadow = $root.attachShadow({mode: 'open' });
  8. shadow.appendChild($p);
  9. </script>

Shadow DOM

Differences in modes

As mentioned earlier, the mode value is open or closed. The main difference is whether you can use Element.shadowRoot to obtain the shadow-root and perform some operations.

  1. <div id= "root" ></div>
  2. <script>
  3. // Get the page
  4. const $root = document.getElementById( 'root' );
  5. const $p = document.createElement( 'p' );
  6. $p.innerText = 'Create a shadow node' ;
  7. const shadow = $root.attachShadow({mode: 'open' });
  8. shadow.appendChild($p);
  9. console.log( 'is open' , $div.shadowRoot);
  10. </script>

open mode

closed mode

  1. <div id= "root" ></div>
  2. <script>
  3. // Get the page
  4. const $root = document.getElementById( 'root' );
  5. const $p = document.createElement( 'p' );
  6. $p.innerText = 'Create a shadow node' ;
  7. const shadow = $root.attachShadow({mode: 'closed' });
  8. shadow.appendChild($p);
  9. console.log( 'is closed' , $div.shadowRoot);
  10. </script>

HTML templates

In the previous case, there is an obvious flaw, that is, DOM API is still used to operate DOM, which is significantly less efficient than Vue's template and React's JSX. To solve this problem, the <tempate> and <slot> tags were introduced in the HTML specification.

Using Templates

In simple terms, a template is an ordinary HTML tag, which can be understood as a div, but all the content within this element will not be displayed on the interface.

  1. <template id= "helloUserTpl" >
  2. <p class= "name" > Name </p>
  3. <a target= "blank" class= "blog" > ##</a>
  4. </template>

In JS, we can directly obtain the instance of the template through the DOM API. After obtaining the instance, we generally cannot directly modify the elements in the template. We need to call tpl.content.cloneNode to make a copy, because the template on the page is not a one-time use and may be referenced by other components.

  1. // Get the tag by ID
  2. const tplElem = document.getElementById( 'helloUserTpl' );
  3. const content = tplElem.content.cloneNode( true );

After we get the copied template, we can perform some operations on the template and then insert it into Shadow DOM.

  1. <hello- user   name = "Shenfq" blog= "http://blog.shenfq.com" />
  2.  
  3. <script>
  4. class HelloUser extends HTMLElement {
  5. constructor() {
  6. //Must call super method
  7. super();
  8.  
  9. // Get the tag by ID
  10. const tplElem = document.getElementById( 'helloUserTpl' );
  11. const content = tplElem.content.cloneNode( true );
  12.  
  13. if (this.hasAttribute( 'name' )) {
  14. const $ name = content.querySelector( '.name' );
  15. $ name .innerText = this.getAttribute( 'name' );
  16. }
  17. if (this.hasAttribute( 'blog' )) {
  18. const $blog = content.querySelector( '.blog' );
  19. $blog.innerText = this.getAttribute( 'blog' );
  20. $blog.setAttribute( 'href' , this.getAttribute( 'blog' ));
  21. }
  22. // Create a shadow node, other elements created should be attached to this node
  23. const shadow = this.attachShadow({ mode: "closed" });
  24. shadow.appendChild(content);
  25. }
  26. }
  27.  
  28. // Define an element named <hello- user />
  29. customElements.define( "hello-user" , HelloUser);
  30. </script>

Add styles

The <style> tag can be directly inserted into the <template> tag to define the style inside the template.

  1. <template id= "helloUserTpl" >
  2. <style>
  3. :host {
  4. display: flex;
  5. flex-direction: column ;
  6. width: 200px;
  7. padding: 20px;
  8. background-color: #D4D4D4;
  9. border-radius: 3px;
  10. }
  11.  
  12. .name {
  13. font- size : 20px;
  14. font-weight: 600;
  15. line-height: 1;
  16. margin: 0;
  17. margin-bottom: 5px;
  18. }
  19.  
  20. .email {
  21. font- size : 12px;
  22. line-height: 1;
  23. margin: 0;
  24. margin-bottom: 15px;
  25. }
  26. </style>
  27. <p class= "name" > User   Name
  28. <a target= "blank" class= "blog" > ##</a>
  29. </template>

The :host pseudo-class is used to define the style of shadow-root, which is the style of the tag that wraps this template.

Placeholder elements

A placeholder element first occupies a position in the template, and then when the element is inserted into the interface, it specifies what should be displayed at this position.

  1. <template id= "helloUserTpl" >
  2. <p class= "name" > User   Name
  3. <a target= "blank" class= "blog" > ##</a>
  4. <! --Placeholder-->  
  5. <slot name = "desc" ></slot>
  6. </template>
  7.  
  8. <hello- user   name = "Shenfq" blog= "http://blog.shenfq.com" >
  9. <p slot= "desc" >Welcome to follow the official account: More amazing front-end</p>
  10. </hello-user>

The usage here is consistent with Vue's slot usage, so I won't go into too much detail.

Summarize

This is the end of the introduction to the basic usage of Web Components. Compared with other frameworks that support component solutions, using Web Components has the following advantages:

  • Native browser support, no need to introduce additional third-party libraries;
  • Truly internal private CSS, no style conflicts;
  • A componentized solution that can be implemented without compilation and is isolated from the external DOM;

The main disadvantage of Web Components is that the standards may not be stable yet. For example, the modular solution of templates that is not mentioned in the article has been abolished, and there is no formal solution to introduce template files. In addition, although the native API can be used, it is not easy to use, otherwise there would not be libraries like jQuery to operate DOM. Fortunately, there are many frameworks based on Web Components. Later, I will start an article to talk about the frameworks lit-html and lit-element that use Web Components.

Well, that’s all for today’s article. I hope you can gain something from it.

<<:  Try these tips to make the iPhone's native map app more convenient

>>:  Android Spring Update Preview: Password Checker, Scheduled SMS Sending, TalkBack Improvements, and More

Recommend

Taobao live streaming methodology!

Brands’ self-broadcasting on Tmall presents both ...

Social media marketing, just start from these 3 points!

Social media marketing has become the standard fo...

5 ways to attract traffic to Tik Tok

As we all know, Tik Tok has become the largest tr...

Geely: 70%-80% of electric vehicles are powered by dirty energy

Recently, the 2022 World Power Battery Conference...