{"id":6486,"date":"2018-09-25T18:14:13","date_gmt":"2018-09-25T18:14:13","guid":{"rendered":"http:\/\/putridparrot.com\/blog\/?p=6486"},"modified":"2018-09-25T18:23:32","modified_gmt":"2018-09-25T18:23:32","slug":"starting-out-with-web-components","status":"publish","type":"post","link":"https:\/\/putridparrot.com\/blog\/starting-out-with-web-components\/","title":{"rendered":"Starting out with web components"},"content":{"rendered":"<p><strong>Introduction<\/strong><\/p>\n<p>Web components are analogous to self-contained controls or components as you&#8217;ll have seen in Windows Forms, WPF etc. They allow developers to package up style, scripts and HTML into a single file which also is used to create a custom element, i.e. not part of the standard HTML.<\/p>\n<p>Sadly, still not yet available in all browsers (although I believe Polyfills exist for the main ones). Hence, for this post you&#8217;ll either need a polyfill or the latest version of Google Chrome (which I&#8217;m using to test this with).<\/p>\n<p><strong>What does it look like using web components?<\/strong><\/p>\n<p>To start with we create an HTML file for our web component, we then define the template for the component &#8211; this is basically the combination of style and the control&#8217;s HTML. Finally we write scripts to effect the shadow DOM by interacting with our web component.<\/p>\n<p>In this post we&#8217;re going to create a simple little web component which will flash it&#8217;s text. Before we look at the component code let&#8217;s see the component in use within the following index.html file<\/p>\n<pre class=\"brush: xml; title: ; notranslate\" title=\"\">\r\n&lt;!DOCTYPE html&gt;\r\n&lt;html&gt;\r\n&lt;head&gt;\r\n    &lt;meta charset=&quot;utf-8&quot;&gt;\r\n    &lt;title&gt;Web Component Test&lt;\/title&gt;\r\n    &lt;link rel=&quot;import&quot; href=&quot;flashtext.html&quot;&gt;\r\n&lt;\/head&gt;\r\n&lt;body&gt;\r\n   &lt;flash-text data-text=&quot;Hello World&quot;&gt;&lt;\/flash-text&gt;\r\n&lt;\/body&gt;\r\n&lt;\/html&gt;\r\n<\/pre>\n<p>The key points here are that we include the web component using the line<\/p>\n<pre class=\"brush: xml; title: ; notranslate\" title=\"\">\r\n&lt;link rel=&quot;import&quot; href=&quot;flashtext.html&quot;&gt;\r\n<\/pre>\n<p>and then we use the custom element <em>flash-text<\/em> with it&#8217;s custom attributes and we use the component like this<\/p>\n<pre class=\"brush: xml; title: ; notranslate\" title=\"\">\r\n&lt;flash-text data-text=&quot;Hello World&quot;&gt;&lt;\/flash-text&gt;\r\n<\/pre>\n<p>It&#8217;s <strong>important<\/strong> to note the closing element, a self closing element is not correctly handled (at least at this time using Google Chrome) and whilst the first usage of a web component might be displayed, if we had several flashing text components in the index.html it&#8217;s possible only one would be displayed and no obvious error or reason for the other components not displaying.<\/p>\n<p><strong>Creating our web component<\/strong><\/p>\n<p>We already decided our component will be stored in flashtext.html (as that&#8217;s what we linked to in the previous section). <\/p>\n<p>To start off with, create the following<\/p>\n<pre class=\"brush: xml; title: ; notranslate\" title=\"\">\r\n&lt;template id=&quot;flashTextComponent&quot;&gt;\r\n&lt;\/template&gt;\r\n&lt;script&gt;\r\n&lt;\/script&gt;\r\n<\/pre>\n<p>We&#8217;ve created a new template with the id flashTextComponent. This id will be used within our script, it&#8217;s not what the pages using the component uses. To create a new custom element by adding the following to the script<\/p>\n<pre class=\"brush: xml; title: ; notranslate\" title=\"\">\r\nvar flashText = document.registerElement('flash-text', {\r\n});\r\n<\/pre>\n<p>But we&#8217;re getting a little ahead of ourselves. Let&#8217;s instead create some styling and the HTML for our component. Within the template element, place the following<\/p>\n<pre class=\"brush: xml; title: ; notranslate\" title=\"\">\r\n&lt;style&gt;\r\n   .flashText {\r\n      float: left;\r\n      width: 152px;\r\n      background-color: red;\r\n      margin-bottom: 20px;\r\n   }\r\n\r\n   .flashText &gt; .text {\r\n      color: #fff;\r\n      font-size: 15px;\r\n      width: 150px;\r\n      text-align: center;\r\n   }\r\n\r\n&lt;\/style&gt;\r\n&lt;div class=&quot;flashText&quot;&gt;\r\n   &lt;div class=&quot;text&quot;&gt;&lt;\/div&gt;\r\n&lt;\/div&gt;\r\n<\/pre>\n<p>The style section simply defines the CSS for both the <em>flashText<\/em> div and the text div within it. The div elements create our layout template. Obviously if you created something like a menu component, the HTML for this would go here with custom attributes which we&#8217;ll define next, mapping to the template HTML. <\/p>\n<p>Next up we need to create the code and custom attributes to map the data to our template. Before we do this let&#8217;s make sure the browser supports web components by writing<\/p>\n<pre class=\"brush: jscript; title: ; notranslate\" title=\"\">\r\nvar template = document.createElement('template');\r\nif('content' in template) {\r\n   \/\/ supports web components\r\n}\r\nelse {\r\n   \/\/ does not support web components\r\n}\r\n<\/pre>\n<p>If no <em>content<\/em> exists on the template, Google Chrome will report the error in the dev tools stating content is null (or similar wording).<\/p>\n<p>Within the <em>\/\/ supports web components<\/em> section place the following<\/p>\n<pre class=\"brush: jscript; title: ; notranslate\" title=\"\">\r\nvar ownerDocument = document.currentScript.ownerDocument;\r\nvar component = ownerDocument.querySelector('#flashTextComponent');\r\n\r\nvar templatePrototype = Object.create(HTMLElement.prototype);\r\n\r\ntemplatePrototype.createdCallback = function () {\r\n   var root = this.createShadowRoot();\r\n   root.appendChild(document.importNode(component.content, true));\r\n\r\n   var name = root.querySelector('.text');\r\n   name.textContent = this.getAttribute('data-text');\r\n\r\n   setInterval(function(){\r\n      name.style.visibility = (name.style.visibility == 'hidden' ? '' : 'hidden');\r\n   }, 1000);\r\n};\r\n\r\nvar flashText = document.registerElement('flash-text', {\r\n    prototype: templatePrototype\r\n});\r\n<\/pre>\n<p>Let&#8217;s look at what we&#8217;ve done here. First we get at the <em>ownerDocument<\/em> and then locate our template via it&#8217;s id <em>flashTextComponent<\/em>. Now were going to create an HTMLElement prototype which will (in essence) replace our usage of the web component. When the HTMLElement is created we interact with the shadow DOM placing our component HTML into it and then interacting with parts of the template HTML, i.e. in this case placing data from the <em>data-text<\/em> custom attribute, into the text content of the div <em>text<\/em>.<\/p>\n<p>As we want this text to flash we implement the script for this and attached to the visibility style of the <em>text<\/em>.<\/p>\n<p>Finally, as mentioned previously, we register our custom element and &#8220;map&#8221; it to the previously created prototype.<\/p>\n<p><strong>Using in ASP.NET<\/strong><\/p>\n<p>ASP.NET can handle static pages easily enough, we just need to add the following to the RouteConfig<\/p>\n<pre class=\"brush: csharp; title: ; notranslate\" title=\"\">\r\nroutes.IgnoreRoute(&quot;{filename}.html&quot;);\r\n<\/pre>\n<p>Now, inside _Layout.cshtml put in the head section<\/p>\n<link rel=\"import\" href=\"~\/Static\/flashtext.html\">\n<p>and within the Index.cshtml (or wherever you want it) place your custom elements, i.e.<\/p>\n<pre class=\"brush: xml; title: ; notranslate\" title=\"\">\r\n&lt;flash-text data-text=&quot;Hello World&quot;&gt;&lt;\/flash-text&gt;\r\n<\/pre>\n<p><strong>References<\/strong><\/p>\n<p><a href=\"https:\/\/developer.mozilla.org\/en-US\/docs\/Web\/HTML\/Element\/template\" rel=\"noopener\" target=\"_blank\">https:\/\/developer.mozilla.org\/en-US\/docs\/Web\/HTML\/Element\/template<\/a><br \/>\n<a href=\"https:\/\/developers.google.com\/web\/fundamentals\/web-components\/customelements\" rel=\"noopener\" target=\"_blank\">https:\/\/developers.google.com\/web\/fundamentals\/web-components\/customelements<\/a><br \/>\n<a href=\"https:\/\/www.webcomponents.org\/\" rel=\"noopener\" target=\"_blank\">https:\/\/www.webcomponents.org\/<\/a><br \/>\n<a href=\"https:\/\/stackoverflow.com\/questions\/45418556\/whats-the-reason-behind-not-allowing-self-closing-tag-in-custom-element-in-spec\" rel=\"noopener\" target=\"_blank\">https:\/\/stackoverflow.com\/questions\/45418556\/whats-the-reason-behind-not-allowing-self-closing-tag-in-custom-element-in-spec<\/a><\/p>\n<p><strong>Code for this post<\/strong><\/p>\n<p><a href=\"https:\/\/github.com\/putridparrot\/blog-projects\/tree\/master\/webcomponent\" rel=\"noopener\" target=\"_blank\">https:\/\/github.com\/putridparrot\/blog-projects\/tree\/master\/webcomponent<\/a><\/p>\n","protected":false},"excerpt":{"rendered":"<p>Introduction Web components are analogous to self-contained controls or components as you&#8217;ll have seen in Windows Forms, WPF etc. They allow developers to package up style, scripts and HTML into a single file which also is used to create a custom element, i.e. not part of the standard HTML. Sadly, still not yet available in [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"closed","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_jetpack_memberships_contains_paid_content":false,"footnotes":""},"categories":[226,45,224,225],"tags":[],"class_list":["post-6486","post","type-post","status-publish","format-standard","hentry","category-html5","category-javascript","category-web","category-web-component"],"jetpack_sharing_enabled":true,"jetpack_featured_media_url":"","_links":{"self":[{"href":"https:\/\/putridparrot.com\/blog\/wp-json\/wp\/v2\/posts\/6486","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/putridparrot.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/putridparrot.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/putridparrot.com\/blog\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/putridparrot.com\/blog\/wp-json\/wp\/v2\/comments?post=6486"}],"version-history":[{"count":5,"href":"https:\/\/putridparrot.com\/blog\/wp-json\/wp\/v2\/posts\/6486\/revisions"}],"predecessor-version":[{"id":6504,"href":"https:\/\/putridparrot.com\/blog\/wp-json\/wp\/v2\/posts\/6486\/revisions\/6504"}],"wp:attachment":[{"href":"https:\/\/putridparrot.com\/blog\/wp-json\/wp\/v2\/media?parent=6486"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/putridparrot.com\/blog\/wp-json\/wp\/v2\/categories?post=6486"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/putridparrot.com\/blog\/wp-json\/wp\/v2\/tags?post=6486"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}