From WebFragments to Web Components

How to create Web Components with WebFragments

Web Components

Web Components is a W3C fresh new HTML5 feature. It is a set of specs which let web developers leverage their HTML, CSS and JavaScript knowledge to build widgets that can be reused easily and reliably. I will explain Web Components with just an example. Suppose you want a BreadCrumbs to look like the one on the Google Support site. Google developers use the following HTML

<ul class="breadcrumbs"> <li style="z-index:2"> <a style="width: 42px;" href=""> <span title="Guide">Guide</span> </a> </li> <li style="z-index:1"> <a style="width: 119px;" href=""> <span title="....">....</span> </a> </li> </ul>

with the appropriate CSS code. Web Components would allow Google developers to simply write

<breadcrumbs/> <breadcrumb title="Guide" href="..."/> <breadcrumb title="..." href="..."/> </breadcrumbs>

The breadcrumbs Web Components

To create the Breadcrumbs Web Component, take a look at the following code (demo):

<template id="Breadcrumbs"> <ul> <li style="z-index:2"> <a style="width: 42px;" href=""> <span title="Guide">Guide</span> </a> </li> <li style="z-index:1"> <a style="width: 119px;" href=""> <span title="....">....</span> </a> </li> </ul> </template>

This is the HTML definition of the Web Components, while

 xtag.register('Breadcrumbs', { lifecycle: { created: function() {} }, events:{ }, accessors: { }, methods: {} } });

is the registration. At this point we are allowed to use the tag “Breadcrumbs” inside HTML pages. Web Components will be rendered has you define them in the template tag (Note: Templates will never be rendered alone, just their implementation will).

Web Components Model

To be effective a template should be parametric. There are different ways to add parameters. The easiest way is to use HTML markup tags.

<Breadcrumbs> <Breadcrumb title="Guide" href="..."> </Breadcrumbs>

This approach is very simple for both static and server-side dynamic pages. The Web Components will read parameters from its children. To implement this technique the created method from the Web Components specifications should be use. The created method is just called every time n Breadcrumbs HTML node is created.

   xtag.register('Breadcrumbs', { lifecycle: { created: function() { // store the list of parameters            var arr=[];            this.children.forEach(function(e){arr.push(e);}); // Generate the Node from a template         var tpl = document.getElementById('breadcrumbs');         this.appendChild(templateContent(tpl).cloneNode(true)); // add breadcrumbs            arr.forEach(function(elem){ ...            }); } ..

Click here to see how it works. A different approach is to use JavaScript to add them by coding.

   xtag.register('Breadcrumbs', { methods: { addBreadcrumb: function(title, href) { this.querySelector("UL").appendChild(..); } } ..

The methods property specifies methods exported from the Breadcrumbs Dom nodes. Thus you can use:

<script type="text/javascript"> document.querySelect("#breadcrumbs").addBreadcrumb("Guide", ""); <script>

or you can use both approaches.

   xtag.register('Breadcrumbs', { lifecycle: { created: function() { // store the list of parameters            var arr=[];            this.children.forEach(function(e){arr.push(e);}); // Generate the Node from a template         var tpl = document.getElementById('breadcrumbs');         this.appendChild(templateContent(tpl).cloneNode(true)); // add breadcrumbs            arr.forEach(function(elem){ this.addBreadcrumb( elem.getAttribute("title"), elem.getAttribute("href"));            }); } } methods: { addBreadcrumb: function(title, href) { this.querySelector("UL").appendChild(..); } ..

Other approaches can be use.

<Breadcrumbs> <script type="text/json"> [ { title: "Guide", href=".."} ] </script> </Breadcrumbs>

or

<Breadcrumbs ajax="query"> </Breadcrumbs>

Styling a Web Components.

Since the main purpose of Web Components is to create reusable html widgets it allows developers to define styles for them. In fact, it is possible to add styles to templates.

<template id="Breadcrumbs"> <style>...</style> <ul> .... </ul> </template>

The style definition should render only the templates and doesn’t affect other html fragments. This is what Web Fragments does. Web Fragments manages isolated and self-consistent html fragments with css rules. Take a look at how the Web Components is done with WebFragments (see demo here).

<template id="Breadcrumbs">     <style type="text/css" media="">             /* 43: * html .goog-inline-block */         .md5_e61f229ff58986f555acabd534cbfdc0, *.webf_41482b6771c1f449c91d864c59602ca3 html .goog-inline-block {             display: inline;         }             /* 244: a, abbr, acronym, address, applet, article, aside, aside, big, blockquote, body, caption, cite, code, dd, del, dfn, div, dl, dt, em, fieldset, figure, font, footer, form, h1, h2, h3, h4, h5, h6, header, html, iframe, img, ins, kbd, label, legend, li, menu, nav, object, ol, p, pre, q, s, samp, section, small, span, strike, strong, sub, sup, table, tbody, td, tfoot, th, thead, tr, tt, ul, var */         .md5_7b5fc6870899a291c28477f524137f1b, ul.webf_41482b6771c1f449c91d864c59602ca3,.webf_41482b6771c1f449c91d864c59602ca3 li,.webf_41482b6771c1f449c91d864c59602ca3 a,.webf_41482b6771c1f449c91d864c59602ca3 span {             margin: 0px;             padding: 0px;             border: 0px none;             outline: 0px none;             font-weight: inherit;             font-style: inherit;             font-size: 100%;             font-family: inherit;             vertical-align: baseline;         }             /* 255: a:hover, a:active */         .md5_70c0f18d91a7ae93f6b3c8d7e951f853, .webf_41482b6771c1f449c91d864c59602ca3 a:hover,.webf_41482b6771c1f449c91d864c59602ca3 a:active {             outline: 0px none;         }             /* 256: a:focus */         .md5_281764cc994971731e87f98131408fc9, .webf_41482b6771c1f449c91d864c59602ca3 a:focus {             outline: thin dotted;         }             /* 542: a */         .md5_41e2d30ebd6c5534cf0df66658806d63, .webf_41482b6771c1f449c91d864c59602ca3 a {             color: rgb(17, 85, 204);             text-decoration: none;         }             /* 543: a:hover */         .md5_67ccd675737b65c84a130449313e9434, .webf_41482b6771c1f449c91d864c59602ca3 a:hover {             text-decoration: underline;         }             /* 544: a:visited */         .md5_93c2638d404ac92258f4c3ebdff6a90f, .webf_41482b6771c1f449c91d864c59602ca3 a:visited {             color: rgb(102, 17, 204);         }             /* 947: .breadcrumbs */         .md5_ca313fc50f64ab94a7eeca46ee55aa22, .breadcrumbs.webf_41482b6771c1f449c91d864c59602ca3 {             white-space: nowrap;         }             /* 948: .breadcrumbs > li */         .md5_5a5eccb18419fe2e428ef47dd85cd07b, .breadcrumbs.webf_41482b6771c1f449c91d864c59602ca3>li {             display: inline-block;             list-style: none outside none;             margin-left: -15px;             position: relative;         }             /* 949: .breadcrumbs .crumb */         .md5_09314cbd5831923950cbe1eccca8b7c3, .breadcrumbs.webf_41482b6771c1f449c91d864c59602ca3 .crumb {             background-position: 100% -93px;             background-repeat: no-repeat;             color: rgb(102, 102, 102);             display: block;             font-size: 12px;             padding-right: 15px;             text-decoration: none;         }             /* 951: .breadcrumbs .crumb:visited */         .md5_a9186830f5439b1c50500927b1dbdc28, .breadcrumbs.webf_41482b6771c1f449c91d864c59602ca3 .crumb:visited {             color: rgb(102, 102, 102);         }             /* 952: .breadcrumbs .crumb:hover */         .md5_08cec0e3176003cdf395b998432f582e, .breadcrumbs.webf_41482b6771c1f449c91d864c59602ca3 .crumb:hover {             color: rgb(51, 51, 51);             text-decoration: none;         }             /* 953: .breadcrumbs .crumb:active */         .md5_23b58def11b45727d3351702515f86af, .breadcrumbs.webf_41482b6771c1f449c91d864c59602ca3 .crumb:active {         }             /* 954: .breadcrumbs .crumb > span */         .md5_8c93f37e15eb9eac24999a5c66d077c2, .breadcrumbs.webf_41482b6771c1f449c91d864c59602ca3 .crumb>span {             background: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAABdCAYAAABkWQwFAAAAYklEQVQYV7WMMQqAMBRDn1+6la7e/zDdunXqDTp0LXSLm4oIKugSkjwSSimi9y4DOIgkzt3PtNaKAROSsBijGGPoZvugexWvQM4ZgGUGHCklWQgB895jzrm756/dHltrsIEVk2pi1fyj3qwAAAAASUVORK5CYII=") repeat-x scroll 0% 0% transparent;             display: block;             line-height: 31px;             overflow: hidden;             padding-left: 25px;             text-overflow: ellipsis;         }             /* 955: .breadcrumbs .crumb:hover > span */         .md5_23b58def11b45727d3351702515f86af, .breadcrumbs.webf_41482b6771c1f449c91d864c59602ca3 .crumb:hover>span {         }             /* 956: .breadcrumbs .crumb:active > span */         .md5_23b58def11b45727d3351702515f86af, .breadcrumbs.webf_41482b6771c1f449c91d864c59602ca3 .crumb:active>span {         }             /* 957: .breadcrumbs > li:first-child */         .md5_1880e495acd6b351b512f4392be7a3df, .breadcrumbs.webf_41482b6771c1f449c91d864c59602ca3>li:first-child {             margin-left: 0px;         }             /* 958: .breadcrumbs .crumb--first > span */         .md5_503c5582d156e927fcf23256d6f25388, .breadcrumbs.webf_41482b6771c1f449c91d864c59602ca3 .crumb--first>span {             padding-left: 4px;             margin-left: 4px;         }             /* 959: .breadcrumbs .crumb--first:before */         .md5_978d6eb5950b5ea455a9eb06497d942d, .breadcrumbs.webf_41482b6771c1f449c91d864c59602ca3 .crumb--first:before {             background-position: 0px 0px;             background-repeat: no-repeat;             content: "";             height: 31px;             position: absolute;             width: 4px;         }             /* 960: .breadcrumbs .crumb--first:hover:before */         .md5_23b58def11b45727d3351702515f86af, .breadcrumbs.webf_41482b6771c1f449c91d864c59602ca3 .crumb--first:hover:before {         }             /* 961: .breadcrumbs .crumb--first:active:before */         .md5_23b58def11b45727d3351702515f86af, .breadcrumbs.webf_41482b6771c1f449c91d864c59602ca3 .crumb--first:active:before {         }             /* 962: .breadcrumbs .crumb, .breadcrumbs .crumb--first:before */         .md5_4cfbd9f68153131334f9ef760f2d871d, .breadcrumbs.webf_41482b6771c1f449c91d864c59602ca3 .crumb,.breadcrumbs.webf_41482b6771c1f449c91d864c59602ca3 .crumb--first:before {             background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAA8AAAC4CAYAAAA8GbaHAAAFgklEQVR42u2Zy0tcVxzHb1+TpE07pKUNJSDdBQqhQkGaXXfmD8iiZBuaRQiUBpQsqoV25dq1FYvgiBAZmIoO4nsT8Dk6Os7o+H47js9RF+X0+xV/Zco4M+d372II9cInYOLn3PM795zfOecXZ3Z2tgyEQAYYDZTDm5ub5vj42JycnKignDo6OlKLIu+XTj48PGTMWq5lV/LBwYHB59JyLbuW8bm0XMuuZDwGDWgpsZxKp9Ou5dDa2pqRBrTyPRAE2272Ktc4xhjXOJ2dnV8NDg7+NTQ0lAFGgzMwMNC1uLhoMpmMOTs7U+HgrXtIo2pR5APseO5lvvn09FTLWy0jjfJTaXmrZSQzN0epUst43JxJSi0jb7vZ3EstI+G72SX/lzKTfnpvb8/dFtvT0xNOJBImlUrp5dra2q+7u7u70INd9V6F5ya4Az4Hd5U4PuB3JQ8PD5eBEMgAo4FyeGxszMRiMROPx1VQTnG0V1dXzfr6ugrK+8vLy2Zra8vs7OyouJDxVvkLvbyysmK2t7e1lFiWAVPjXV5aWjIod6jxLvMEuLGxoca7vLCwwLmqxrucTCYNrgpqvMvz8/Ncz2q8y3NzcwaLQ413mTkMi0ONd5lpFItDi3c55UUORSIRw2+N1aWC8j0QBNvqvcoY93iTLy/SG6AW+FQyD9082XCJQQ4Bn0q+RBpo1Mm5Dbx0I/OIRPkcVBSVrzrBcxODnAR+tUyYHSG/1soCUw0beOFKxg1A4i9Xy4RbKeQY8CvknPgDObLlZYQJjw08cyXjTE45Ax5oZcINXeK/rZWJTN9XWllGPgH87mKWb07Z8iYjo/2UkkrmNJV1rpF5cKEYAbdU8u7uLsV9cF8EW5m7AuXHeVdVkTjrC67nK25qshDeAJ9GlhSUAmX8BVuZE4EbGOVHVtkzS5SJUGUjisybiowsRP1eFQVPNCIp4RaLS/TfqBus9/f3/xoIBG7gZ8cWh2VXrleexXB776ivr7+pkgU20NfX9ycu1u+oZcLR7+3trbJpwJHqqcAtBv9wHg6HH2plwhXF+Bebm5s/VcuEx2h8gRC6/65WJhL/T/nidwpVVLlYGD/+0+BbrUx4rmb884xfKxOJv43dV8uEdRTUkJ5nN+DYlmaZhiFkEH+5ViZMGIw/3tDQ4FfLhLcC1NB+YfcdRXlW3pysrq7+kpPHWmZKxro/xZL9njU01ZtnZmZMW1vbz5A+Au8hZrvNndO0q6srAOkT8D5FKxm3es7vmcrKSsbpY3dtZO7NjPO4rq7uO4lTRJB/l+RmMDU1ZVpbW39knLI0rWROx46Ojj+y47SRuRAY51hFRcUXEqeNLCkoXVNT8012nEVlTv6JiQnT1NT0A6QPJc68MgdGdkpOhGAw+Bukj2UiFJV5jODItre3/w7JDz6wydv8g4MTb2lpea4RCZ+74DN2VSOKfAP4GCNFR/NcXqQ3QC3wAccGI8eKaDRqRkZG2EgI+KxljjRPQawZoNrMBhpt5IuY+X15gufxn6Wt0dFRNvDSWs5qgAmO8jmoKCozNwm4vTAEThg2kAR+W5kwATBLco6zgdcqmd3HIDJ+GcAXeeUrarjsvpy3Jf5ya5mw+9xeJycn2UAM+HPkQrVcjj6z5/j4OBsIaGS+nd1nWpLv/+w/cpF6LgeP3WeioJwBD6xlwu7zBoDSl8R/+0K2rOty8rD7soBeaWTGLl1PAL/Dx7auy0nDmOWb28rssnzvpxT/lYvUcznSslAac5JBAZFvlDgj4FaOXKCWy8M7xX1wn4KtnL0sH+fNYVfUcDkhZIDqgWMt843T09MU3wBfQZm/LOCN3NQppkAZcGxlTj9Zfo+AYyszb8vEr7JO+lzsTDccWRFtZdmrouAJcDTyP5aBBLoXa4rHAAAAAElFTkSuQmCC");         }             /* 1551: * html .gsc-inline-block */         .md5_e61f229ff58986f555acabd534cbfdc0, *.webf_41482b6771c1f449c91d864c59602ca3 html .gsc-inline-block {             display: inline;         }             /* -1 */         .md5_3ff44d9a24fdcef9e3fb9f71c1926e29, .webf_41482b6771c1f449c91d864c59602ca3 {             font: normal normal 400 13px/18.2px "Helvetica Neue","HelveticaNeue",Helvetica,sans-serif;             width: 528px;             height: 35.1px;         }     </style>     <ul class="breadcrumbs webf_41482b6771c1f449c91d864c59602ca3">     </ul> </template> <template id="Breadcrumb">     <li style="z-index:2"><a href="" style="width: auto;"><span title=""></span></a></li> </template>

This Web Fragment has been generated using WebfBug. Note that all the css rule selectors have been isolated and they work only inside the Web Component. They will not affect other html nodes. Here you can find the Breadcrumbs (and some other Google widgets) implementation using Web Fragments. This code is wtitten using the x-tag library.

Here you can find a test page for small UI library I’m building for Web Components¬†take a look if you want to know how I’m implementing it.