{"id":14,"date":"2024-09-10T12:28:59","date_gmt":"2024-09-10T10:28:59","guid":{"rendered":"https:\/\/extendsclass.com\/blog\/?p=14"},"modified":"2023-04-17T10:15:47","modified_gmt":"2023-04-17T08:15:47","slug":"introduction-to-web-components","status":"publish","type":"post","link":"https:\/\/extendsclass.com\/blog\/introduction-to-web-components","title":{"rendered":"Introduction to Web Components"},"content":{"rendered":"\n<p><strong>Web components<\/strong> encompass several technologies that allow for the creation of custom and reusable HTML tags.<\/p>\n\n\n\n<p>In this article, I will present the entry points for<strong> creating custom HTML tags.<\/strong><\/p>\n\n\n\n<p>We will create an HTML tag called &#8220;<strong>paragraph-component<\/strong>&#8221; which represents a paragraph with a title, all surrounded by a border (this has no practical use and is only for the purposes of this tutorial ^^):<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>&lt;paragraph-component data-title=\"Intro to Web Components\"&gt;Content of my paragraph!&lt;\/paragraph-component&gt;<\/code><\/pre>\n\n\n\n<p>The rendering of our HTML tag:<\/p>\n\n\n\n<script>\nlet tmpl = document.createElement('template');\n\/\/ Nous ajoutons le title dans le template\ntmpl.innerHTML = `\n  <style>\n  :host div {\n      border: 1px solid black;\n  }\n  :host span {\n        text-align: center;\n        font-size: 3em;\n  }\n  <\/style>\n  <div>\n    <span id=\"title\"><\/span>\n    <p><slot><\/slot><\/p>\n  <\/div>\n`;\n\nclass ParagraphComponent extends HTMLElement {\n    static get observedAttributes() {\n        \/\/ On indique de recevoir une notification quand l'attribut data-title change de valeur\n        return ['data-title'];\n    }\n    constructor() {\n        super();\n        \/\/ Dans le constructeur, on ne fait qu'indiquer d'utiliser shadow DOM\n        \/\/ On ne g\u00e8re pas encore le rendu \n        this.root = this.attachShadow({\n            mode: 'open'\n        });\n    }\n\n    \/\/ Getter pour r\u00e9cup\u00e9rer l'attribut data-title\n    get title() {\n        return this.getAttribute('data-title');\n    }\n\n    \/\/ On g\u00e9n\u00e8re le rendu d\u00e8s que notre balise est ajout\u00e9e au DOM\n    connectedCallback() {\n        this.render();\n    }\n\n    \/\/ On met \u00e0 jour le rendu quand le title change\n    attributeChangedCallback(attrName, oldVal, newVal) {\n        this.render();\n    }\n\n    \/\/ M\u00e9thode qui g\u00e9n\u00e8re le rendu\n    render() {\n        \/\/ Clone le template\n        const content = tmpl.content.cloneNode(true);\n        \/\/ Ajoute le title\n        const title = content.getElementById(\"title\");\n        title.textContent = this.title;\n        \/\/ On re-initialise le contenu de notre balise (plusieurs appels \u00e0 render)\n        while (this.root.firstChild) this.root.removeChild(this.root.firstChild);\n\n        this.root.appendChild(content);\n    }\n}\n\nwindow.customElements.define('paragraph-component', ParagraphComponent);\n<\/script>\n\n<paragraph-component data-title=\"Intro to Web Components\">Content of my paragraph!<\/paragraph-component>\n\n\n\n<p><div id=\"ez-toc-container\" class=\"ez-toc-v2_0_47_1 counter-hierarchy ez-toc-counter ez-toc-grey ez-toc-container-direction\">\n<div class=\"ez-toc-title-container\">\n<p class=\"ez-toc-title\">Table of Contents<\/p>\n<span class=\"ez-toc-title-toggle\"><a href=\"#\" class=\"ez-toc-pull-right ez-toc-btn ez-toc-btn-xs ez-toc-btn-default ez-toc-toggle\" aria-label=\"ez-toc-toggle-icon-1\"><label for=\"item-69da84dc9a174\" aria-label=\"Table of Content\"><span style=\"display: flex;align-items: center;width: 35px;height: 30px;justify-content: center;direction:ltr;\"><svg style=\"fill: #999;color:#999\" xmlns=\"http:\/\/www.w3.org\/2000\/svg\" class=\"list-377408\" width=\"20px\" height=\"20px\" viewBox=\"0 0 24 24\" fill=\"none\"><path d=\"M6 6H4v2h2V6zm14 0H8v2h12V6zM4 11h2v2H4v-2zm16 0H8v2h12v-2zM4 16h2v2H4v-2zm16 0H8v2h12v-2z\" fill=\"currentColor\"><\/path><\/svg><svg style=\"fill: #999;color:#999\" class=\"arrow-unsorted-368013\" xmlns=\"http:\/\/www.w3.org\/2000\/svg\" width=\"10px\" height=\"10px\" viewBox=\"0 0 24 24\" version=\"1.2\" baseProfile=\"tiny\"><path d=\"M18.2 9.3l-6.2-6.3-6.2 6.3c-.2.2-.3.4-.3.7s.1.5.3.7c.2.2.4.3.7.3h11c.3 0 .5-.1.7-.3.2-.2.3-.5.3-.7s-.1-.5-.3-.7zM5.8 14.7l6.2 6.3 6.2-6.3c.2-.2.3-.5.3-.7s-.1-.5-.3-.7c-.2-.2-.4-.3-.7-.3h-11c-.3 0-.5.1-.7.3-.2.2-.3.5-.3.7s.1.5.3.7z\"\/><\/svg><\/span><\/label><input  type=\"checkbox\" id=\"item-69da84dc9a174\"><\/a><\/span><\/div>\n<nav><ul class='ez-toc-list ez-toc-list-level-1 ' ><li class='ez-toc-page-1 ez-toc-heading-level-2'><a class=\"ez-toc-link ez-toc-heading-1\" href=\"https:\/\/extendsclass.com\/blog\/introduction-to-web-components\/#What_are_Web_components\" title=\"What are Web components?\">What are Web components?<\/a><\/li><li class='ez-toc-page-1 ez-toc-heading-level-2'><a class=\"ez-toc-link ez-toc-heading-2\" href=\"https:\/\/extendsclass.com\/blog\/introduction-to-web-components\/#Creating_a_custom_element\" title=\"Creating a custom element\">Creating a custom element<\/a><ul class='ez-toc-list-level-3'><li class='ez-toc-heading-level-3'><a class=\"ez-toc-link ez-toc-heading-3\" href=\"https:\/\/extendsclass.com\/blog\/introduction-to-web-components\/#Events\" title=\"Events\">Events<\/a><\/li><li class='ez-toc-page-1 ez-toc-heading-level-3'><a class=\"ez-toc-link ez-toc-heading-4\" href=\"https:\/\/extendsclass.com\/blog\/introduction-to-web-components\/#Attributes\" title=\"Attributes\">Attributes<\/a><\/li><li class='ez-toc-page-1 ez-toc-heading-level-3'><a class=\"ez-toc-link ez-toc-heading-5\" href=\"https:\/\/extendsclass.com\/blog\/introduction-to-web-components\/#Shadow_dom\" title=\"Shadow dom\">Shadow dom<\/a><\/li><li class='ez-toc-page-1 ez-toc-heading-level-3'><a class=\"ez-toc-link ez-toc-heading-6\" href=\"https:\/\/extendsclass.com\/blog\/introduction-to-web-components\/#The_tag\" title=\"The &lt;template&gt; tag\">The &lt;template&gt; tag<\/a><\/li><li class='ez-toc-page-1 ez-toc-heading-level-3'><a class=\"ez-toc-link ez-toc-heading-7\" href=\"https:\/\/extendsclass.com\/blog\/introduction-to-web-components\/#The_tag-2\" title=\"The &lt;slot&gt; tag\">The &lt;slot&gt; tag<\/a><\/li><li class='ez-toc-page-1 ez-toc-heading-level-3'><a class=\"ez-toc-link ez-toc-heading-8\" href=\"https:\/\/extendsclass.com\/blog\/introduction-to-web-components\/#Here_is_the_complete_code_for_our_Custom_Element\" title=\"Here is the complete code for our Custom Element:\">Here is the complete code for our Custom Element:<\/a><\/li><\/ul><\/li><li class='ez-toc-page-1 ez-toc-heading-level-2'><a class=\"ez-toc-link ez-toc-heading-9\" href=\"https:\/\/extendsclass.com\/blog\/introduction-to-web-components\/#Extending_an_existing_element\" title=\"Extending an existing element\">Extending an existing element<\/a><\/li><li class='ez-toc-page-1 ez-toc-heading-level-2'><a class=\"ez-toc-link ez-toc-heading-10\" href=\"https:\/\/extendsclass.com\/blog\/introduction-to-web-components\/#Conclusion\" title=\"Conclusion\">Conclusion<\/a><\/li><\/ul><\/nav><\/div>\n<h2 style=\"margin-top: 40px; margin-bottom: 20px; padding: 0px; box-sizing: border-box; -webkit-font-smoothing: antialiased; font-family: Roboto; font-size: 32px; color: rgb(1, 1, 1); white-space: normal;\"><span class=\"ez-toc-section\" id=\"What_are_Web_components\"><\/span><span id=\"Les_composants_Web_crsquoest_quoi\" style=\"margin: 0px; padding: 0px; box-sizing: border-box; -webkit-font-smoothing: antialiased;\">What are Web components?<\/span><span class=\"ez-toc-section-end\"><\/span><\/h2><\/p>\n\n\n\n<p>Web components allow creating reusable graphical user interface components without frameworks. They rely on several technologies:<\/p>\n\n\n\n<ul>\n<li><strong>Custom Element: <\/strong>A set of APIs that allows creating new HTML tags or extending standard HTML tags.<\/li>\n\n\n\n<li><strong>Shadow Dom<\/strong>: The Shadow DOM API is a way to encapsulate an element&#8217;s DOM. When creating a Custom Element using the Shadow DOM, its DOM is &#8220;hidden&#8221; and does not conflict with the page&#8217;s DOM. The element&#8217;s DOM is not part of the page, so a call to <code>document.querySelector<\/code> will not return it.<\/li>\n\n\n\n<li><strong>HTML template<\/strong>: This allows for the creation of skeletons\/templates that are not displayed on the page. They can be used as a basis for creating elements.<\/li>\n<\/ul>\n\n\n\n<p>We can use our custom tags in our projects as if they were standard HTML tags (there&#8217;s only one JS file to include).<\/p>\n\n\n\n<h2 class=\"wp-block-heading\"><span class=\"ez-toc-section\" id=\"Creating_a_custom_element\"><\/span>Creating a custom element<span class=\"ez-toc-section-end\"><\/span><\/h2>\n\n\n\n<p>It&#8217;s really simple to create a Custom Element, just two lines of code are enough:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>class ParagraphComponent extends HTMLElement {}\ncustomElements.define('paragraph-component', ParagraphComponent);<\/code><\/pre>\n\n\n\n<p>Small explanations:<\/p>\n\n\n\n<ul>\n<li><strong>customElements.define<\/strong>: This method allows creating a Custom Element and takes 3 parameters: the name of the Custom Element, the constructor function to create this Custom Element, and finally the options.<\/li>\n\n\n\n<li><strong>extends HTMLElement<\/strong>:Extending the HTMLElement class allows you to inherit the entire DOM API. In other words, any property or method you add to your class becomes part of the DOM interface of your element. It&#8217;s magical \ud83d\ude42<\/li>\n\n\n\n<li><strong>paragraph-component<\/strong>: This is the name of our HTML tag. The name must contain a hyphen to allow the HTML parser to differentiate between standard elements and Custom Elements.<\/li>\n<\/ul>\n\n\n\n<p>And voil\u00e0, now we can use our Custom Element:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>&lt;paragraph-component&gt;&lt;\/paragraph-component&gt;<\/code><\/pre>\n\n\n\n<p>We can also instantiate our custom element via JavaScript:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>&lt;script&gt;\nconst myParagraph = document.createElement('paragraph-component');\n\n\/\/ add a paragraph to my page\ndocument.body.appendChild(myParagraph);<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\"><span class=\"ez-toc-section\" id=\"Events\"><\/span>Events<span class=\"ez-toc-section-end\"><\/span><\/h3>\n\n\n\n<p>Custom Elements have callback functions executed at different stages of their lifecycle:<\/p>\n\n\n\n<figure class=\"wp-block-table\"><table><tbody><tr><td><strong>Callback<\/strong><\/td><td><strong>Execution step<\/strong><\/td><\/tr><tr><td>constructor<\/td><td>During the element creation.<\/td><\/tr><tr><td>connectedCallback<\/td><td>This is executed each time the element is inserted into the DOM.<\/td><\/tr><tr><td>disconnectedCallback<\/td><td>Executed every time the element is removed from the DOM.<\/td><\/tr><tr><td>attributeChangedCallback<\/td><td>Executed when an attribute has been added, modified, or removed. This callback function is only executed for attributes listed in the observedAttributes property.<\/td><\/tr><tr><td>adoptedCallback<\/td><td>Executed when the custom element is moved to a new document (document.adoptNode).<\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<h3 class=\"wp-block-heading\"><span class=\"ez-toc-section\" id=\"Attributes\"><\/span>Attributes<span class=\"ez-toc-section-end\"><\/span><\/h3>\n\n\n\n<p>The method <code>getAttribute<\/code> allows you to retrieve the value of an attribute.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>    \/\/ Getter to retrieve the attribute data-title\n    get title() {\n        return this.getAttribute('data-title');\n    }<\/code><\/pre>\n\n\n\n<p>It is possible to be notified when the value of an attribute changes. We need to list the attributes for which we want to receive a notification in the&nbsp;observedAttributes property.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>  static get observedAttributes() {\n        \/\/ Notification when the data-title attribute changes its value\n        return &#91;'data-title'];\n    }<\/code><\/pre>\n\n\n\n<p>The method <code>attributeChangedCallback<\/code> is executed every time an observed attribute&#8217;s value changes.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>    attributeChangedCallback(attrName, oldVal, newVal) {\n        if (attrName == 'data-title') {\n       ...\n       }\n    }<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\"><span class=\"ez-toc-section\" id=\"Shadow_dom\"><\/span>Shadow dom<span class=\"ez-toc-section-end\"><\/span><\/h3>\n\n\n\n<p>The Shadow DOM allows encapsulating the CSS and DOM of our custom element, so they won&#8217;t conflict with those of the page.<\/p>\n\n\n\n<p>The internal DOM of our tag will not be accessible with document.querySelector.<\/p>\n\n\n\n<p>The&nbsp;attachShadow&nbsp;method attaches a Shadow DOM tree to our custom element and returns a reference to the root node.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>    constructor() {\n        super();\n        this.root = this.attachShadow({mode: 'closed'\n});\n    }<\/code><\/pre>\n\n\n\n<p>The root node is a regular element, and we can add elements to it using the <code>appendChild<\/code> method.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\"><span class=\"ez-toc-section\" id=\"The_tag\"><\/span><strong>The &lt;template<\/strong>&gt; tag<span class=\"ez-toc-section-end\"><\/span><\/h3>\n\n\n\n<p>The<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>&lt;template id=\"myTemplate\"&gt;\n  &lt;style&gt;\n    &lt;!-- CSS of our template --&gt;\n    ...\n  &lt;\/style&gt;\n\n  &lt;!-- HTML of our template--&gt;\n  ...\n&lt;\/template&gt;<\/code><\/pre>\n\n\n\n<p>The &lt;template&gt; can be instantiated in JavaScript:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>\/\/ Get the template\nconst myTemplate = document.getElementById('myTemplate');\n\/\/ clone the template\nconst content = myTemplate.content.cloneNode(true);\n\n...\n\n\/\/ Then we add our template to the shadow root.\nthis.root.appendChild(content);<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\"><span class=\"ez-toc-section\" id=\"The_tag-2\"><\/span>The &lt;slot&gt; tag<span class=\"ez-toc-section-end\"><\/span><\/h3>\n\n\n\n<p>The &lt;slot&gt; tag represents a placeholder in a web component, and can be filled with any HTML structure. We will use this tag to display the content of the paragraph.<\/p>\n\n\n\n<p>So we will use this tag in our template:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>&lt;template id=\"myTemplate\"&gt;\n  &lt;style&gt;\n\n  &lt;!-- :host is a CSS selector. It allows selecting a Custom Element from inside the shadow DOM.--&gt;\n  :host div {\n      border: 1px solid black;\n  }\n  :host span {\n        text-align: center;\n        font-size: 3em;\n  }\n  &lt;\/style&gt;\n  &lt;div&gt;\n    &lt;span id=\"title\"&gt;&lt;\/span&gt;\n\n    &lt;!-- Slot will contain the HTML of the Custom Element --&gt;\n    &lt;p&gt;&lt;slot&gt;&lt;\/slot&gt;&lt;\/p&gt;\n  &lt;\/div&gt;\n<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\"><span class=\"ez-toc-section\" id=\"Here_is_the_complete_code_for_our_Custom_Element\"><\/span>Here is the complete code for our Custom Element:<span class=\"ez-toc-section-end\"><\/span><\/h3>\n\n\n\n<p>Below is the complete code for our paragraph-component Custom Element:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>\/\/ create our template with JavaScript\nlet tmpl = document.createElement('template');\n\/\/ we add the title in the template\ntmpl.innerHTML = `\n  &lt;style&gt;\n  :host div {\n      border: 1px solid black;\n  }\n  :host span {\n        text-align: center;\n        font-size: 3em;\n  }\n  &lt;\/style&gt;\n  &lt;div&gt;\n    &lt;span id=\"title\"&gt;&lt;\/span&gt;\n    &lt;p&gt;&lt;slot&gt;&lt;\/slot&gt;&lt;\/p&gt;\n  &lt;\/div&gt;\n`;\n\nclass ParagraphComponent extends HTMLElement {\n    static get observedAttributes() {\n        \/\/ We indicate to receive a notification when the data-title attribute changes its value.\n        return &#91;'data-title'];\n    }\n    constructor() {\n        super();\n        \/\/ In the constructor, we simply indicate to use shadow DOM.\n        \/\/ we haven't handled the rendering yet.\n        this.root = this.attachShadow({\n            mode: 'open'\n        });\n    }\n\n    \/\/ Getter to retrieve the data-title attribute.\n    get title() {\n        return this.getAttribute('data-title');\n    }\n\n    \/\/ We generate the rendering as soon as our tag is added to the DOM.\n    connectedCallback() {\n        this.render();\n    }\n\n    \/\/ We update the rendering when the title changes.\n    attributeChangedCallback(attrName, oldVal, newVal) {\n        this.render();\n    }\n\n    \/\/ Method that generates the rendering\n    render() {\n        \/\/ Clone  template\n        const content = tmpl.content.cloneNode(true);\n        \/\/ Add the title\n        const title = content.getElementById(\"title\");\n        title.textContent = this.title;\n        \/\/ On re-initialize the content of our tag (multiple calls to render)\n        while (this.root.firstChild) this.root.removeChild(this.root.firstChild);\n\n        this.root.appendChild(content);\n    }\n}\n\nwindow.customElements.define('paragraph-component', ParagraphComponent);\n\n\n<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\"><span class=\"ez-toc-section\" id=\"Extending_an_existing_element\"><\/span>Extending an existing element<span class=\"ez-toc-section-end\"><\/span><\/h2>\n\n\n\n<p><strong>Web Components <\/strong>also allow us to extend standard HTML tags, as well as custom elements!<\/p>\n\n\n\n<p>We can easily extend our paragraph:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>class SuperParagraphComponent extends ParagraphComponent {\n}\nwindow.customElements.define('super-paragraph-component', SuperParagraphComponent );<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\"><span class=\"ez-toc-section\" id=\"Conclusion\"><\/span>Conclusion<span class=\"ez-toc-section-end\"><\/span><\/h2>\n\n\n\n<p>Great, I hope this tutorial was helpful. It covered the basics of creating custom HTML tags using Web Components. It&#8217;s worth noting that we only scratched the surface, and there is much more to learn and explore!<\/p>\n\n\n\n<p>Also, keep in mind that you don&#8217;t have to create Custom Elements using pure JavaScript. You can create them with frameworks like Angular, <a href=\"https:\/\/www.polymer-project.org\/\" title=\"\">Polymer Project<\/a>, and more.<\/p>\n\n\n\n<p>You can find the code for this tutorial on <a href=\"https:\/\/codepen.io\/devtools\/pen\/poNpyqY\" title=\"\">CodePen<\/a> (In French).<\/p>\n","protected":false},"excerpt":{"rendered":"<p>This tutorial presents the basics of web components in order to create custom HTML tags (Custom Elements). Find the different concepts.<\/p>\n","protected":false},"author":1,"featured_media":52,"comment_status":"open","ping_status":"open","sticky":true,"template":"","format":"standard","meta":{"_sitemap_exclude":false,"_sitemap_priority":"","_sitemap_frequency":""},"categories":[2],"tags":[],"aioseo_notices":[],"_links":{"self":[{"href":"https:\/\/extendsclass.com\/blog\/wp-json\/wp\/v2\/posts\/14"}],"collection":[{"href":"https:\/\/extendsclass.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/extendsclass.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/extendsclass.com\/blog\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/extendsclass.com\/blog\/wp-json\/wp\/v2\/comments?post=14"}],"version-history":[{"count":10,"href":"https:\/\/extendsclass.com\/blog\/wp-json\/wp\/v2\/posts\/14\/revisions"}],"predecessor-version":[{"id":17,"href":"https:\/\/extendsclass.com\/blog\/wp-json\/wp\/v2\/posts\/14\/revisions\/17"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/extendsclass.com\/blog\/wp-json\/wp\/v2\/media\/52"}],"wp:attachment":[{"href":"https:\/\/extendsclass.com\/blog\/wp-json\/wp\/v2\/media?parent=14"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/extendsclass.com\/blog\/wp-json\/wp\/v2\/categories?post=14"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/extendsclass.com\/blog\/wp-json\/wp\/v2\/tags?post=14"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}