Web Components are a different set of technologies that allow you to create reusable custom elements whose functionality is encapsulated outside of your code and use them in your Web applications.

Concepts and Usage

As developers, we all know it’s a good idea to reuse as much code as possible. This is often not so easy for custom tag structures — think of complex HTML (and associated styles and scripts), sometimes you have to write code to render custom UI controls, and using them multiple times can mess up your pages if you’re not careful.

Web Components aims to solve these problems — it consists of three main techniques that can be used together to create custom elements that encapsulate functionality and can be reused anywhere you like without worrying about code conflicts.

  • Custom elementsCustom Elements: A set of JavaScript apis that allow you to define custom elements and their behavior, and then use them as needed in your user interface.
  • Shadow DOMShadow DOM: A set of JavaScript apis for attaching a wrapped “shadow” DOM tree to an element (rendered separately from the main document DOM) and controlling its associated functionality. This way, you can keep the functionality of elements private so that they can be scripted and styled without fear of running afoul of the rest of the document.
  • HTML templatesHTML template:<template><slot>The element lets you write tag templates that are not displayed on rendered pages. They can then be reused many times as a basis for custom element structures.

The basic approach to implementing a Web Component is usually as follows:

  1. Create a class or function to specify the functionality of the Web component (if you use classes, use ECMAScript 2015 class syntax).
  2. useCustomElementRegistry.define()Method registers your new custom element and passes it the name of the element to be defined, the class that specifies the element’s functionality, and optionally the element it inherits from.
  3. Use if neededElement.attachShadow()The Shadow DOM () method attaches a shadow DOM to the custom element. Add child elements, event listeners, and so on to the Shadow DOM using the usual DOM methods.
  4. Use if needed<template><slot>Define an HTML template. Clone the template again using normal DOM methods and attach it to your Shadow DOM.
  5. Use custom elements wherever you like on the page, just as you would use regular HTML elements.

The life cycle

Special callback functions defined in the class definition of a custom element that affect its behavior: • connectedCallback: called when the custom element is first connected to the document DOM. • disconnectedCallback: Called when the custom element is disconnected from the document DOM. • adoptedCallback: called when a custom element is moved to a new document. • attributeChangedCallback: as a custom attributes are added, removed or change is invoked.

attributeChangedCallback > connectedCallback > disconnectedCallback

The selector

  • :defined: Matches any defined element, including built-in elements and usageCustomElementRegistry.define()Define a custom element.
  • :host: Selects the current custom element
  • :host()Select the shadow host of the shadow DOM, and the content in () is the CSS it uses internally (so you can select custom elements from within the Shadow DOM) — but only the shadow Host element that matches the selector of the given method. Such as::host(name="hello").
  • :host-context(): Select the shadow host of the Shadow DOM, which is the CSS it uses internally (so you can select custom elements from within the Shadow DOM) — but only match the child shadow host element of the matching element with the selector of the given method.

The sample

Now that you know the basics, let’s implement what a basic drop-down selection component (including select and Option) does

  1. Add change event
  2. Displays/hides the drop-down list box
  3. Assign input when option is clicked
  4. Click on other areas to hide drop-down boxes

The specific implementation

Basic style

class Select extends HTMLElement {
  constructor() {
    // The super method must be called first
    super(a);// The function code of the element is written here
    const template = document.createElement("template");
    template.innerHTML = `   
      
`
; const shadowELe = this.attachShadow({ mode: "open" }); const content = template.content.cloneNode(true); shadowELe.appendChild(content); }}class Option extends HTMLElement { constructor() { // The super method must be called first super(a);// The function code of the element is written here const template = document.createElement("template"); template.innerHTML = `
`
; const shadowELe = this.attachShadow({ mode: "open" }); const content = template.content.cloneNode(true); shadowELe.appendChild(content); } static get observedAttributes() { return ["value"]; } } customElements.define("ivy-select", Select); customElements.define("ivy-option", Option); Copy the code

Add event

class Select extends HTMLElement {
  constructor() {
    // The super method must be called first
    super(a);// The function code of the element is written here.this.$input = shadowELe.querySelector(".select-inner");
    this.dropEle = shadowELe.querySelector(".drop");
    this.value === null;
    this.$input.addEventListener("click".() = > {
      this.dropEle.style.display = "block";
    });
    this.dropEle.addEventListener("click".(ev) = > {
      const target = ev.target;
      const nodeName = target.nodeName.toLowerCase();
      if (nodeName === "ivy-option") {
        this.value = target.getAttribute("value");
        this.$input.setAttribute("value", target.innerHTML);
        // Custom events
        this.dispatchEvent(
          new CustomEvent("change", {
            detail: {
              value: this.value,
            },
          })
        );
        this.dropEle.style.display = "none"; }});this.BodyClick = () = > {
      this.dropEle.style.display = "none";
    };
  }
  connectedCallback() {
    document.addEventListener("click".this.BodyClick, true);
  }
  disconnectedCallback() {
    document.removeEventListener("click".this.BodyClick); }}Copy the code

Note that if you want to trigger the attributeChangedCallback() callback after an element attribute changes, you must listen for this attribute. This is done by defining the get observedAttributes() function, which contains a return statement that returns an array containing the names of the attributes to listen on: observedAttributes() static get observedAttributes() {return [‘value’]; }

Full code: Portal