What do you want to learn?
Leverged
jhuang@tampa.cgsinc.com
Skip to main content
Pluralsight uses cookies.Learn more about your privacy
A Practical Guide to Vanilla Web Components
by Leon Revill
Web Components are set to change how you build front-end web applications. This course teaches you how to use the Web Components specifications to create encapsulated and reusable UI components which can be used with almost any framework or library.
Start CourseBookmarkAdd to Channel
Table of contents
Description
Transcript
Exercise files
Discussion
Learning Check
Recommended
Course Overview
Course Overview
Hello, and welcome to Pluralsight. I'm Leon Revill, web architect specializing in front-end technologies based around JavaScript. I am so excited to be able to deliver my course, A Practical Guide to Vanilla Web Components, to help you prepare for the future of web development. Web components solve fundamental problems that web developers have been putting up with since the dawn of the web. These problems include style encapsulation, reusability, interruptability, and the big one, framework lock-in. This course is for web developers who want to get ready for the next big shift in front-end web development. In the course, I'll introduce you to the web components specifications and their superpowers. We'll also create two high-quality web components which will demonstrate the specifications in detail. By the end of the course, you'll be able to write top quality encapsulated UI components which can be used in nearly any front-end technology stack. They'll be reusable, extensible, and themeable, and I'll also show you how to make them production-ready by preparing them for cross-platform use, including legacy browsers such as IE 11. Web components have been a long time coming to the web platform, and they're finally here. With libraries, frameworks, and even entire conferences dedicated to web components, now is the perfect time to dive deep into this exciting new technology to discover the possibilities they'll open for you and the applications you create.
Web Component Fundamentals
Introduction to Web Components
Hello, my name is Leon Revill, and welcome to the Web Components Fundamental module, and A Practical Guide to Vanilla Web Components. In this module, you'll learn all about the building blocks of a web component, which are four web standards. You will look at each of the standards in detail, and get stuck into some code to learn how use each of them. This module will build the perfect foundation for learning how to build high quality web components. Before we jump into the web components specifications, let's take a short moment to clarify exactly what a web component is. When we build web applications, there are three fundamental building blocks. These are HTML, CSS, and JavaScript. HTML allows us to define this trip to other web pages, HTML is the markup. CSS allows us to control the visual aspects of the markup, and JavaScript allows us to add logic, and extended functionality to our web apps. With the four standards that make up the web component specification, we are able to bring these three technologies together seamlessly, and are able to finally build truly encapsulated and easily reusable components for the web. This course is specifically about vanilla web components. But what exactly are vanilla web components? There are already many web component libraries that build upon the native web component specifications. A few of the more popular libraries are Polymer, Skate JS, and x-tag. Many of these libraries not only provide an obstruction layer from the native web component implementation, they often also add features. Polymer, for example, provides data binding capabilities. With obstruction and additional features, comes some down sides, though. With your components relying on these libraries, you will lose some portability and interruptability. This course is about vanilla web components. This means using the web components specifications as designed with no reliance on any third-party libraries or frameworks, just vanilla JavaScript. You might think that being able to build modularized code in web applications is nothing new, and you'd be right. However, until now, it's not been possible to create truly encapsulated, self-contained components. So it's true we've been writing components for the web for years, in various different forms. One of the first methods was jQuery plugins. You could write a reusable set of jQuery code, which other people could include into their apps and reuse. Then there came the type of components provided by monolithic JavaScript frameworks like directives in Angular JS, and more recently, there are JavaScript class-based components in framework such as Angular2 and React. So what do web components offer in addition to these approaches? Let's take jQuery plugins as an example. First of all, you would include the framework or library. In this example, that's jQuery. Then you would include your component or plugin. What about styling? You probably need to include a CSS library, and then a specific styling for the plugin. That's a lot of dependencies and prerequisites just to get an animated button into your app. Furthermore, it was often the case, that if you included additional plugins, you would get conflicts. CSS from one plugin may inadvertently affect the other, and you'd end up with broken pages or worse. This is because there is no way to encapsulate style for individual components other than carefully selected class names. Because web components allow developers to combine multiple technologies, and the important fact that web components rely on nothing more than the web standards, there are no third-party dependencies and less prerequisites. This means that web components are more portable and reusable. One of the most important features of web components is the ability to create sub-DOM tree exclusively for a single web component. This sub-DOM tree will not inherit style from the appliation, and equally, component styling will not leak to the outer application, solving the conflict issues, which plagues other component eco systms. To summarize, the benefits web components provide over other component implementations. Web components give a style encapsulation through the ability to create a sub-DOM tree, which solves all kinds of conflict issues in our apps. Because web components only rely on browser features, there are few dependencies and prerequisites. Web components are not tied into any particular library or framework, and can be used in conjunction with any library or framework as long as it uses the DOM. Because of this agnostic nature, we can more easily migrate and upgrade frameworks and libraries, giving longer lives to the components in our apps. Web components give us these capabilities by bringing together four web standards. These web standards are custom elements, shadow DOM, HTML templates, and HTML imports. Each of these standards provides web components with a different capability. They are almost like web component superpowers. This module will guide you through using each of these web standards outside of the web component eco system, because it is important to understand how each functions independently.
What Are Native DOM Elements?
Web components are just DOM elements that are created by the component author. Using the custom element specification, it's possible to create your own custom elements, define their tag and behavior. With your custom DOM elements, you can add properties, methods, and functionality beyond what native DOM elements can provide. Custom elements also give you the capability to extend native DOM elements and enhance them, but also extend other custom elements. In this demo, we're going to take a look at the anatomy of a typical DOM element so that it's easy to understand what a DOM element is doing when rendered in the browser. I'll then show you how easy it is to create a custom element using the custom element specification. The custom elements API provide some lifecycle callback methods that developers can use to add interesting functionality to their custom elements. I'll show you what they are and how to use them. We'll then take a look at how to extend custom elements, and finally how we can extend native DOM elements and build on top of them. To understand custom elements, we first need to understand how native DOM elements work. To demonstrate, I've create a simple web page with nothing more than a paragraph tag. As web developers, we are familiar with the DOM and understand that we can interact with the DOM using JavaScript. In this example, we have a simple paragraph element, which we can interact with via JavaScript. Like all DOM elements, it has methods and properties. Some properties are specific to certain set of elements, like a line on the paragraph element, and some properties and methods are more generic and available on all DOM elements, like offsetTop and clientWidth. But where to these properties and methods come from? Where are they defined? DOM elements are just JavaScript objects which are dir to normal JavaScript prototype inheritance, which we can demonstrate by outputting the paragraph tags, methods and properties, using console.dir. Here we can see all the methods and properties available. If we look at this elements prototype, we can see that its the HTML paragraph element interface. This is where the align property is defined. If we then take a look inside the HTML paragraph interface, we can see that it inherits the HTML element interface, which all DOM elements ultimately inherit. This is where the more generic properties and methods are defined, such as clientWidth and offsetTop. When we create a custom element, we are basically defining our own interface with it's own set of properties and methods, which we can interface in exactly the same way as we would interface with a native DOM element.
Custom Elements API and Lifecycle Callback Methods
Now we have a little more understanding of what makes a DOM element. Let's create our own DOM element. Here, I have a very simple project with a single HTML page. To define the prototype for our custom element, we can use ES2015 class syntax. We must at least extend the HTML element interface, which will give us all the typical properties and methods a DOM element is required to have without us having to do any work. We'll also define a constructor, where we must always super to construct the parent. In this case, that's the HTML element interface. In here, I'll simply add a console.log, so we can see that it's working. Okay, so now we've created the class, which defines our custom element. We need to tell the browser about it, and specify a HTML tag. To do this, we use a custom elements API. Using the defined method, we first provide the tag name, which must have a hyphen, and then the element's prototype, which is the class we have just created. Now the browser will know how to construct any DOM elements with the tag name of MyCustomElement. Let's add this element to the body so we can see it in action. Now when we render this in the browser, we can see that the constructor is called, thanks to the console.log we added earlier. We just created a very simple custom element with nothing other than a constructor. The constructor is executed when an instance of the custom element is created. In addition to the constructor, the custom elements API defines some lifecycle callback methods, which a developer can use to execute code at certain times in the element's lifecycle. I will now show you how to use the three most useful of these callbacks. The first callback method is the connected callback, which is executed when the element is added to the DOM. I'll put a descriptive console.log in here, so we'll be able to see when this method is executed. Next is the attribute change callback method, which allows us to subscribe to changes of certain attributes on the element instance. To configure what attributes we want to subscribe to, we must specify the names of this attributes, using the static observed attributes get method, by providing an array of these names. For this example, we'll only subscribe to changes for the demo attribute. I will also add a descriptive console.log here, so we'll be able to see when this method is executed, and see that we can react to attribute changes. Last is the disconnected callback, which is executed when the element is removed from the DOM. You typically use this method to do some clean up, for example, canceling any JavaScript intervals you have running. To make it easier to illustrate these callbacks working, instead of adding a static DOM element, let's replace it with some simple JavaScript code. Here you can see, I'm creating a new instance of MyCustomElement using document.createElement. I am then adding it to the body three seconds later. If we then go and load this web page in a browser, the first thing that we're going to see is the My Custom Element constructed message, because we first of all created the element. Then three seconds later, we see that the element has been added to the DOM. Then if we go ahead and modify the demo attribute, which is one of the attributes we have subscribed to, you can see that we can react to those changes. And finally, if we then remove the element from the DOM, we can see that console output, so we can see the queries that react when the element is being removed from the DOM. We now know what these callback methods are and when they're executed, but as we move through the course, we'll look when we would use then, and how they will help us build awesome web components.
Extending Custom Elements
Next up is extending custom elements. To demonstrate this, I created another simple project. We've got a single custom element called Division Element, which has a single custom method called divideByTen, where you can specify a value, and it's basically going to divide that value by ten and output it to the console. I've also added the new division element to the body. If we go ahead and open this simple project in the browser, we won't see anything rendered, because our custom element doesn't have any content. But what we can do is we can go and select it. Once we've selected it, we'll be able to call the custom method we've just created, called divideByTen. Can select using the querySelector and specifying the custom tag name, and then just in the same way as we would call a native method, we can call our custom method. Here we're specifying 10, 10 divided by 10, is obviously going to give us one. So what about extending a custom element then. Let's do that by extending our Division Element. Let's create another element called MultiplyElement, which instead of extending HTML element, it's going to extend Division Element. Here, instead of the divideByTen, we'll have it multiplyByTen. Same as the other method, you will be able to specify value, take that value and multiply it instead of divide by ten. Again, we need to register this element with the browser, specifying a different tag name, and also specifying the different interface. We'll also add our new custom element to the DOM so it can interact with it in the same way that we did before. If we open these changes in the browser, we can go ahead and select our new custom element in the same way that we did before, specifying it's custom tag name. Once we've done that, we can see that that we can call the new custom method we've added, which is multiplyByTen, giving us the expected value of 100, but in addition, because we've extended the division element, we now also have the divideByTen method, which is also giving us the expected output. This has demonstrated how easy it is to extend custom elements and inherit methods and properties. But what about all the native elements, which are already available in the DOM? How can we extend and build on those, and add functionality, which can be useful in our apps? This is what we're going to demonstrate next.
Extending Native Elements
We started again with another very simple project. The first thing to notice about this simple project though, is that we've had to include a polyfill. At the time of writing this video course, the part of the custom element specification, which allows us to extend native DOM elements, isn't implemented in any browsers, and that also includes Chrome. So we've had to include the document ready to element polyfill, which will allow us to use its functionality. The document ready to element polyfill can be found on Github at the following URL. Also in this project we've got a link, which takes the user to Google, and I've also got a class skeleton, which we'll use to create another customer element. In this particular example, I don't want to use it to be able to easily and accidentally navigate away from the page. So, let's say we wanted the user to be asked if they were sure they want to navigate to Google when they click on that link. We can easily do that by extending the native anchor element, using a new custom element. As you can see, I've already started creating the custom element. I'm using a classical custom anchor, which instead of extending HTML element, I'm extending the HTML anchor element. First to be able to display a message to the user, asking them if they're sure they want to navigate to that link, we're going to need to add an EventListener to the element so we can detect when they've actually clicked on it. The first thing we'll do in this event handler, is use the preventDefault method on the event, because we don't want the user to be automatically navigated to that link. We want to ask them if they're sure first. To actually ask them, we're just going to use a normal JavaScript confirm message, and if the result of that is true, and not false, then we'll take the HREF from the element they've clicked on, and then navigate the browser to that link. If they choose cancel on the confirm message, it's not going to do anything, because we've used the preventDefault method. When we register this custom element with the browser, as before, we'll specify the tag name, and we'll also specify the class as is, interface as prototype, but because we're extending a native element, we need to also specify which tag we're actually extending. In this case, that's, A, for anchor. With our custom element created and defined, we now need to tell the Google link to implement its extension. And to do that we use the, is, attribute. We then just simply specify the name of our custom element as the value for that attribute. Now if we open this in the browser and click on the link, what's going to happen, is we're going to see the confirm message, if the user chooses cancel, it's not going to go anywhere, and if they choose Okay, it's going to navigate directly to Google. The custom elements API is one of the more important parts of the web components specification because custom elements are essentially the entry point to the component. It's how a user will consume the components you create. As part of the short introduction to the custom elements API, you now understand the basic anatomy of a typical DOM element, you know what lifecycle callback methods are provided by the spec, and you've seen how to extend both custom and native DOM elements. This demo was designed to be an introduction to custom elements, giving you all the knowledge you'll need when it comes to web components. The demo didn't include all there is to know about custom elements. So for those of you who are hungry to learn more, I'd recommend you check out Eric Bidelman's article.
Using the Shadow DOM
With the introduction to custom elements complete, it's now time to move on to the next specification, which is probably the most powerful, and that's the Shadow DOM. Using the Shadow DOM API, it's possible to create a sub-DOM tree for a specific element. That means that this element can contain and maintain its own encapsulated DOM tree. These sub-DOM trees do not inherit style from the main application, giving developers complete control over how content within these sub-trees look. When we create a sub-DOM tree for an element, there are two types available to us. There is open, which means we will have complete control over the sub-DOM tree, and closed, where it is not possible to manipulate the sub-DOM tree after it has been created. You will probably never use the closed type of Shadow DOM. In this demo, I will show you how you can use the Shadow DOM API to create a sub-DOM tree for an element. I'll then show you how you can add content to the sub-DOM tree, and finally, I'll show you the Shadow DOM in action, so you can see how its encapsulation works. To demonstrate this, we're once again, going to use a simple project. In this project we've got a couple of paragraph tags, we've got a div tag with an ID of simple.div, and we've also got a bit of basic styling, setting the paragraph tag's color to red. What we're going to do in this demo is, we're first of all going to select the simple.div element, and we're going to attach a shadow root, or a Shadow DOM to that particular element. So we'll just select it, using the document.getElementById method, and then to actually create a shadow group for this element, we just use the attachShadow method. Then in here we'll provide an object with a mode property with a value of open. As previously mentioned, open mode will allow us to manipulate the contents of the shadow root freely. Opening this project in the browser, we can see how the shadow root is depicted for this element in dev tools. For the shadow root created for this element, we can now go ahead and add some content to it, and get the benefits of its encapsulation. By using the shadow root property, we can get direct access to the shadow we just attached to this element, and we can interact with that in the normal way that we would with the element, such as using an append child or in a HTML. Here, I've just added another paragraph element, and added some new styles, setting the paragraph color to blue. Once again, opening the project in the browser, we can see that this new content has been added. Straightaway we can see the benefits of the Shadow DOM encapsulation. Because we added our new paragraph tag to the Shadow DOM, it isn't affected by the starting of the outer application, you should otherwise set it's color to red. Instead, it's color is blue, because we added some additional styling to the Shadow DOM, which it will still inherit because it's part of the encapsulated context. Additionally the other paragraph elements in the page are not affected by this new styling because the styling inside the Shadow DOM will not leak out into the outer application. These encapsulation capabilities are so significant because we can utilize this functionality in our web components. With this functionality, we can be sure that when users add our components to their applications, the look and feel of our component isn't going to be broken by any styling they added in their application. Equally, we can be confident that anything we add in our component isn't going to affect their application. Shadow DOM is a huge topic. As part of this course, you'll learn much more about the Shadow DOM than what I've just demonstrated. For those of you who would like to read more about Shadow DOM, Eric Bidelman has written another excellent article on this topic.
Using HTML Templates
With light shed on the Shadow DOM, it's now time to move on to the next spec. HTML templates. Templates give the developer a way of creating segments of HTML that can be easily reused throughout an application. This is done by a browser's verdict on the contents of the template tag, but not rendering it, thus allowing the developer to render the contents as required, perhaps with a specific set of data, like in a list, for example. In this demo, I'll show you how to create an HTML template, using a template element, and how to pass and render the contents of the template using JavaScript. And we'll do all this in context of a simple real-world example. The concept of HTML templates is really easy to get into grips with. In this project, we're going to take a short list of movies and add them to a list. But instead of defining the HTML in markup for each of the list items using JavaScript, we're going to use a template and define the HTML there. That means it can be easily reused. On lines eight through thirteen, you can see this is where we defined our template. It's got an ID list item, and inside there's a list element, there's a header one element, which is where we're going to the movie title for each list item, and then we've got paragraph element as well, which is where we're going to display the plot. I've already gone ahead and defined list of movies, and also selected the templates and the list. So now we can go ahead and add the movies to the list. We can simply just loop through each of the items, which is the list of movies, and for each of these items, we're going to clone the contents of the template. And we can do that by using the document.importNode method. We specify the template and specify the content property, which is the actual contents of the template. The second parameter, true, is whether we want to do a deep clone, which will grab all of the children of the template, which we do. Then once we've got that clone, we can update the clone contents. So we an update the H1 first, with the movie title, and then we're going to update the paragraph tag with the plot of the movie, then finally, we'll just use the append child method to add it to the list. It's a simple as that. Opening this project in the browser, we can see that everything's gone according to plan. Each of the movies has been successfully added to the list. If we expand the template in the DOM explorer, we can see that it still contains the content we originally added, but it hasn't been rendered by the browser. That makes it really easy for us to reuse this content as in when we want to, not when the browser decides to render it, like it does with other DOM elements. This is obviously, a very simple example, and templates are more useful when you've got more complex markup that you want to reuse to write your application. Regarding web components, you can uses HTML templates to define the markup and structure of your component. And this is something I'll be demonstrating later on in the course. HTML templates are pretty simple, which you've probably gathered from this quick demo. Again, though, this demo gives you everything you'll need in relation to web components. If you want to learn more, there's a great article on HTML5rocks you should check out.
Using HTML Imports
We can now move on to the final web component specification, HTML Imports. HTML imports allow you to include all the HTML documents into a webpage using the link element, in a very similar way that you would include a CSS file into your page. HTML imports aren't restricted to markup. The document import can include CSS and JavaScript, as well. I often use them to load HTML templates into my apps. HTML imports were designed to make including web components into applications really easy. Unfortunately, though, the HTML import specification is a contentious one. As of creating this course, some browser vendors have made the decision not to implement this spec, and wait for the outcome of the JavaScript module specification. The good news, though, is that the HTML imports feature is very easily polyfilled. In this short demo, I'll show you how to use HTML imports to import a HTML document, and also how to access its contents from JavaScript. In this short demo, all we're going to do is take the contents notice.html, and include them into index.html. The content in notice.html adds some style and some simple markup, which says, "Web Components are awesome." To actually include notice.html into index.html, we use the link element, just like we would if we wanted to include a style sheet. The difference here though, is instead of rel stylesheet, we use rel import, but then we still specify the HREF, pointing to notice.html. We'll also give it an ID called notice, so we can reference it later. So that will actually import the contents of notice.html, and make them available to us. But to actually get out the contents, we need to write a little bit of JavaScript. The first thing to do is select the link element, using the ID we've just added. So that would be just using the element by ID method, specifying notice as the ID, which will give us a reference to the element. Then we need to get the contents of that import. And we can simply do that by doing link.import, which will give us those contents, and then we can actually query the contents of that import, just like we would any other DOM tree. So, in this case, we're going to go and grab the notice frame, and then we're going to add the entirety of the notice frame to the body of index.html. Opening the simple example in the browser, we can see the contents of notice.html has been added to the main document. Then in the network tab, we can see the network request has been made to notice.html, importing the contents. The HTML Import specification is another simple, but useful, spec. This quick demo didn't demonstrate it's entire capabilities, so if you want to learn more, there is another article by Eric Bidelman you should read. That concludes the Web Component Fundamentals module in a practical guide to vanilla web components. This module has defined exactly what a web component is, and has identified that web components solve fundamental problems, such as style encapsulation. We've discussed the web standards that make up the web component specification, and learned how to use these standards. Custom elements to create your own DOM elements with added functionality, Shadow DOM to create encapsulated sub-DOM trees for elements. HTML Templates for creating HTML snippets that can be easily reused, and HTML Imports for importing for importing HTML documents and their contents. Most importantly, this module has built a strong learning foundation, so we can move on and get stuck into some really interesting web components content.
The Basic Anatomy of a Web Component
Introduction
Hi and welcome to the Basic Anatomy of a Web Component module in A Practical Guide To Vanilla Book component. In the Web Component Fundamentals module I walked you through all of the work component specifications to give you a solid understanding of how these individual technologies work. In this module, we'll bring all of them together to show you how to build the basic structure of a web component. More specifically, in this module you'll learn the two different methods for writing a component and why you would choose one method over the other. I'll introduce you to some best practices for creating custom elements for your components. Dome element and tributes are a large part of the work component ecosystem, so we will look at how best to handle them. We'll look at how to define and use properties of a work component and finally, I'll show you how to use the Shadow Dom with your components. Before you start writing web components, you'll first need to decide how you want to write them. There are currently two common methods, each of which have their own advantages or disadvantages which is what we're going to cover now.
Method 1: Pure JavaScript
In this demo, I'll show you how to write a basic web component skeleton using the Pure JavaScript method. We'll then look at how someone using a component written with this method would add it to their application. Once you are aware of what the Pure JavaScript method looks like, we'll then go through the pros and cons of writing your web components in this way. When writing web component using the Pure JavaScript method, you're basically writing your component as a single JavaScript file. The component definition and the template will all be written with JavaScript. To demonstrate this, I'll start by creating the ES2015 class which will define the prototype for my component custom element. Then I'll use the connecttoCall method which is executed when the element is added to the Dom. This will then stamp an initial template for that component. Because this refers to the element instance, I can then just update the innerHTML property just as you would with a typical Dom element. To make altering HTML and CSS within JavaScript a little easier, we can use the EA2015 template stream feature, which depending on your ID, should mostly provide correct syntax highlighting and IntelliSense. For this essential template, I'll add a paragraph element with the text myworkcomponent and also add some CSS to set the color to red. That's all our work component is going to do for this demo. Now we need to define the custom elements so that it can be used. To do that, we use the customElement API's define method specifying the tag name and the class we just created. That's it, a very simple web component written using the Pure JavaScript method. Obviously, this simple example doesn't harness all of the work component features. I'll show you these as we move through the course. To use this web component is as simple as including a JavaScript file and we'll do this now. With an index to html, we'll just include the MyComponent.js file and then add the custom element where we want to use it. If we open index to html within a browser, you'll see that everything works as expected. The first benefit for writing your work components in this way is that you're using methods that most web developers will be familiar with. Simple JavaScript has only a few new concepts such as template strings, custom elements and potentially the Shadow Dom. Next, as a consumer of the component, you'll be just including a JavaScript file which is a port that in all browsers and requires no polyfills. Because you're just writing JavaScript, it's easy to transpile if you need to support all the browsers. Using ES2015 template strings make writing HTML and CSS within JavaScript much easier than it would do if you're using ES5. The main disadvantage most people identify with this method is that it feels quite unnatural to write HTML and CSS within JavaScript even with template strings. It also adds a lot more lines to your components, making them feel a little bloated. Additionally, many text editors and IDEs might not provide full syntax highlighting and IntelliSense within template strings. I use Web Storm which does provide support, but once my template strings reach a certain length, all the syntax highlighting and IntelliSense is removed. This will probably get better with time though.
Method 2: HTML Import
In this short demo we will now take a look at the HTML input method. We will once again write simple work component skeleton which will include a custom element and template using the HTML input method. Once we have it created, I'll show you how a consumer would use this component as a HTML Import. Afterward, we'll look at the pros and cons of this method also. When writing a work component as a HTML import, everything is done within an HTML file. The first thing I'll do is create my component's template within a template element. I'll add a single paragraph element with the content My Web Component and add the CSS to set its color to red. With the template complete, we can now move on to create the custom element for our web component. Because we are in a HTML file, we obviously need to put our JavaScript within the familiar script tags. The first thing we're going to do is declare a constant called owner where we will store a reference to the owner document of this script. We need to know this so we can grab the template element we have just created. To access the owner document, we can simply reference document.currentscript.ownerdocument which will give us the contents of the current file. However, at the time of writing this course, the HTML input spec has only been implemented in Chrome which means to consume this component in another browser you'll need to use the HTML input Polyfill, Part of the work component .js suite of Polyfills. The workcomponents.js suite of Polyfills can be found on GitHub at the following URL. This Polyfill doesn't patch the current script property but instead offers an alternative property underscore current scripts which we'll need to use if we want this component to function with the Polyfill. We can simply add that in using an or statement. I'll now create another constant which stores a reference to the template using the standard currently selected method on the only document object to select the template we've just created. Without template ready to go, we can now create the class for our custom element. I'll call the class MyComponent which will extend the HTML element interface. Within the connecttocall button method, we can attach the template to the custom element. Firstly, we'll need to input the contents of the template using the document.input node method. Then, we can simply use this .appendChild to add the template contents as children to the MyComponent custom element. Finally, we register our custom element with a browser using the custom elements.define method specifying the element tag name and the class we've just created. To use this web component, it's just as easy as with the Pure JavaScript method, however, slightly different. Within the head of the index.HTML document, instead of including a JavaScript file, we use the link element with the role value of import to pull in the My Component.html file. Then we can just use the custom element as we would expect to. Open this up in the browser, we can see that a custom element has been imported correctly and we can see that everything is working as we would expect it to. HTML imports are just as easy to use as including a JavaScript file but they also automatically de-duplicate. That means that if you include the same component in multiple places throughout your application, it won't get included more than once. This means it's easy to insure you'll always have the component when you need it but you don't need to worry about it being included more than it should be. Ration your component templates this way is much more natural and you don't have to worry about IDE support. Unfortunately, the HTML Import specification isn't currently well supported. HTML Imports are easily polyfilled but there are some issues with the Polyfill which add unneeded complexity to your components. Finally, because the JavaScript is within an HTML file, it's hard to have it transpiled to aide and support for all the browsers. My choice method and the method I will continue to use throughout this course will be the Pure JavaScript method. These type of decisions often come down to requirements and personal preference. Anything you see in this course will be applicable to both methods as is up to you to decide which method you'd rather use. I recommend you try each method to help you make your decision.
Custom Element Best Practices
Now that you're aware of the different ways you can choose to write your components, we can move on to the specifics. Next, I'll be showing some best practices for creating your web component's custom element. In this demo we'll take a more detailed look at how the custom elements class we use to define our work components. I'll show you some tips for naming the class and element tag, on implementing custom methods, how to best define local variables, and best practices when updating your component's template. In this demo we're going to create a random quote component which will simply print out a random quote every 10 seconds. This simple example will allow me to share some best practices I've discovered while working with web components. The first thing I'll do is define our class and define the custom the element. When choosing a name for my class, I always use camel case and be as descriptive as I can while not making it too long. Then when choosing the element name, I always convert the camel case class to a hyphen telemeter string. When creating a custom element, your element tag must have a hyphen in it to distinguish it from the native elements so this works perfectly. Notice that I added another view in front of my class and element name. This is because your element name has to be unique. If we called our component simply Random Quote, it is very likely that someone else will do or has already created a component with that name. This means that our component and their component couldn't coexist on the same page. So it's important to try to keep the element name unique by adding a prefix. I've chosen RW for Rebel Web, but you'd use something else. Think of it as a kind of name space for your elements. Next I'll create the class constructor calling super as always and then I'll define some private local variables. ES2015 classes don't have the concept of declaring private variables but you can intact script over the languages but there is an unwritten rule that prefixing a variable or method with an underscore means that it's private and probably shouldn't be used externally. With that, I can create a private course array with three different quotes. A quotes variable which is going to store a reference to the Dom which will display the quote text. Know that here I also prefix it with a dollar sign. I do this with any variable that holds a reference to a dom element so it's easy to see what type of data you are dealing with. By looking at that variable, you'll know instantly that you'll have access to methods like query selector or the methods and properties at least part of the HTML element interface. Next, I'll add an interval variable which will store the interval instance used to update the quote every 10 seconds. We'll now move on to using the connectToCallback method to stamp the component with our initial templates. Here we're just displaying a title and providing an element to display the quote text. I've also added some CSS for some very basic styling. Because I'm not using the shadow dom for its encapsulation in this component, I'll need to prefix the class names to limit the chance of a conflict. With the initial template added, I'll pull the reference to the quote element out and configure the interval. The set interval function will call a random method which we haven't created yet. With the interval configured, we'll also need to initially call the random method. Now we'll move on to creating the random method. The random method is a custom method on our class. We will prefix it with an underscore identifying it as a private method and that it shouldn't be used externally. Then, all we'll do is take a random item from the quotes array and update the inner HTML of the quote element with the quote text. Notice here that we're only updating the part of the dom which needs to be changed by individually selecting the quote element. What would actually be easier for us to do is just restamp the entire template every time we want to update the quote text. This would be very inefficient now. Always remember that dom updates are expensive and to only update the parts we need to change. To finish off the twelfth component, we'll use the connecttoCallback method to get the interval that it won't continue to execute if the element has been removed from the dom. Here you can see that I've already included this component into index.html so we can go ahead and take a look at it working in the browser. Here we can see that the initial render has chosen the first random quote and updated the template. Then after 10 seconds we can see that the contents of the quote element will be updated again calling the random method from the interval. These tips and best practices I'm sharing with you are guidelines only. Don't feel you need to follow them religiously. Once you start building web components more frequently, you'll most likely discover your own style and new best practices.
Web Component Attributes
Attributes are one way your web components can receive data and publish date. In this section, I'll show you how easy it is to handle attributes on your components. For this demonstration, I'll be building on top of the random quote component we built in the previous demonstration. Just as a reminder, this element simply takes a random quote from an array and prints it in the component template every 10 seconds. In this demo I'll show you how you can make the interval configurable by using attributes and also how we can use an attribute to share which quote is currently being displayed. The first thing today is to create a custom method called SetInterval. This will give us a reusable way to update the render interval. This method will take the interval value in milliseconds. It will first cancel any variable instances using the clear interval method. Then if the specified value is greater than zero, it will configure a new interval. The same writing the code again, we can just reuse line 32 from the previous version of the component. To access the element's attributes, we can use the GetAttribute method. We'll call the setInterval method from the connecttocallback with any value from the interval attribute. This way if there is an initial interval attribute specified in a set interval, will be configured with the specified value. If we jump over to index.html and then specify an initial interview value, we can then go ahead and open this in the browser and we'll be able to see that the initial interval which is now configured is set to one second, but if we change the value at run time, it won't change the interval because we're not yet watching for those changes so let's go and do that now. If we want to observe the tribute changes, you need to specify the serveAttributes method and provide an array of the attributes we want to observe. In this case, that's just going to be the interval attribute. Next, using the attribute change callback, we can now be informed when the interval attribute value gets modified. When the interval attribute gets updated, the attribute change callback method will get executed and then we can now use the new value variable to call our set interval method with the new value. Loading this back in the browser, we can then try it out and see that it works. We can also use attributes to provide information about something happening inside the components. As an example, we can add an attribute to our component when a new quote is displayed. It is as easy as using the set attribute method. There are many reasons why you'd want to use attributes to display information about a component. Think of how native elements work and disable a tribute is used to identify import which is read only or a check attribute is used to show a check box has been checked. Back in the browser, we can see that the current index attribute gets update with the index of the quote every time it changes.
Web Component Properties
Properties are another way your components can receive data. Properties can also be used to distribute data outward. In this demo I'll show you how we can create an API of your component using properties. I'll describe the difference between properties and attributes. I'll demonstrate how you can allow data to be pushed in to your component and I'll show you how getter methods can be used to allow data inside your components to be retrieved externally. Using attributes to provide data to your components, it's perfect for simple data, but ultimately attribute values can only be strings and therefore attributes are not suitable for more complex data like objects and arrays. For this demonstration, I've prepared a poll work component which presents the question to the user and allows them to choose a single answer. Before we get started, I'll quickly walk you through how this component works. In the constructor, I've declared a few private variables and set some initial data to display. In the connecttocallback, I first set the attached variable to true so we know when the element has been added to the dom then I add the component template and store some references to the important elements. I've added an event listener to the answers list so when a user clicks an answer, it will find the index storing it in a private variable and also adds the selector class to mark the chosen answer as green. The last thing in the connectocallback is calling the render method which updates the component template for the data in the data variable. Currently, this component is just displaying some static data which in practice is pretty useless. How can we create an API so someone using this component in their app can not only provide it with a new question and answers but also programmatically select the selected answer. This is where properties come in. First and foremost we want it to be possible to dynamically add a question and a set of answers so we'll remove the static data. To define a property and a custom element, we will use setters and getters. We'll start by adding a data setter. A setter takes only a single argument which is the value being set on a property. As a rule, I always check to see if the data being provided is actually new to avoid doing needless work. If there is something new, instill the data to a private variable and then call the render method to update the template with the new data. We also want it to be possible to get up the data which is currently set and we can do that with a getter. A getter has no arguments. Here we'll simply return the value of the private data variable. Okay, we also want it to be possible to programmatically set the selected answer as well as allowing it to be selected by the user. To do this, we'll use another setter. This setter takes an index of the item to be selected. It then uses the career selected method to select that specific child from the list. It removes the selected class from any of the list items which might have it and then adds a selector class to specify an item. Finally, it then stores the selected index in a private variable. To expose the selected index, we can then use another getter to just return the value. To finish this component off, we can remove the duplicated logic from the answers event listener and just call the selected property internally. With the component open in a browser, we can now try out the properties we've just added. It's not rendered anything in the dom yet because it doesn't have any data so let's give it some. Once the data is set, we can also print it out. We can still make a selection as a user but we can also use a selector property to dynamically change it and we can also use the selector property to print out the currently selected value.
Web Component Shadow DOM
The shadow DOM is one of the most powerful features of work components. In this section, I'll show you how to actually implement the shadow DOM in the work component. Getting the encapsulation benefits of the shadow DOM in your component is very easy and in this demo, I'll show you how. I'll demonstrate how easy it is to create a subdom tree for your work component and I'll also show you how to add your component's template to this subDOM tree and also how to update it. In this demo we're going to modify the pull component we created earlier to gain the benefits of the shadow DOM and it's going to be really easy. We've already seen the shadow DOM provides style encapsulation. To demonstrate this working with the poll component, I'm firstly going to add some conflicting styles to the index.html forcing all of these elements to have a black background. Currently in the poll component we are always referring to the custom element's direct children, ie when we add the template and also when we select the question and answer elements. When we introduce the shadow DOM, we'll need to place the template in the subDOM tree and also select the elements there too. In the constructor, we'll attach a shadow route to this element making sure it's in open mode and store the reference to the shadow route in a private variable. Refreshing the component in the browser will show us a shadow group that has been added. Now all we need to do is make sure we interact with the shadow route instead of the element's direct contents. This is where we use the route variable. In the browser we can see that our template is now within the shadow route. Adding some data will also place the content within the shadow route because we updated the references to the question and answer elements. You'll also notice that our list items now have the desired background color because the shadow route of our work component won't inherit the main document styles giving us complete control over what our component looks like.
Web Component Skeleton
And that concludes this module. All of the concepts I've shown you during this module come together nicely to provide a work component skeleton which you can use as a starting point for all of your work components. It shows how to declare private variables, create a shadow route for your component, where to add an initial template, to store a reference to important elements for later use and the prefix DOM elements with a dollar sign and how to declare private methods and the best practice for selectively updating template content, how to observe certain tribute changes and react to those changes, how to create an API for your component with properties and finally, best practices for element and class naming. This module has introduced you to the two principle methods for writing work components and presented the advantages and disadvantages of each. It's provided you some best practices for creating custom elements for your work components. It's walked you through handling attributes for receiving data and advertising states. It's demonstrated how to use the component properties to distribute and receive data and finally showing you how to gain the shadow DOM's encapsulation methods within your work components but most importantly this module has solidified your understanding of the individual work component specifications and taught you how they're used together to build web components.
Creating a Star Rating Component
Introduction
Hello and welcome to the Creating a Star Rating Component module in A Practical Guide to Vanilla Web Components. In this module, I'm going to walk you through building a high quality star rating web component which will provide a practical demonstration of all the concepts you've learned through this course so far. This module will be focused on taking you through the process of building a high quality web component. A high quality web component is a web component which strives to be as functional and robust as a native element. We will be analyzing how current elements work to decide how our custom element should behave. I'll demonstrate some Shadow DOM styling capabilities to help in our component design. The star rating component will make use of multiple attributes for different purposes, so I'll show you how to handle them, and finally, our component will need to emit events to inform users when its value has changed. By the end of this module, you'll be well on your way to having an in-depth understanding of web components and how to build them. Before we get started in building our star rating component, I'm first going to demonstrate the end product. In this demo, I'll briefly show you how the component is going to look, how it's going to behave, and also its API, in terms of what properties are available. The star rating web component we're going to create is a simple component that will present the user with five stars they can choose from. As you'd expect, when a user hovers over the element, the appropriate numbers of stars will be highlighted and will allow them to make a selection. The component will also have a value attribute, allowing for an initial value to be specified. It will also have a disabled state so that it can be used to display read-only data. And finally, it will have a single-value property to provide the ability to programmatically set and retrieve its value. When building web components, it is very important to realize that there are a large number of super-performance, well-tested elements already available in browsers. Years of development has gone into some of the native elements, with millions of people using them daily. Web users are very familiar with these native elements. They'll have expectations of how certain types of elements should work. When you build the web component, you should look to native elements and take inspiration from how they work and strive to match this behavior as much as possible. This is exactly what we're going to do with our star rating component. When building a web component, try and pair it with a native element, if there is one. Take our star rating component, for example. It takes user input, so it's a type of input element. Evaluate stores as a number, so it's basically a simple number input with a fancy UI. That means that we don't have to spend lots of time trying to figure out how the star rating component should behave in subtle situations. We can learn from the native number input. Before we can do that, though, we need to understand exactly how the number input works.
The Number Input
The number input does more than just take a number from the user. In this demo, we'll discover some important answers about the number element so we can understand how to best write our star rating components. We need to learn what attributes the number element uses, does the element react to those attribute changes and when, what properties does it have, and what events does it fire and when? We can then decide if our star rating component would benefit from any of these features or behaviors, and then simply implement them. The number input implements the HTML Input Element interface, which provides a large API including a variety of properties and methods. In context of our star rating component, we are only interested in the disabled state, the value attribute, the value property, and when the change event is fired. On the face of it, this all might seem very obvious, but you might be surprised how some of this functionality subtly works. Most input elements have a value attribute. We've all probably used it, at least on a text input. The number input has one also. Setting it, we can see that it updates the input's value, and it will also keep reacting to the changes. But, once the user enters a value manually, those changes will no longer be applied. We can also see that the same behavior occurs if we set the value by the value property. Once we do that, then try to update the value by the value attribute, it won't affect the input's data. If we attempt to change event lists into the element, we'll see that it's fired when the user changes the value, as you'd most likely expect. It won't fire the change event if we update the value attribute, but slightly more oddly, it also won't fire the change event if we update the value by the value property. This is because the implementation assumes if you are setting the value programmatically, by JavaScript, you already know the value has changed. The act of learning from native elements is a process that can really enhance the quality of your web components. There is, of course, many situations where you'll be writing a complex or variable scope component which won't have a matching element, but you should try to learn from the common interface patterns implemented by native elements as much as possible. This, of course, doesn't mean you should limit the capabilities of your web component. You use the interface patterns as a foundation and build on top of them.
Creating the Foundation of the Star Rating Web Component
Now that we've got a good understanding of how we want our element to behave, we can get to work building our star rating web component. In this demo, we're going to create the very basic requirements for our star rating custom element, give it a shadow root so we can go in the Shadow DOM encapsulation benefits, we'll add an initial template, along with the basic styling for the five stars in the component template. We'll start off by creating our class called Rw Star Rating, which extends the HTML element interface. The element name, as always, will be the class name separated by hyphens and there we can just specify the class we just created. The constructor will create a private variable. Storing a reference to the shadow root will create for this element, and also some private variables for important template element references. Next up, within the connector callback, we'll stamp an initial template. We have a container with a top and bottom section. The top section holds the gold stars, which will become visible when a value is set by setting an appropriate width on this element. The bottom section holds the gray stars underneath. We add a data set value for each of the bottom stars so when one is selected by the user, it's easy to get the selected value. You may have also noticed that these values are in the wrong order. I'll explain this shortly. Moving on to the CSS, within a shadow root, we can use the host selector, which refers to the host of the shadow root. In this case, that's our star rating element. This means we can apply styles directly to the custom element from within the shadow root. Next, we add some basic styling to position the container and its sections correctly. And finally we'll add some magic, which will give us the correct hover effect for the gray stars in the bottom element. From the template, we've seen that we have a top section of stars, which will sit above the bottom section using a zed index. When a value is set, either by the value attribute, the value property, or via a user selection, the value will be multiplied by 10 then doubled to give us the correct width percentage to be applied to the top section. The bottom section is a little trickier, though. What we want to happen is, when the user hovers over the stars, current star and all preceding stars are highlighted. The problem, though, is that there is no way in CSS to select the preceding elements, but there is a way to select the succeeding elements. If we did that, though, the opposite stars would get highlighted. To fix that, we can just change the element direction from left to right to right to left. Here you can see the CSS, which selects the succeeding elements and the CSS property which changes the direction of the bottom stars. This is why we need to put the data set values in the opposite order. I've already added the web component index to HTML so we can take a look at it in the browser. We can see that it's rendering the five stars in line with the text, and when we hover, we get the effect we're hoping for.
Implementing the Disabled Mode
Next, we're going to implement the disabled mode for our star rating web component. We'll use some CSS to prevent hover and change the user's cursor. We'll utilize a private variable to keep track of whether the element is disabled or not, and we'll watch the attribute changes so we know when the element should be put in disabled mode. The CSS we add first resets the cursor so the user doesn't think they're able to click the element in disabled mode. Then it just cancels out the display and none property in line 43 because we want the top layer of stars to be visible on hover for disabled mode, and it also cancels out the hover effect of the bottom stars. Using the host selector, along with square brackets, we can specify the disable to distribute, which means these CSS properties will only get applied when our custom element has a disabled attribute. Next, we'll create a private disabled variable. In the connector call button method, we'll set this variable to true if the disabled attribute is present. Otherwise, it'll be false. We also need to observe changes of the disabled attribute, so it can update this variable if the disabled attribute is added or removed at one time. We do that by specifying that we want to observe the disabled attribute, and then use the attribute change callback to update the private variable accordingly. I'm using a switch statement here because we'll be adding an additional attribute observation later. If we go into index to HTML, and add a disabled attribute to our component, in the browser we can see that the hover effect has been disabled and that our cursor reflects the disabled state. Additionally, thanks to the attribute change callback, if we remove the disabled attribute at run time, the component falls back to its default behavior.
Adding a Value Property
Next up is the ability to programmatically set and get the component value. We'll use a private value variable to hold the data. We'll create setter and getter methods to set and retrieve the data. And we'll create render method to update the template to reflect the new value. We'll start out by declaring the private value variable, which will hold the element in its current value. Then, in the connector callback, we'll create a reference to the top element, so we can update its width within the render method. We do that by simply using the query selector method on the reference to the shadow root to pull out the element with the top class. The render method is a custom class method which simply takes the value, multiplies it by ten, then doubles it to give us the required width percentage. And using the style property, you can set the width of the top element accordingly. The setter method, as always, will first check to see if the value being provided is actually different to the current value. If it is, update the current value and then call the render method to update the template. The getter method simply returns the current value. Opening our star rating component in the browser by selecting it from the DOM and then using the value property, we can set its value and it'll be reflected in the template. Also, we can use the value property to get the current value.
Allowing a User Selection
Now we've implemented the render method. We can let the user make a selection by clicking one of the stars. This will then update the component value based on the star they selected. And based on what we learned about how the number input works, we should also dispatch a changed event. We're going to need to listen for clicks on the bottom stars, so first, we'll need to create a reference to the bottom element. Again, we do that by using the query select method on the reference to the shadow root, selecting the class. Then, by adding a click event handle to that element, we'll know when one of the stars has been clicked. We don't want to perform any action if the element is in disabled mode, so we'll first ensure that the disabled value isn't set to true. Then we'll make sure that the element has a data set value, i.e., this is one of the bottom star elements. Finally, if the current value is not the same as the just selected value, we'll then dispatch a change event and also we'll set the value property to the new selected value. In the browser, we will first attach an event listener for the change event on our star rating component. Then when we make a selection, we can see that that event is getting fired and that selection is being made. The final implementation detail is that if we make the same selection twice, the change event isn't fired because the value's not actually changed.
Handling the Value Attribute
The final part to our star rating component is the ability to add an initial value using the value attribute. We need to observe changes to this attribute, then update the component's value based on this data, and to match the behavior of the number input. We should only do this if the value hasn't already been set by the user or by the value property. The first thing to do is check the initial value of the value attribute within the connector callback. If there is a value, then update the private variable and call the render method. There's a reason why we don't just use the value property here, which you'll see shortly. Before we go any further, we need to declare a new private variable called touched. We'll then set this variable to true within the value setter so that if a user makes the selection or a value set by the value property, we'll know they want to ignore any value attribute changes. This is why we don't use the value property in the connector callback for the value attribute. Next, we add an additional string to the return value of the observer distributes method, specifying we want to observe changes for the value attribute. And then we add another case to the switch statement. We're at the touched value is false, we'll take the attribute value and update the private variable once again calling the render method. And that completes our star rating web component. To see this last bit of functionality working, if we set a value attribute on our component, and open it in the browser, we can see that the three stars is reflected on initial render. If we change the value attribute, we can see it gets updated. And finally, once we've made a user selection, the value attribute changes are no longer reflected in the UI. This module walked you through building a high quality web component step by step, taught you how to analyze native elements to better understand how custom elements should be implemented, demonstrated how to harness some of the Shadow DOM styling capabilities with the host selector, provided insight in how to handle multiple attributes in a single component, and showed you how to dispatch events from within a web component. The web component built in this module is a practical example of a web component which will be very useful in real projects and should give you more confidence to experiment with the OMA components using techniques learned in this module.
Creating a Configurable Slide out Menu Component
Introduction
Hello, and welcome to the Creating a Configurable Slide Out Menu Component module in A Practical Guide to Vanilla Web Components. In this module, we're going to build a more complex component, and I'll demonstrate some additional features of the Shadow DOM. In this module, I'll be walking you through building a slide-out navigation menu component. We're going to add some basic built-in color themes that the component user can easily apply, and we'll do this by using CSS, so we don't have to observe attribute changes. We'll use a property to set the menu to open or closed, and I'll introduce you to another feature of the Shadow DOM called slotting, which allows users to project their own content into the Shadow Root of your Web Component. Before we get started in building our slide-out menu component, I'm going to show you the completed component in action. I'll show you how it works, what configuration options it has, and how it is constructed in terms of the DOM structure and important bits of CSS. The slide-out menu component has a custom element of rw-slide-menu. It slides out from the left-hand side. The menu itself has a title and content section. It also has a backdrop which, when clicked, closes the menu. The backdrop can be disabled using the backdrop attribute, setting its value to false, which then allows for items behind the menu to be clicked. A theme can also be applied to the menu, using the theme attribute. There is a blue and red theme available. The component template consists of a frame element, which covers the entire browser viewport. the frame element contains nav element, which is positioned off the left-hand side of the screen, using the translateX transform. When the open property on the component is set to true, an open class is added to the frame element, which removes the translateX transform. The nav element has a will-change transform property, which promotes the nav element to a separate layer. This prevents the browser from redrawing the rest of the page when the menu is slid in or out. This will increase the animation performance, especially on low-cost or older mobile devices. Because the frame element is an overlay to the entire browser viewport, we need to stop that element from being clickable, otherwise a user wouldn't be able to interact with the application behind the frame element when the slide-out menu is closed. We can do this using the pointer-events CSS property, and setting it to none. Then when the menu's opened, we can set this property back to auto, which will then allow the user to interact with the menu and backdrop.
Creating the Foundation of the Slide out Menu Web Component
Now we've got a rough idea of what we're aiming to build, we can get started. In this demo, we'll create the component class and define the custom element, we'll add the initial template, and we'll also add the template CSS, which will include the slide-in animation. I've already created the component class and defined the custom element. Next, I'll create the class constructor, which will create the Shadow Root, and some private variables which we'll use later. The first private variable will hold a reference to the frame element, and we'll also create a private variable which will hold the open state. In the connectedCallback method, we'll stamp an initial template into the Shadow Root. In here, we'll add the frame element, which covers the entire browser viewport, then a nav element with a container class, next, the title, which contains a content area and a close button, and finally, the menu content area. Inside here, just for now, I'm going to add some basic static content. Moving on to the CSS, we'll first add some for the frame element. This CSS ensures the frame covers the entire viewport, sets pointer-events to none, so the user can click through it, sets a high z-index, so it's above all other elements, and configures a background-color animation used for the backdrop. Next is the nav element, which has the container class. We use the translateX transform to slide if off the screen by default, and use the transition to ensure the sliding effect is animated. The will-change transform property is added here to promote this element to a new layer. The CSS for the title and close button is pretty self-explanatory. And finally, we add some CSS for when the frame has an open class, which resets the pointer-events, allowing the frame and nav element to be clicked, and also removes the transform on the nav element, sliding it back in. Now we have the very basic foundation to our slide-in menu component. Let's try it out. We currently have no way of opening and closing our menu, but we can simulate it by adding the open class to the frame element. When we do so, we can see that the nav element slides in from the left. Here, we can see the title, close button, and the pretty ugly content area. If we then remove the open class, we can see that the nav element is then transitioned back off the screen.
Styling the :host
In the Creating A Star Rating Component module of this course, we touched on using the :host selector to apply style to our component when it had a certain attribute. We are going to use this technique again in this component, but I also wanted to take this opportunity to introduce some more capabilities of the :host selector, which you may find useful in your own component implementations. To recap, we want the user to be able to specify some baked-in themes by the theme attribute. Here, we can see the red theme has been chosen. When a user changes the attribute value to blue, the blue theme should be used. Many developers would jump straight to the idea of using the powerful attributeChangedCallback to observe changes of the theme attributes, and then do something like add a class to the frame element where they could apply some CSS. The best JavaScript developers will try to avoid writing JavaScript as much as possible. When it comes to applying styling based on host element attributes, classes, or state, such as hover, there is a much easier approach. Within the Shadow Root of an element, you're able to use the :host selector to select the Shadow Root host, and are also able to specify some other selection criteria. For example, a specific attribute using square brackets, along with a specified value. In the component we built in the Creating A Star Rating Component module, we applied some style when the host element had the disabled attribute. In addition to attributes, you can also specify a class. Note the absence of the square brackets. We can also use other pseudo selectors, such as hover. There is also another selector called :host-context, which in this example will apply the green theme if the custom element, or any parent elements, have the class green. With these various selection capabilities, it is often possible to avoid writing sometimes complicated JavaScript, and it is important to always consider if it's possible through CSS first. This will not only make your Web Components simpler, it can also improve performance, especially for elements which are used many times on a page.
Adding Themes to the Slide out Menu Component
We now have the foundation of our slide-out menu components, and can start adding some features. In this demo, we're going to implement the red and blue theme for the component, which will be selectable by the theme attribute. And we'll do this using only CSS, thanks to the :host selector. The only styling we're going to apply based on the theme, to start with, is going to be the title. So we'll first use the :host selector, I'm going to use square brackets so we know we're dealing with attributes, then specify the theme attributes and the value we'd like to specify, in this case red. Then specifying the title class, we can specify the appropriate background-color, and color. I'll also do exactly the same thing, except for the blue theme. Opening the project in the browser, we can try out these themes. We're going to need to simulate opening the menu again using the open class. Once we've done that, we can then specify the theme attributes on the custom element, starting with red, we can see it gets applied, and then changing the value to blue, we can see that instantly the blue theme gets applied. This nicely demonstrates that using the :host selector can really simplify adding theming and styling to your component, instead of falling back to a more complicated JavaScript implementation.
Allowing the Menu to Be Opened and Closed
Now it's time to add the main feature of the slide-out menu component, which is the ability to be able to open and close it. In this demo, we'll define the open property, which will allow the menu to be toggled open and closed, we'll ensure the menu is closed when the user clicks the backdrop or the close button, and we'll add the CSS which allows the user to disable the backdrop. We're going to use a property to allow the user to set the open state of our component. We've already created the private variable to hold the Boolean state. Now let's just add a setter and a getter for the open property. The setter will take the value and determine if it's a Boolean value. This ensures that if the user passes in something other than a Boolean, it will just default to false and not throw any errors. As always, if the specified value isn't different to the value we currently have, just return. We then update the local open variable, and then call the render method, which we will create shortly. The getter simply returns the private open variable. There are two ways to close the menu: via the close button, and by clicking the backdrop if it's enabled. What you could do here is add an eventListener to both the frame element, i.e. the backdrop, and the close button. But I like to use as few eventListeners as possible. We first need to store a reference to the frame element, which we can do in the connectedCallback. Then add a single eventListener to the frame element, which will use the event object to determine if the clicked element has a close dataset item. If it does, we set the property to false. To make this work, we need to add the close dataset property to all elements which can close the menu. In this case, that's the frame, i.e. the backdrop, and the close button. Next, we'll add the render method. First, let's ensure the frame element is available, i.e. we've been connected to the DOM. If open is true, then we need to add the open class to the frame element. Then we'll dispatch a CustomEvent called menu-opened, informing any users that menu's just been opened. If open is false, then we'll do the opposite. We'll remove the open class, and dispatch the men-closed event. To finish off, we'll add the CSS which allows the user to disable the backdrop of our attribute. Once again, we use the :host selector, specifying the attributes and the value. Before we open this in the browser to see the functionality we've just added, I first want to show you what I've added to index.html to help us demonstrate this functionality. The first thing I've done is add a few buttons, one to open the menu and one to close the menu. Both of these buttons are all within a division element with a class of controls. Then I add a click eventListener to the controls element, so I can determine which button has been pressed. If the Open Menu button has been pressed, I then set the menu.open property to true. If it's the Close button, then I set the open property to false. I've also added some eventListens to the menu itself, one for menu-opened and one for menu-closed. Now I can go ahead and open this in the browser, and see if it's all working. So the first thing we're going to do is press on the Open Menu button, which you can see is working as expected. And then the console will be able to see that the menu-open has been fired. If I then close the menu with the Close button, we can see that that's worked, and the menu-closed event has been fired. If I open the menu again, and click on the backdrop, we can see that that also closes the menu. Now let's demonstrate that we're actually able to disable the backdrop using the backdrop attribute with a value of false. Now when we open the menu, we can see that the backdrop isn't present, allowing us to click through to the buttons and close the menu.
Allowing Users to Provide Component Content with Slotting
We've nearly completed the slide-out menu component. To finish it, we're going to use another feature of the Shadow DOM. This feature is called slotting. Throughout this course, you will have come to learn that Web Components are all about encapsulation, particularly in relation to the Shadow DOM, the idea being that a Web Component shouldn't affect the outer application, and the outer application shouldn't affect the contents of the component. But what about components which require content from the user to be useful? A modal, for example, is useless unless the user is able to specify the content they want to display inside the modal component. Our slide-out menu is exactly the same. We want our users to be able to specify the content they want inside the menu. This could be any HTML, an image, some text, a few buttons, or a list. Slotting allows us to do exactly this. As a component user, we can add content as child elements to the Web Component, and then as a component author, using slotting, we can choose where this content is placed within the Shadow Root of our Web Component. The slotting feature projects the content into the Shadow DOM, making the elements appear to be within the Shadow Root of the component, but are actually kept within the Light DOM of the component. This means that the usual styling restrictions of the Shadow DOM don't apply to the slotted content. This is a huge benefit, because component users will be able to style the content they've provided to the component, just like they would style any other DOM element which was outside of a Shadow Root. In this demo, we'll add slots to the component template for both the menu content and the menu title, we'll also add some default style for slotted content, and finally we'll specify some fallback text for the title slot, to ensure there's always a title for the menu. The first thing we're going to do is replace the static content we added earlier with a slot element. We're also going to give this element a class, so we can style the slotted content later. The slot element allows the component author to decide where the slotted content is placed with inside the template in the Shadow Root. Next, in index.html, we'll add some content that we want slotted inside the component. As we've been building a menu, it only seems appropriate that we'll add some anchor tags. I'll simply add three, each with the text One, Two, and Three. Next, we'll open it up in the browser, and we can see with the menu open that all three anchor tags have been placed in the appropriate place. In the element explorer, when we drill down to where the slotted element is, we can see the projection at work. When we click on each of these slotted elements, we can see that they are revealed in the associated place in the Light DOM. You've probably noticed that the slotted content looks pretty ugly in its current form. We know that the user can style this content however they like, because it's still in the Light DOM. But as component authors, we can still add some default styles, so that the slotted content looks good straight out of the box. Within the Shadow DOM, we have a new CSS pseudo selector available to us, which will allow us to add style specifically to slotted content. We can specify tag names. In this example, we are styling all slotted anchor elements whose parent has the class content-slot. In the code we've just written, this is the slot element. We can use other pseudo selectors, such as the hover selector, and we can also specify classes. This all seems great, and is incredibly useful, but there are limitations. You can only style top-level items. If you were to do something like this, it just wouldn't work. It's also important to understand that user-specified style will always take precedence over slotted style which is added inside the Shadow DOM. In this example, on the left we have our slotted content style added within the Shadow DOM of our Web Component, and on the right we have the user-specified style for the slotted content they added to our component. The user-specified styling would always win. Now we're aware of the slotted pseudo selector, we can use it to add some default style to the slotted anchor elements. The styling I've added here is self-explanatory. It ensures the anchor elements are displayed on top of each other, have a bottom border, and also change color when hovered. We also need to update the hover color based on the theme, so I'll do that now. First, we ensure the hover color is red for the red theme, and then blue for the blue theme. Back in the browser, when we open the menu, we can see that it looks much better. The default styles have been applied to the slotted anchor elements. When we swap to the red theme, we can see the hover style updates, and the same for the blue theme. To further illustrate my point I made earlier about user-specified styles taking precedence over the slotted styling within the Shadow DOM, if I add some styling in the document which changes the anchor color to red, we'll be able to see that this styling wins over the styling we added inside the component. Currently, in our slide-out menu Web Component, we have a single slot element. Any child elements will be projected at this location in the Shadow DOM. But what about having multiple places in your templates that you want to be able to project content into? With our slide-out menu component, we have that exact problem. Not only do we want users to be able to add menu content, we also want them to be able to specify the markup for the menu title. This is where name slots come in. To do this, it's actually really easy. If we replace our menu text with another slot element, except this time we give it a name, give it something meaningful, so in this case title, then what we can also do with slots is we can provide some default content, so that if a user doesn't specify a value, we have a reasonable default. We can demonstrate that now by going straight to the browser, and what we'll be able to see is that the menu text is still displayed, even though we haven't added any content to be slotted. And we can see inside the slot element, it's defaulted to the default text. Now if we go back into our project, and open up the index page, and actually add some slotted content there. So we'll add a h3, which is appropriate for a title. And the way we specify for it to go in the additional slot element is by giving it a slot attribute, and then specifying the name we chose. In this case, that's the title. Back in the browser, we'll be able to see that the menu now holds our new slotted content. And that concludes our slide-out menu Web Component. In this module, we've built another Web Component step-by-step, which is our slide-out menu component. We used the :host selector to add style to the component based on its attributes. In this case, that was with theming. We learnt about the :host-context selector. You were introduced to the slotting capability of the Shadow DOM, which included default slots and name slots. This allows component users to project their own content into the Shadow DOM of your Web Components, while still giving the component author complete control over where that content goes. You were also shown how to add default style to slotted content using the ::slotted selector.
Styling Web Components
An Introduction to CSS Custom Properties
Hello, and welcome to the Styling Web Components module in A Practical Guide to Vanilla Web Components. The course so far has demonstrated multiple times the power of using the shadow DOM to encapsulate style within a component, to prevent the outer application from affecting the component internals. With this capability though comes some interesting problems. A component user needs to be able to adapt the look and feel of a component so it fits into their application. This module focuses on how component authors can create a styling API for their components, providing flexibility to their users while still leaving control in the hands of the author. In this module, I'll give you an introduction to CCS Custom Properties, which forms the basis of the web component styling capabilities. We'll create a styling API for the star rating web component we created earlier in the course. We'll also apply what we learn in this module to the slide out menu component we created previously so users can easily change various visuals in the component, and I'll show you the possibilities of using CSS @apply mixins to make theming your components even easier. Before we start thinking about creating the styling APIs for our components, I'm going to give you a brief introduction to CSS custom properties and also give you an overview of which browsers support this feature. To demonstrate CSS custom properties, I'm firstly going to need something to use them on. For this, I'll quickly create four squares in the center of a page. I'll add some CSS to remove any margin and padding from the body, and then some CSS to position the container and boxes in the center of the screen. Refreshing the browser, we can see the four empty squares on the page. Now we're going to use CSS custom properties to color them in. If you're familiar with CSS variables in a CSS pre-processor, like SASS, then you're already familiar with CSS custom properties. Except they are a native CSS feature with slightly different syntax. The principle is simple. At a high level, we can declare some custom properties depicted by the double hyphen, and also assign some values to them. In this example, I'm going to declare some colors I'd like to use throughout the application. We have primary color, secondary color, and border color. With those custom properties defined, let's use them to color in the boxes. Let's make one and four the same. To use one of the custom properties, we use the var keyword, specifying which custom properties to use. Here, we'll set the background and border color accordingly. Refreshing the browser, we can see that it's working. Next, I'll use the same principle to color the boxes two and three, this time using the secondary color and gray for the border. Another refresh, we can see each box is colored in. The idea here is that we can declare easily reusable variables which are not limited to color. You can declare variables for font families, text sizes, padding, all sorts of things. We can also use CSS custom properties to pierce the shadow DOM, which is exactly what we'll be doing with our components. If you're not already familiar with CSS custom properties, then you might be surprised by the wide support already available. Using caniuse.com, we can see which browsers already support the feature. As you might have expected, Chrome has got full support. Safari 10 has got support for this feature, as well as iOS Safari, the Android browser support set, and so does Chrome for Android. Edge is currently in development and it shouldn't be much longer until we have this feature natively, and there are polyfills available if you want to use CSS custom properties with Internet Explorer.
Adding a Styling API to the Star Rating Web Component
Now you're familiar with CSS custom properties, I can show you how we can use them in our web components to allow users to style parts of the template even within the shadow DOM. In this demo, we'll create a styling API so users can choose the color of the stars in their default, hover, and selected states. I'll show you how you're able to specify fallback values for properties using CSS custom properties. And we'll take a look at how a user can specify CSS custom properties for the component to use. Before we get started, just a quick refresher on what the star rating component is all about. It's a very simple component that allows you to make a selection of one to five stars. When the stars are hovered over, they change color to show the potential selection, and when selected, they keep the gold color. The first thing we're going to do is allow the user to change the default color of the stars, which is currently gray. To do this, we need to change the value of the color property on line 37 to be a custom property instead of a static hex value. Remember that the custom properties you create are basically the styling API to your component. To ensure they have a descriptive name, I'll choose star-default-color. The problem now, though, is that if a user doesn't provide a custom property for the component to use, then the stars' color will fall back to the browser default, which is not the color we want. To fix this, we can provide a second parameter to the var function, which is the fallback value. Now if we refresh, we can see that the gray color is retained instead of the browser default, which would have been black. Next, we'll do the same for the selected color, again choosing a descriptive name for the custom property and using the current hex value as the fallback value. A refresh shows us everything is still using the default colors. The last custom property to add is for the hover state. Using the same process again, let's just quickly add it. Another refresh, and it's still all looking good. That's how easy it is to expose certain properties, which will allow the user to provide new values for them, even from outside the shadow DOM. But how exactly does a user do this? If we head over to index.html, we can do that now. To provide values to these custom properties, we just need to declare them on a parent of the component. We could add them to body, but instead, assuming that these custom properties will only be applicable to the star rating web component, we'll add them to the host element using the element name as a selector. We'll first declare a new value for the default color. Refreshing the browser, we can see that the stars are now a darker gray. Next, let's make the selected color red. Another refresh, and we can see that when selected, the stars change to red. And finally, if we set the hover color to blue, we can see that each color has now been modified using the CSS custom properties.
Adding a Styling API to the Slide out Menu Web Component
The star rating component is a very simple component with little that a user would want to change. The slide out menu component is much more complicated. In this demo, we'll create a styling API so that users can customize the various colors the menu uses, change the size of the title, also change the width of the menu, which includes the ability for it to be 100% wide. Once again, just a quick reminder of what the slide out menu component does. It's a menu component which slides out from the left, and via slotting allows the user to specify a title and some content. Because slotted content is projected into the shadow root but captured in the light DOM, then the user has full styling capabilities of this content. This means that we won't be creating CSS custom properties to style this content. Instead, we'll be adding properties for styling shadow DOM internals. The first custom property we will add is for the menu width. Currently, this is set to a static 80%, and I'm sure users would like to change this based on their application. Instead of the static width, then, we'll declare a custom property called menu width, remembering to give it a descriptive name and a default value of 80%. This ensures that if a user doesn't specify a value for this custom property, we fall back to the originally intended width. We'll also need to remove the max width property, otherwise the user won't be able to set anything greater than 400 pixels. With a quick refresh, we'll just double check that the default value is still being used, and we can see that it is. Moving into index.html, let's add a value for this property and see if it works. I'll set 100% width so we can cover the entire page with the menu. A quick refresh, and now when the menu opens, we can see the width is as we specified. Okay, so the next property we're going to add is going to be for the title size. This will basically allow the user to specify the font size of the title. We'll give it a descriptive name again, title-size, and specifying the default value for it to fall back to. We'll just double check that the default fallback is still being used, once again it is. And then we can move into index.html to specify a new value. In this case, let's make it smaller than the default. So let's use 0.8em. Save that and refresh the browser again, and we can see that the whole title has been resized. So we already have themes available for this component. We've got a red theme and we've got a blue theme. But these themes are obviously quite limited, so let's move on to adding some more custom properties that allow the user to have more control over the colors used inside the menu. To do this, we'll use the same process once more, specifying custom properties for the values we want the user to be able to change, starting with the background color of the title and then moving on to the actual color for the title, using the original gray as the default color. Moving into index.html, we'll first remove the theme attribute from the element, and then we'll specify some new values for these two colors. We'll add a wonderful orange for the color, and then to make things even look more spectacular, we'll use a nice red background. Then when we refresh the browser, we should see that our new colors have been applied. The final thing we're going to do is allow the user to be able to specify the background color for the entire menu. To do that, once again, we'll just add another custom property, and using the originally-intended color as the default. Double check that everything's working, and then finally in index.html, specify a new value, and there's only one color that's going to make the menu look even better, and that's a nice green. A refresh to see if it's all working, and we can see the worst-looking menu you've ever seen in your life. There is a reason why I've chosen such awful colors for this demonstration, and that is to illustrate a point. That point is that if you give the user this kind of flexibility, they will very easily be able to make your component look ugly, as opposed to the theme approach, which is where we can provide a predefined set of colors which we know work well together.
An Introduction to the CSS @apply Rule
CSS custom properties are a great way to give flexibility to component users, but you might have noticed in the last demonstration that the number of custom properties in the title element were starting to grow. This means that a user has to specify many individual custom properties. If we wanted to give users complete control over the title element, we'd have to add even more CSS custom properties, and even then, they wouldn't be able to change the properties we hadn't defined. They couldn't, for example, just set the height. The CSS @apply rule introduces the concept of CSS mixins that allow component authors to pass even more control to users. The caveat to this feature, though, is that at the time of creating this video course, the CSS @apply feature is only available in Chrome behind a flag. There is a polyfill for this feature, but unfortunately, to get this to work, you'll probably need to structurally change how you've written your components. Before we move on to a demonstration of the CSS @apply feature, I'm first going to show you how to enable this flag in Chrome so you can try it out for yourself. To enable a flag in Chrome, just open up the browser and navigate to chrome://flags. Here, you'll be presented with a series of flags you can enable and disable. The flag we want to enable to allow us to use the CSS @apply feature is the experimental web platform features. Choose Enable, and then choose to relaunch the browser. Once relaunched, we'll be able to see that this flag has now been enabled. Now you've enabled the feature, this short demonstration will show you how to use a CSS mixin within the menu component, and demonstrate how a user can modify multiple CSS properties with this feature. So the first thing we're going to do is get rid of that awful green and also remove the other title-specific custom properties. And then we can see, other than the width, we've got the default set of colors and styles. Then if we move into the component itself, the first thing we're going to do in here is we're going to remove all of the custom properties and stick with the default values. Again, just a quick check to make sure that everything's working as planned, and it is. Now we're going to say that we can apply a CSS mixin if one has been provided, and we simply use @apply and then specify the name, just like we would a custom property. Then, inside index.html, just as we would specify a custom property, we can specify the mixin, but this time it's an object as opposed to a single value, and in here we can provide a variety of property and values. We'll start with the background color, specifying a dark gray. Next, we'll specify the color, and we'll set it to a lighter gray. And finally, we'll change the font size, and we'll set that to 2em. In the browser, we can see that each of these properties has been applied, and we've not had to specify multiple individual custom properties to do so. Instead, we provide a mixin which allow us to choose which CSS properties we want to modify instead of being constrained by the custom properties the component author has added to their component. That concludes this module, which has been all about styling web components. In this module, you've been given an introduction to styling web components, which allows users to customize the look and feel of your components to fit with their application theme. You've learnt about CSS custom properties and how to use them in your components, which included understanding which browsers support them. We've created a styling API for both the star rating web component and the slide out menu component, and you were given a brief introduction to the capabilities of the CSS @apply feature.
Production Ready Web Components
Browser Support for the Web Component Specifications
Hello, and welcome to the Production Ready Web Components module in A Practical Guide to Vanilla Web Components. The course so far, I've shown you how to build high-quality web components for the cutting-edge browsers. Unfortunately, though, we don't yet live in a world where all browsers implement the same features at the same time, and most businesses still need to support Internet Explorer 11. To combat this, this module will show you how you can get your components running in all of the major browsers, including IE11. To do this, we're going to need to understand which browsers support which parts of the Web Component specifications, use Babel to transpile our ES2015 code so we'll run in browsers which don't yet support ES2015, identify the different polyfills available, and decide which ones to use, understand the limitations of polyfilling the ShadowDOM, and use the ShadyCSS polyfill to simulate style encapsulation and to provide support for CSS Custom Properties and @apply Mixins in multiple browsers. Before we get to making our web components production-ready, let's first understand which browsers support which features of the Web Component specifications. Custom Elements V1 is currently available in Chrome and the next release of Safari. In both cases, support for customized built-in elements, which allows the developer to extend native elements, is not currently supported. This information is regularly updated, so I urge you to visit the link to caniuse.com to ensure you're looking at the most recent information. ShadowDOM support is a little better, where current releases of Chrome and Safari including iOS Safari have support for this feature. HTML Templates support is excellent, where only IE11 is lacking here. And finally, HTML Imports are only really supported in Chrome-based browsers, which might not be an issue for you if you chose to write your web components using the pure JavaScript method. The polyfill story for HTML imports is pretty good, though, so even if you are using them with your web components, you shouldn't encounter many problems. Once again, I urge you to follow the link to caniuse.com to get the most current information. The first part of making Web Components production-ready is to convert the component code to a version of JavaScript which is compatible with all major browsers, including IE11. Because the Custom Elements V1 specification requires the use of classes, you will, at the very least, be using ES2015 features in part. Classes and class features such as super are not available in ES5 and therefore will not work in older browsers such as IE11. Additionally, you're also very likely to use any more ES2015 features to make writing web components easier and to future-proof your code. If we look back at any of the web components created in this course, we've made heavy use of template strings and arrow functions, all of which will cause syntax errors in a browser such as IE11. To get around this, we need to convert our ES2015 code back ES5 so that it's going to run in older browsers. To do this, we can use a tool called Babel, which makes transpiling our ES2015 code to ES5 incredibly easy.
Transpiling the Star Rating Web Component
In this demo, we're going to go through that process. We're first going to use NPM to install Babel, and then we're going to use Babel to convert the ES2015 code in our Star Rating web component to ES5, ready for browsers such as IE11. This demonstration assumes you already have NPM and Node.js installed on your system. Within the Star Rating Web Component project, we're going to open a terminal window within that directory and run NPM in it. Once we've accepted all of the default options, we will now have a package.json file. Next, back in the terminal, we're going to use NPM to install Babel CLI, which is the CLI tools for Babel, which will do all of the work for us. But we're also need to install a Babel preset, which bundles all of the functionality we need to use to transpile our ES2015 code. While that's installing, we'll create a dist folder, which is where our transpile code will go. Once installed, we need to go back into the package.json file. Here, we can see the devDependencies now listed. So, we don't want to have to type the Babel TLI commands every time we want to transpile. So we'll quickly create an NPM script. Instead of test, we'll name it transpile, and then specify the command we want to run. We want to use Babel with the ES2015 preset we installed, and we want to transpile rw-star-rating.js, and we want to place the output with the same filename within the dist folder. To actually run that script, we now go back to the terminal, chose NPM, run, and the name of our script, which is transpile. Once that script is finished executing, we can go into the dist folder and see the new file has been created with the transpiled ES2015 code. Looking through the file, we can see a lot of similarities, but we can see that all of the ES2015 features have been stripped out, and it's been completely converted to ES5.
An Introduction to the Polyfills
You've seen the current lack of support for the Web Components specifications in many browsers, and there is an obviously need to fill these holes with a set of polyfills. Because Web Components rely on multiple new specifications, the number and complexity of these polyfills required to get Web Components running in all major browsers is often daunting. I've seen many developers get so frustrated when it comes to getting their component working across the different platforms that they give up and deem Web Components not yet ready. This section of the module is designed so this doesn't happen to you. I'll explain the different polyfills involved and what they do and show you step by step how to use them with the Star Rating web component we created earlier in this course. The Web Components organization on GitHub consists of Web Component advocates from Google and the Polymer Project and other web component enthuse developers. It is there where you can find a complete suite of polyfills for the Web Components specifications. These polyfills are split up into different repositories and have their own dependencies depending on which platforms you are targeting. To make things easier, if you go into the webcomponentsjs repository under the Web Components organization, you'll find a series of bundles which each of the polyfills have been built and concatenated into single, easy-to-consume JavaScript files. There's a variety of bundles available, depending on which platforms you need to target. Please be sure that you visit the link provided on your screen as this information will change as browsers implement the Web Components specifications. Instead of having to decide which bundle to use, webcomponentsjs also ships with two loaders. These loaders will then feature detect and only include the polyfills which are required. Because the loaders execute before your code, they cannot know if you are trying to use ES5 or ES2015 code. This means you must choose the Web Components loader if you're shipping only ES2015 code and Web Components ES5 loader if you are only shipping ES5 code, which is a requirement for IE11 support. Thanks to the creators of these polyfills, as web component developers, we don't really need to understand exactly how these polyfills work, but it is useful to understand what each does and if there are any specific issues or caveats. Very quickly, I'll go through each of the more important polyfills and describe them in a little more detail. The HTML Imports polyfill allows us to use the HTML Imports feature, which allows the inclusion of HTML documents into another in browsers which don't already support this feature. There are no longer any major caveats with this polyfill, but please follow the link to get full documentation on the limitations. The Custom Elements polyfill gives us the Custom Elements version one API. There's a list of known bugs and limitations listed at the URL provided. The main one to note is that if you're using Custom Elements with ES5 code, which is a requirement for IE11 support, you'll need to include a native-shim, which will allow the Custom Elements version one API to be used without JavaScript classes. The good news, though, is that the Web Components ES5 loader will automatically include this for us. You might have expected to see a ShadowDOM polyfill and not a Shady DOM polyfill. The story here is that the Shadow Dom is an incredibly complex feature that is very difficult to polyfill. There's another, older, more complete ShadowDOM polyfill, but it was very intrusive and had performance implications. The document for the Shady DOM polyfill states that it's less correct but less intrusive and faster than the ShadowDOM polyfill. The Shady DOM and the ShadowDOM polyfills allow you to use many ShadowDOM features such as slotting and allows you to call the various API methods, but you cannot create a truly encapsulated shadow route for your web components. We can use the ShadyCSS polyfill to simulate ShadowDOM style encapsulation, though, and this will also allow us to use CSS Custom Properties and @apply Mixins in browsers which don't support those features. The caveat to this polyfill is that you'll need to slightly modify how you write your components and also add some additional JavaScript to your application to gain these benefits. The Template polyfill simply allows us to use a template elements in browsers which don't support it. We'll need this element if we want to make use of the ShadyCSS polyfill. The generic platform polyfills are dependencies of many of the specification polyfills, as they make use of some of the features which are not available in all browsers. Array-from, for example, is not available in IE11. That might have been a lot of information to take in all in one go, but don't worry. Remember that the hard work has already been done for us. We get all of that, just by including a single JavaScript file.
Adding the Polyfills to the Star Rating Web Component Project
Now you're aware of the different polyfills and what they do, I'm now going to show you, step by step, how to use them with a typical web component by adding them to the Star Rating Web Component project. We're first going to install webcomponentsjs into the project via Bower. Then we'll add webcomponents-es5-loader to the project to load the required polyfills, and finally, we'll ensure that the ES5 version of the web component is being used. First off, in index.html, we'll remove the reference to the ES2015 version of the component. Next, in the terminal, we're going to use the CLI tool Bower to install the webcomponentsjs suite of polyfills. Here, I'm specifying a version to install, but you'll want to ensure you have the latest. Just make sure it's the same or later version than the one I'm using here. Once that is completed, we should be able to see all the files under the Bower components directory. We can now just include the Web Components ES5 loader file into the head of index.html. Because the loader detects what features a current browser has and loads the required polyfills asynchronously, we need to change how we load the rw-star-rating js file. We don't want to load it until the Web Component polyfills have been fully loaded. To do this, we can hook into the WebComponentsReady event, which the polyfills will dispatch on window when they finish loading. In here, we can create a script element whose source will be the ES5 version of the component from the dist folder. We'll then append the script element to the head of the document so the file will get downloaded and be ready to use. Now we've got an ES5 of our Star Rating web component, and we've ensured that all the required polyfills are going to get loaded. We're not far off getting this component working in all major browsers, including IE11.
Using the ShadyCSS Polyfill
The Star Rating component is now at the point where all we have left to do is utilize the ShadyCSS polyfill to simulate the ShadowDOM style encapsulation for browsers without native ShadowDOM support, and also shim CSS Custom Properties for browsers which don't support that. In this demo, we'll first include the final dependency, which is the CustomStylInterface. The ShadyCSS polyfill assumes you've used the template element for your component templates, so we'll need to update the Star Rating web component to do so, and we'll use the prepareTemplate and styleElement methods to make sure the style encapsulation happens and to ensure the CSS Custom Properties in the component template aren't broken in IE11, and then last of all, we'll use the addCustomStyle method on the customStyleInterface to be sure the CSS Custom Properties we added in index.html to style the component work across all the different browsers. The first thing we're going to do is create a new JavaScript file within the dist folder called custom-style-interface.min.js. Then, heading over to the ShadyCSS repository, we're going to grab the raw contents of the custom-style-interface file to take a copy of it. Back in our project, we'll add the contents to the new file we just created, and then we'll simply include this after the Web Components ES5 loader in index.html. Before we move on, let's take a quick look at the state of things in IE11. Because we're now using ES5 and I've included the required polyfills, the component is now running, and there are no errors in the console. However, when we try and interact with the component, we can see that none of the styling is working in IE11. This is because we used CSS Custom Properties to style the different states of the stars, and IE11 doesn't support CSS Custom Properties. To combat this, we can use the ShadyCSS polyfill to ensure not only simulator style encapsulation but also support for CSS Custom Properties. To do this, we first need to update our components so its template is within a template element. If you've chosen to use the HTML import method of creating web components, then this step will already be done for you. To make this change in the constructor of the component, we need to create a new template element. We'll then take the component template from the connectedCallback and use inner HTML to place the template within the template element. Then, still within the constructor, we'll check to see if the ShadyCSS polyfill is present. If it is, we use the prepareTemplate method, specify the template element we just created, and the tag name of our component. We'll then store the imported contents of the prepareTemplate to a local private variable for use in the connectedCallback. In the connectedCallback, we'll once again check for the presence of the ShadyCSS polyfill, and if it is present, we'll use the styleElement method specifying the current instance with this. Finally, we need to insert prepared template contents into the shadow route, so we replace line 99 with this.root.appendChild, specifying the template. That's all we need to do to our component, so let's run the transpile script and then take a look at it in IE11. In IE11, we can now see that all the default styles have been used, and if we look in the DOM Explorer and explain the head element, we can see how the ShadyCSS polyfill simulates the style encapsulation, which is by prefixing our component styles with a unique scope class based on the element tag name. So the default styles within the component template are all working now, but the CSS Custom Properties we added to index.html are still being ignored. We can make them work by using the added customStyleInterface to apply some ShadyCSS magic to other style elements in our project if we first add a class to the style element where we defined our custom properties. This is so we can select the specific style element. Then, at the bottom of the page, we can check for the presence of the customStyleInterface, and then call the addCustomStyle method, providing a reference to the star element we want to use. Back in Internet Explorer, we can now see that the CSS Custom Properties are being used to style the web component. Just for added completeness, we can also see that the component is still working perfectly in Chrome and also working perfectly fine in Firefox. And finally, everything's functioning in Microsoft Edge also. It's obvious from this module that to get your web components running across all major platforms with the use of these polyfills, there is some additional work to be done. Hopefully, though, this module has made it clearer as to what is needed to get your component production-ready and working in browsers such as IE11. It's important to realize that we are closer than ever to having the Web Components specifications natively supported in Safari, Edge, and Firefox, and it's only a matter of time before many businesses will no longer need to support IE11, completely removing the need for many of these polyfills. That concludes this module, which has given you understanding of which browsers currently support the different Web Component specifications, an overview of the high quality polyfills provided by the Web Components community, a step-by-step demonstration of including and using these polyfills, but most importantly, this module should have given you the information you need to get your components working in all major browsers, giving you more scope to use them in production for many projects.
Course author
Leon Revill
Leon Revill is a Google Developer Expert for web technologies, co-host on @TheWebPlatform Podcast, blogger, author and web architect specialising in JavaScript and Web Platform features.
Course info
LevelIntermediate
Rating
(32)
My rating
Duration2h 10m
Released11 Apr 2017
Share course