You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
extended built-in elements: e.g. extend a button to your own custom button
Web Component Lifecycle
constructor(): is called when element is created; use it for basic initialization
connectedCallback(): is called when element is attached to DOM; use it for DOM initialization
disconnectedCallback(): is called when element is detached from DOM; use it for cleanup work
attributeChangedCallback(): this observes attribute updates; use it to update data and DOM
Light and Shadow DOM
light DOM is the normal, accessible DOM that you can see in the browser
shadow DOM is a proper DOM of an HTML element that is invisible for users in the browser AND not directly connected to the real DOM (i.e. is not affected by global css)
Example Custom Tooltip
<p><mp-tooltipclass="important" text="Tooltip text set as text attribute in light DOM"><!-- Text between Web Component tags is NOT part of shadow DOM, so can be styled in light DOM --><spanclass="highlight">Web Components</span></mp-tooltip>
are nice.
</p><mp-tooltip></mp-tooltip>
// every custom element has to extend HTMLElementclassTooltipextendsHTMLElement{constructor(){super();this._container;this._icon;this._text='Default tooltip text';this._tooltipVisible=false;// [1] attach a shadow DOM to the custom element// mode: A string specifying the encapsulation mode for the shadow DOM tree.// 'open': elements of the shadow root are accessible from JS outside the root, for example using Element.shadowRootthis.attachShadow({mode: 'open'});// [2] append template to your custom element with custom styles// :host -> to style custom element; apply styling conditionally if certain class, id, attribute// is set on custom element in light DOM, us :host(YOUR_SELECTOR)// ::slotted() -> to style "slot" content of light DOM inside shadow DOM// pass * as argument to select ALL slotted content, otherwise you can pass// all normal CSS selectors, but NO child selector (e.g. span a)// Notice: light DOM styling overwrites shadow DOM styling herethis.shadowRoot.innerHTML=/*html*/` <style> :host { background-color: #ccc; } :host(.important) { color: var(--color-primary, #4a4a4a); background-color: #e2b664; } ::slotted(.highlight) { border-bottom: 2px solid red; } div { position: absolute; top: 20px; left: 50%; transform: translateX(-50%); width: 100px; color: #fff; background-color: #4a4a4a; padding: 5px 10px; border-radius: 4px; box-shadow: 1px 1px 6px rgba(0, 0, 0, 0.26); z-index: 1; } #icon { position: relative; cursor: default; } </style> <slot>Default slot text, if nothing is set between custom element tags in light DOM</slot> <sup id='icon'>ⓘ</sup> `;}connectedCallback(){// [3] get value of a custom attribute and store it into variable that is accessible inside classif(this.hasAttribute('text')){this._text=this.getAttribute('text');}// [4] actions when mouse enters or leaves elementthis._icon=this.shadowRoot.getElementById('icon');this._icon.addEventListener('mouseenter',this._showTooltip.bind(this));this._icon.addEventListener('mouseleave',this._hideTooltip.bind(this));// [5] append an element to the shadow DOM root// "this" refers to the current class, i.e. the shell HTML elementthis.shadowRoot.appendChild(this._icon);// render custom element on mountthis._render();}// [6] observing attribute changes on custom element in light DOMattributeChangedCallback(name,prevValue,newValue){if(prevValue===newValue)return;if(name==='text'){this._text=newValue;}}// [6.1] establish connection which attributes you want to observestaticgetobservedAttributes(){return['text'];}// [7] clean up work when custom element is removed from DOMdisconnectedCallback(){console.log('disconnected');// you do NOT have to remove event listeners here, since browser does it automtically for all DOM elements}// [8] custom logic to define how DOM should be updated_render(){if(this._tooltipVisible){this._container=document.createElement('div');this._container.textContent=this._text;this.shadowRoot.getElementById('icon').appendChild(this._container);}if(!this._tooltipVisible&&this._container){this.shadowRoot.getElementById('icon').removeChild(this._container);}}// [9] methods responsible for updating data that results in re-rendering_showTooltip(){this._tooltipVisible=true;this._render();}_hideTooltip(){this._tooltipVisible=false;this._render();}}// built-in method to make a custom element available as HTML element// Rule: string name should consist at least of 2 words separated by a dashcustomElements.define('mp-tooltip',Tooltip);
Example Custom Anchor extending HTMLAnchorElement
<!-- when using specific element as Web Component template, then use is='YOUR_CHOSEN_NAME' --><ais="confirm-link" href="https://www.google.de">Google</a>
classConfirmLinkextendsHTMLAnchorElement{connectedCallback(){this.addEventListener('click',(event)=>{// confirm() is browser built-in method where user can confirm or deny a questionif(!confirm('Do you really want to leave?')){event.preventDefault();}});}}// Whenever you extend a specific element (not a basic HTMLElement), you have to add third argumentcustomElements.define('confirm-link',ConfirmLink,{extends: 'a'});
Using CSS Variables of Light DOM in Custom Element