# Web Components & Stencil.js
## Web Components with JS, HTML and CSS
### Define your custom element
The name of the custom element should have at least two words separated by a dash. The reason for this is that single words are reserved for builtin HTML tags. Best practice is to prefix your custom element name with your company name or some abbreviation.
```javascript=
class Foo extends HTMLElement {
constructor() {
super();
}
}
customElements.define('my-foo', Foo);
```
After you've done this you can start using the newly created custom element in your template.
```htmlmixed=
<html>
<head>
<script src="path/to/foo.js"></script>
</head>
<body>
<my-foo></my-foo>
</body>
</html>
```
You can also extend any existing HTML tags. This is handy if you want to add extra functionality to an anchor tag for example.
```javascript=
class Bar extends HTMLAnchorElement {
constructor() {
super();
}
}
customElements.define('my-bar', Bar, {extends: 'a'});
```
To use the extended custom element, you have to do the following:
```htmlmixed=
<html>
<head>
<script src="path/to/bar.js"></script>
</head>
<body>
<a is="my-bar" href="https://...">Link</a>
</body>
</html>
```
The anchor tag still works as it should be, but you can add more functionality to it.
### Lifecycle callbacks
When you use your custom element in your template, whenever the page is loaded the lifecycle of your custom element begins. Right after it is constructed that custom element will be attached to the light DOM. When that is done the `connectedCallback` will be invoked. This is the place to create new HTML elements or register events. When your custom element is destroyed the `disconnectedCallback` is invoked, be sure to cleanup any registered event listeners. I will talk about `attributeChangedCallback` in another section. Below you'll find all the events:
- constructor
- New instance of your class is created; setup your custom element.
- connectedCallback
- Attached to the DOM; you can talk to the DOM.
- disconnectedCallback
- Removed from the DOM; cleanup phase.
- adoptedCallback
- Moved to another document; check the new context.
- attributeChangedCallback
- Attribute on the host has changed; update the content.
### Shadow DOM
To enable the shadow DOM you have to initialize this in the constructor of your custom element.
```javascript=
this.attachShadow({mode: 'open'});
```
The shadow DOM will not inherit any styles from the light DOM and the shadow DOM cannot change any styles in the light DOM. Its completely isolated.
### Style
You can style the host of the custom element with:
```css=
:host {}
:host(.class, tag or #id) {}
:host-context(.class, tag or #id) {}
```
Styling the slots can be done using the `::slotted` pseudo element.
### Templates and slots
Your custom element is just a set of builtin HTML elements and other custom elements, so you'll need a way to define this template. To make things easy you can add this template directly in your JS file.
```javascript=
class Foo extends HTMLElement {
constructor() {
super();
this.innerHTML = `
<div>
My first Web Component
</div>
`;
}
}
customElements.define('my-foo', Foo);
```
Using backticks lets you format the template just like you would do in a normal HTML file. Easier to read.
When you use the new custom element in your project, the custom element will be filled with the `innerHTML` template whenever the DOM is ready. If you want this template to live inside the shadow DOM, you have to change `this.innerHTML` to `this.shadowRoot.innerHTML` and don't forget to call `this.attachShadow` before calling the `this.shadowRoot.innerHTML`.
The text in the custom element's innerHTML is static. If you want to be able to change this from inside your own template, you can use slots.
```javascript=
class Foo extends HTMLElement {
constructor() {
super();
this.innerHTML = `
<div>
<slot>My first Web Component</slot>
</div>
`;
}
}
customElements.define('my-foo', Foo);
```
Now if you use it in your own template, everything between `<my-foo>I changed the text!</my-foo>` will be placed inside the `<slot>` tag, replacing the default value of `My first Web Component`.
#### Slot names
If you want to add more slots to your template you can assign names to your slots.
```htmlmixed=
<slot name="header"></slot>
```
#### Query slots
#### `slotchange` event
#### AssignedNodes function
### Listen to attribute changes
By default your custom element will not update when a change is made to any of the attributes. You can activate this by adding a static getter that returns the attributes that need to be watched.
```javascript=
static get observedAttributes() {
return ['attribute'];
}
```
After you've done this you can start listening to changes.
```javascript=
attributeChangedCallback(name, oldValue, newValue) {
if (name === 'attribute') {
// update the local variable
}
}
```
### Events
You can communicate between custom components and your templates by adding events. These are normal JS events. You can use the `dispatchEvent` function to dispatch an event.
```javascript=
this.dispatchEvent(new CustomEvent('awesome', {
bubbles: true,
detail: { text: () => textarea.value }
}));
```
## Stencil.js