Intro to Web Components - Full Walkthrough

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments

I'm ten minutes in, and so far web components just feel like out of date React... React seems to have improved the development experience by leaps and bounds with functional components compared to class based ones. I'm not entirely sure I understand why anyone would use web components as they're shown here. They seem extraordinarily clunky and verbose for something you can accomplish much easier with the sole negative being a compiler is required.

👍︎︎ 1 👤︎︎ u/ArryPotta 📅︎︎ Aug 16 2021 🗫︎ replies
Captions
for this video i'll introduce you to web components with real examples i share with you everything you need to know to get started with web components on your web projects as well as many tricks i use building websites with web components technology support the channel by liking or commenting on this video subscribe so you don't miss out on anything now let's dive in [Music] web components is not a single thing but rather our collection of technology that allows you to create reusable elements with encapsulated logic and style it is a native way to build components much like you would with react angular or vue.js but without the baggage of a large bundle some of these apis include custom elements registry shadow dom template and slot tags you may also hear about the usage of es6 modules as well as mutation observer when dealing with web components i wrote a more detailed article on web components you can read later especially if you want to learn about when or if you should use it in your projects link in the description to walk you through everything i created a project where i only have an index.html file as you can see here then on the right a app.js file that is already linked in the page to create a component you must create a class which must extend html element or a specific html native element type once you have your class you must register your new custom html element aka component you do that by using the custom elements objects and calling the define method which you specify the name of your html tag and then the class you created to control your html tag like i mentioned before you can also create a component that extends a native html element for example i can create my button which extends the native html button element whenever you extend a native element you must provide a third argument to the defined method call this option is to tell it that this element you created extends a specific native html element one thing you should know is that extending a native html element is one of the least supported type of web components you can create by the time of this recording apple has refused to implement this capability on safari browsers and you must have a specific other browser's versions for this to work as well you can always use web components polyfills or use web components libraries like lit and stencils that already includes these polyfills for you to kick started easily if i go ahead and use our blog post component in the page nothing renders as we haven't defined the content of this tag and we can see it here on the page when i inspect it one very important detail about tag names is that they must follow the normal tag naming rules and must be at least two were connected by a dash so if i rename my tag to post and check the browser we can see an error saying that post is not a valid custom element name usually people use a prefix or a namespace for all the tags for example i could use bfs as my namespace and this would be called bfs post where bfs is short for before semicolon that would help distinct my tags from other third-party ones for example google material designs components use matte m-a-t short for material now we can define the content of our tag and it is recommended to do it from inside the constructor as well as attaching your shadow dom more on that in a little whenever you have a constructor in a class that extends another you must call super to initialize the parent class the this keyword inside this class refers to the blog post tag element so if i log it and inspect the page we can see the blog post element in the console since it is a html element i can cite its inner html which will simply be a div of blog post class with a h2 heading a paragraph with some auto-generated lower absent text and the anchor tag would learn more tags now if we check the page we can see that our component rendered the content we defined as inner html if i inspect it here you can see that it puts the content right under the blog post tag this simply means that the content of this component is not encapsulated and it is as simple as if i added this content directly inside my blog post tag in the html file it also means that it belongs to the document therefore i can target it with style and set its background to red for example which will have an effect on the content normally people want to isolate this element content from the document and that's when the shadow root comes into play to create a shadow root for this blog post element i can call the attach shadow which is a method inherit for the html element with extended the function takes two options and one of them is the mode which i'll set to close for now and the method itself returns a shadow root object now instead of using this to set the inner html i'll use the shadow root we got back and this shadow root is already attached to the tag itself if we check the browser the first thing that becomes evident is that our element is no longer red like i said it before which means that the style was set to target the blog post element no longer can reach inside our element and when i inspect here we can see that inside our blog post stack there is a shadow root in closed mode and inside the shadow root there is our content you see everything we set inside our html page has a root and the root it is the document itself and that's why any style we set can be applied to the tags which means we can target our blog post tag with style and other dom selections but its content has a different root which is the shadow root so therefore it is isolated from the rest of the document the shadow root mode simply dictates whether or not the shadow root can be accessed from outside the element or not so if i go ahead and select our blog post tag with query selector and log it we can see it in the console if i tried to log its shadow root i would get a null because when i created the shadow root i specify that it should be closed but if i change it to open we can see that it now log the shadow root which means we can manipulate it like any other element for instance i can query the blog post div and make the background red if i change it back to close we get an error as the shadow rule is not exposed to the outside of the class we created use the close mode if you really don't want anything from outside to tamper with the things inside the shadow root and use the shadow root when you want to isolate content and style from the rest of the document which makes it hard to style from the outside but i'll share with you my technique to do that by the end of this video also if i set the mode to be open i don't need to catch the returned shadow root from the attached shadow call i can just reference it directly since it will not be null and again everything is fine another option that you can specify to your shadow dom is the delegate's focus which simply means that whenever any content inside the shadow root receives focus the same focus style should be applied to the element tag itself so with that being true and i go to the browser and tab to the link we can see that the link received focused and the default browser focus style is applied to it as well as the blog post tag if i go to the anchor tag and i specify to not apply the focus style to it and do the same thing in the browser we can see that now only our tag has the focus style this can be useful to show that this element content is in focus which is a great for accessibility one thing specific to web components elements is the life cycles callbacks and there are four in total there are the connected and disconnected callbacks that if you are familiar with react this would be the equivalent to component mount and component will and mount hooks for angular is the same as ng view init and ng destroy it pretty much means that when the element is loaded on the document the connected callback will be called and when the element is removed from the document the disconnected callback is called to show the disconnected i'll query our blog post tag and after two and a half seconds i'll remove it if i refresh we see the connected clog and two and a half seconds later we see the disconnected log these two are great for when you want to do something in the beginning and at the end of the element lifespan maybe you want to query the data set or check something on the dom check the local storage also you want to unsubscribe or cancel a request when the element is removed from the dom to avoid memory leak to give you an example i'll create a custom render method and move the code setting inner html here and then i'll call this render from the connected callback this particular example may lead to errors depending on what you are doing inside the render i'll show you what i mean when i start dealing with attributes in a little there is also the abductor callback which is called when the element is adopted inside a document for example you may use iframe to show some content pretty much to embed content onto the page and the iframe content belongs to the iframe itself and is separate from the document but you can adopt the iframe element inside the document by using the document adopt node method the fourth lifecycle callback is the attribute change callback which is called when some observed element attributes change either it gets set removed or the value change itself different from any javascript library framework you must explicitly specify which attribute should trigger an element update which is good and allows you to not worry about extra stuff and unnecessary updates to tell our element which attributes we want to observe we must specify a static getter for observed attributes which must return an array with name of the attributes you want to observe for this i want to observe title description and link attributes the attribute change callback gets called with three values the name of the attribute that changed its old and the new value and i'll log the name for now in react this is equivalent of component update and in angular it's called ng change i'll go ahead and set a title for our element and we see a log for the title change note that if i set an id for example it does not trigger a change call because we are not observing the id attribute i'll log the old and new value as well we can see that the previous value is no and these values will be no if the attribute was removed or simply contains no value we could check if the attribute lost value or was removed by making sure there was an old value and there isn't one anymore but to check if the attribute was removed you can just call the has attribute method with the name of the attribute to check that i'll remove the title attribute two and a half seconds later after load we see two updates where the first one it says the attribute was set and exist then the attribute value is no and the attribute does not exist it is common to set properties inside a class to contain the value of these attributes that you can use it is in fact a best practice to map your attributes to your properties inside your class now i can use these properties inside our render template which renders nothing because these are empty so what i can do is set a switch for each expected attribute and update these properties accordingly we can check the browser and see that it captures the title from the first render i'll go ahead and set a description as well and it renders fine too if i log when the connected and attribute change callback gets called we can see that attribute change callback gets called twice first one for each observed attribute title and description then the connected callback is called this is a very important detail to know about this means that if there is something in the attribute change callback method that depends on something done in the connected callback this will throw an error maybe what you can do is check the property is connected inside the attribute change callback before doing anything the ease connected property will inform you if the element was loaded on the document or not this is equivalent of checking if the component is mounted or not it is best practice to only do anything when the element is on the dom it is possible to create this element and do things to it before adding it to the dom so what i can do for now is also call the render again when there is an attribute change the third attribute change log we see here is because we are removing the title after two and a half seconds and we see the title removed because we are calling render and attribute change re-rendering the whole thing may be expensive so what you probably can do is update a specific part of the content instead in general it is best practice to keep your custom elements content simple and small so you don't have to worry about re-rendering a large piece of component so what i'll do is grab a reference of the title description and link elements after i render and i can just update this directly instead of setting the value on property and then re-rendering the whole thing when we check the browser we see errors in the console and this is because i am not checking if this component is connected before i try to access the elements and because the attributes change callback is called before the connected callback which does the rendering there are no reference to these elements therefore we get errors i'll continue not to check this is connected and simply move the render call to inside the constructor for now and construct and function is called before any other method inside the class now all the errors are gone passing data to element using attributes is very limiting because you can only pass strings you can parse the string into numbers and booleans or objects in case you provide a json string but is not ideal you should always parse numbers and boolean before you store the values but avoid passing more complex data using attributes to pass data other than strings to the component aka more complex data types you must take advantage of the properties and the fact that the dom element is an object itself so if i wanted to send a data object to the blog post element i can simply set the data property which the value can be anything and in this case i'll set a data object with title description and link properties to show you that this works i'll remove the attributes and we see nothing render anymore i'll also get rid of these properties in the template now to receive this data opted i'll set a data setter and receive the value which for now i'll simply log in if i check the console we can see the object with the data as i am passing it to the element from here i can use this object to set the content of this element like that the element renders things fine you have to know that when we query the dom and get a element back that element is an instance object for of this class and because it is an object we can do anything we would in an object so if i set the title and log it inside this data center we see it prints an empty string and if i go ahead and set this title before i set the data from the outside like i did with data we can see it logs the correct value i set if i don't want a property to be set directly from the outside i could make this title a private property and to have more control over the property itself i could set a title setter and from there receive the value and then set it on the private property myself that way i could validate or format it before i set it which gives me more control i can also set a getter to return it this private title in any format that i want making a property private simply means you cannot set it from the outside in some cases you want to have a private property to use inside your class and then have setters and getters that let you expose and control the value itself now that you know all details of the web component let me show you a real example how to use it in a page i went ahead and i changed things back and i am using the properties now and added one for thumbnail which i'll add here to be observed as well i am calling render for every update which for this element won't hurt because i am not expecting it to be updated from the outside and it's not much to render as well i added a template getter where i also added a thumbnail div with the image tag for the post and i use this thumbnail to set the shadow root in our html everything else remains the same and this is a very basic component i made videos with more complex web components you can check in this channel i'll leave some link below so you get more reference and examples of real word web components to show you a real example at a base page as you can see here and the only thing missing is the render blog posts right below this blue banner you are seeing here with title of latest post the layout is fluid not responsive and all the work will happen inside this main tag here the page markup is pretty basic and i have this wrapper classes which has a nice css trick i recommend you try i also have style for the page in a style sheet with basic html and body style as always i box size border box everything and remove the top module for heading in paragraph tags something i like to do the wrapper is a smart play on padding which allows inner container content to take up to a max width in this case 960 pixels and when it fills the the full view width it leaves a 25 pixels gap from the edge as you can see here it is a calculation on left and right padding i wrote an article with many css tricks like this i'll leave the link below so you can check the rest of this style is pretty basic one check the source code in link below for full details another technology i want to take advantage of is the esx module which allows me to import and export things in the browser as well better organize my components into files as i would like to for that i'll change this script tag into a module type then i'll create a module for the blog post component and move all this code from the app.js file to there here i'll simply export this class and now from inside the app.js file i'll import the blog post file and for the modules import statements in the browser you must specify the extension of the file you are importing if i now go ahead and set the blog post tag inside the main tag we can see the post rendered just fine but i did not pass any details for the post another technology you can take advantage of for when building components is the template and slot tags which are a great way to share template between components if we check our blog post we can see that this template belongs to it i could select the tag and read the template but it still is not reusable without having to manipulate it first to reuse this template in my index html i'll set a template tag which content is the template from the blog post i'll make sure to remove reference to the internal properties of the class and set a default title and description in all uppercase for distinction i'll also give this template an id since it isn't unique and to allow us to grab it later on adding a template tag to the page will not result in any render the template content is a document fragment and therefore will be ignored on render so now inside our render instead of using this internal template i'll query the page template by id and i'll append the template content to the shadow root now we see the template from document rendered instead having the templates on the page is not my thing so let me show you when how i sometimes do it i'll create a templates file and here i'll export a templates object and i'll set a getter for the blog post where inside i'll create a templates element and grab our element content and set it as inner html then i'll return this template element you don't actually need to create a template element you could just return the string but i like to work with templates elements this object can grow big as you pile up templates here i recommend you putting each template in their respective file and export them instead the only advantage of having templates like this is that you have to make a single import and would get you all the templates but sometimes you don't need to load all the templates on the page so use this wisely now inside the blog post file i'll import this templates option and replace it here and in the render instead of querying it on the dom since like i did something wrong here ah i forgot specify extension when i imported templates a good practice is to use the mjs extension instead of just to make the distinction between module and script files and now the template works fine as well so use templates like this when you want to share templates between components like models dialogs drop down tabs which can share the same structure but have different types or content and you can also add style to the template as well more on that in a little the best part of templating is the usage of slots aka placeholders what if i want to use the tag other than h2 for the block title what i can do is wrap it with a slot tag and provide identification name and this slot will be a placeholder anything put inside the slot tag will be used if you don't try to replace it with anything else it's the default content pretty much i'll also slot the link as well as the image tag so people can provide a custom image tag with a description alt attribute for example now in the html file inside our blog post tag i'll add my content i'll add title with h3 tag instead to and to indicate which slot to replace this with i'll add the slot attribute to it that matches the title slot name now when i check the page we see my title rendered i'll also add a link that say read more instead instead of learn more and we see it in the page now i can also add one for the time now but i'm not rendering any image for now so we don't see anything one thing to know is that you don't have to use template tag for slot to work if i use my normal internal template inside the blog post element class it will work as well to show you let me add slots to the internal template i'll also change it to always rendered image tag as well and if i check the page the slot just worked fine which shows that slot and templates are not dependent on each other slot tags work fine with your component markup another best practice to not create dependencies between custom elements each element should work independently of any context or tag you should also try to mimic native elements behavior as much as possible with that you should now know three ways to pass data to your component the first is through attributes which only takes strings and you can control them inside the attribute change callback method the second is through properties which allows you to pass any kind of data to component and you can control it with setters inside your class the third is through slots which allows you to inject html into your component template which helps your component be flexible with the option to extend its template with extra markup super cool in the real world you wouldn't render each blog post one by one passing the data to each directly on the page so what i'll do is create a blog post list file where i'll add my blog post list component class i'll start with it being closed and in render i'll set content being a section tag with a slotted h2 tag saying blog post then i'll define a div where i'll put each post the post will be stored in a private post array where inside of posts div i'll map it returning our blog post tag where i'll use each post info to set the expected attribute on it when you map your html you must join it at the end to change it to a string because map returns a new array and not a string i'll define my blog post list tag now instead of rendering the single blog post i'll render my blog post list which will take care of running all the posts and receive the data to use it this will only render the tag for now because i never called the render inside of class and i also never imported into our app inside our app i'll import this blog post list component and now we can see that it rendered the tag with a shadow root but no content because the render is not called yet i'll query the blog post list tag and set posts directly on it i already have a list of dummy blog posts i can use this post would have come from an api call as well so it's up to you where you make that call but i like my components to receive data rather than fashion data themselves so they they remain display only sometimes uh now inside the component i'll set a setter where i'll receive the post and set it on a private process property and then call render like that we see all the posts being rendered in the page now all they need now is to be styled so let me do that i'll start with the list first and i'll define a getter for the style and the way you add style is inside of style tag so this returns a style tag i'll add a class to the section and i can target the actual blog post list tag with the host selector which i'll make sure it is display block because every customer element you create is always in line and that leads to a weird behavior sometimes so i like to make sure my posts are block or inline block or if i feel like it makes sense to be in line i could leave a messaging line but make sure you cover that detail as well because you may find weird situation where your post does not render quite right on the page then i'll target every tag inside the element and set box size into border box i'll set padding to the section tag and display grid with repeated columns out fill for responsiveness and each column must be minimum 250 pixels wide and can take as much available space with one fr for rows i'll simply make it take max available space which will be defined by the tallest card in that row for the style to make effect i'll add it before our section tag inside of shadow root inner html by the way to add a style to a template you can simply set a style tag inside your template with your style and it will just work fine for that template we don't get the right effect and this is because i target the section instead of the post div for display grid we see two columns of posts if i resize you can see it is responsive and becomes three columns in its max width and one when in mobile size now for this slotted title i could simply target the h2 tag which is the default tag used when no tag is provided for the title but in case you want to target any tag provide for the title as well you can use the slotted pseudo element selector and provide a tag selector and in in this case since we won't know which it will be i'll simply select any with slot attribute of heading you don't have to style these slotted tags these can come with their own styles sometimes which allows your component style to be dynamic based on the tag provided for the slot for the heading i'll simply force it to be one ramp size bold font way and five percent base font size for the ladder spacing and add a nice bottom border for separation with some padding to space it styling the blog post from here would not work because blog post elements content is inside a shadow root so i'll need to style the blog post itself i'll also add a style getter for it and the same thing for the host tag aka blog post tag to test i'll make it grey background and to make sure the style is applying i'll add it to the internal template i'll use a lighter gray background now some padding and round corner and make sure it takes full height of the row for the thumbnail i'll make it dark gray and fix height of 150 pixels hidden overflow and bottom margin to this distance it this is so i can fill it with the image tag for the image tag i'll make sure to not show it if it has an empty href attribute it did not work so i inspected seems like it has undefined string as value so to fix this i'll go back to the blog post list component and make sure i'll default to empty string if thumbnail does not exist now the image is hidden for the image you can position center absolute to the thumbnail div and make it fit the thumbnail box for the heading i'll remove margin and padding on the left and right i make sure to use any sanitary front 120 percent size of the base font size in 135 line height back to the thumbnail i don't want this gap on the sides so i wanted to cover uh the upper part of the postcard so i use negative 10 pixels for the left and top since the padding is 10 pixels on the post tag this will push it to the left and up but will not cover everything so i'll adjust its width with 100 plus 20 pixels where 20 is the total when you add left and right padding of the card i made a post where i show how to style many card types like this check that out for more examples now for the paragraph i'll make 80 of the base font size 150 line height dark gray color and ten pixels left and right padding for the link nine percent base font size and remove text decoration since this is slotted i'll make sure to only target this default link and not one provided to be used in the slot so i'll class it and use that class to style it instead this will make sure that when a link is provided for the slot it will keep the style it came with i'll make it bolder with five percent base font size of letter spacing with color i'll pick then i can make i can capitalize and add a custom underline if you want to learn how to add cool underlines to text check my underlying video for some cool tricks you would love pretty sure so the tricky thing about custom element is that it is hard to override their style from outside i already showed you how to use shadow root to style its content in the beginning of the video the problem with that is that it only works if the shadow root is created in open mode there is another way you can allow your custom elements to be styled from the outside which is by using the css variables which values make through the shadow root so if i want the thumbnail background to be override i could use a variable of thumb and if that is not provided i'll fall back to my default background column by passing it as a second argument to the var function everything looks normal but if i go to the blog post list element and define this variable with a nice color it will be used instead of this default grid and it works and that's how you allow your components to have their style over in i could also write as learn more default link on the post by providing my own link with the read more instead and because it has the right slot attribute value it replaces as you can see the default site for the links in the post did not apply here which means i can go ahead and set a style for it and that will work just fine this is because the injected slot html is kept outside the shadow root and the dom but the slot inside the template references it like as alias for example this is also a way you can allow some part of your components to be styled from outside by making parts of it slotted if that's a word seems like the margin did not make effect this is because the anchor tag is an inline tag so i'll make it display inline block instead i'll go ahead and change this thumbnail to a nicer color and like that my components are in place and looking nice and my pages not bad at all let me know what you think in the comments like this video to support the channel subscribe and turn on notifications so you don't miss out on anything grab the code by checking the link in the description once again thanks for watching catch the next one bye [Music] you
Info
Channel: Before Semicolon
Views: 825
Rating: 4.9024391 out of 5
Keywords: web development, technology, programming, web, web components, javascript
Id: PFpUCnyztJk
Channel Id: undefined
Length: 33min 31sec (2011 seconds)
Published: Sun Aug 15 2021
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.