Overview
Purpose: Create a component's shadow root, and clone a template into it when it is first rendered.
This mixin forms a core part of the Elix render pipeline:
events → methods → setState → render
Expects the component to provide:
- internal.template property that returns an HTMLTemplateElement used to populate the shadow tree of a new component.
- Optional internal.shadowRootMode property to indicate whether to create open (default) or closed shadow roots.
Provides the component with:
- internal.render implementation that creates and populates the shadow root the first time the component is rendered.
- internal.ids property that can be used to access elements in the shadow tree that have
id
attributes. See the ids property below. - internal.shadowRoot property holding a reference to an element's shadow root.
All Elix elements use ShadowTemplateMixin
to populate their Shadow DOM subtree with template elements, so is included in the ReactiveElement base class.
Usage
import ShadowTemplateMixin from "elix/src/core/ShadowTemplateMixin.js";
class MyElement extends ShadowTemplateMixin(HTMLElement) {}
ShadowTemplateMixin
must be applied to your base class before any mixins or classes that want to render to elements in the Shadow DOM subtree. This is unusual for Elix mixins, which by design can usually be applied in any order.
Example
import * as internal from "elix/src/internal.js";
import ShadowTemplateMixin from "elix/src/core/ShadowTemplateMixin.js";
class GreetElement extends ShadowTemplate(HTMLElement) {
connectedCallback() {
this[internal.render]();
}
get [internal.template]() {
const template = document.createElement("template");
template.innerHTML = `Hello, <slot></slot>!`;
return template;
}
}
customElements.define("greet-element", GreetElement);
const element = new GreetElement();
element.textContent = "world"; // User sees, "Hello, world!"
When the element above is instantiated, the constructor supplied by ShadowTemplateMixin
finds the element's template and stamps it into a new shadow root.
Rendering
This mixin does its primary work in a component's internal.render method. The first time that method is called, the mixin:
- Looks for a template property (see below).
- Attaches a new shadow root. By default, the shadow root will be open, although you can request a closed shadow root by defining a internal.shadowRootMode property that returns "closed".
- Clones the template into the shadow root.
The template
property
The ShadowTemplateMixin
expects a component to define a property getter identified as internal.template. If this property has not been defined, the mixin issues a console warning, but does not throw an exception.
The template property should return an HTML template (an HTMLTemplateElement
). This HTMLTemplateElement can be defined by any means, including directly in JavaScript (as shown in the example above). You may also find it convenient to define a template with the html helper in the template module. Using that helper, the above example becomes:
import * as internal from "elix/src/internal.js";
import * as template from "elix/src/template.js";
import ShadowTemplateMixin from "elix/src/core/ShadowTemplateMixin.js";
class GreetElement extends ShadowTemplate(HTMLElement) {
get [internal.template]() {
return template.html`Hello, <slot></slot>!`;
}
}
For better performance with components that dynamically construct a template, ShadowTemplateMixin
caches a component's template.
The ids
property
As a convenience, ShadowTemplateMixin
defines a property called ids
that represents the collection of all subelements in an element's Shadow DOM subtree that have an id
attribute. This makes it easier for you to obtain a reference to a specific subelement, e.g., for the purpose of rendering changes to it.
The ReactiveMixin example shows a simple increment/decrement component that wants to render the value
in its state to a span with the id "value". We can use the ids
property to refer to that span efficiently:
import * as internal from "elix/src/internal.js";
import ReactiveElement from "elix/define/ReactiveElement.js";
class IncrementDecrement extends ReactiveElement {
[internal.render](changed) {
if (changed.value) {
// When the value changes, show the value as the span's text.
this[internal.ids].value.textContent = this[internal.state].value;
}
}
}
The reference this[internal.ids].value
is equivalent to this.shadowRoot.getElementById('value')
.
API
Used by classes AlertDialog, AutoCompleteComboBox, AutoCompleteInput, AutoSizeTextarea, Backdrop, Button, CalendarDay, CalendarDayButton, CalendarDayNamesHeader, CalendarDays, CalendarMonth, CalendarMonthNavigator, CalendarMonthYearHeader, Carousel, CarouselSlideshow, CarouselWithThumbnails, CenteredStrip, CheckListItem, ComboBox, CrossfadeStage, DateComboBox, DateInput, Dialog, Drawer, DrawerWithGrip, DropdownList, ExpandablePanel, ExpandableSection, Explorer, FilterComboBox, FilterListBox, HamburgerMenuButton, Hidden, Input, ListBox, ListComboBox, ListExplorer, ListWithSearch, Menu, MenuButton, MenuItem, MenuSeparator, ModalBackdrop, Modes, MultiSelectListBox, NumberSpinBox, Option, OptionList, Overlay, OverlayFrame, PlainAlertDialog, PlainArrowDirectionButton, PlainAutoCompleteComboBox, PlainAutoCompleteInput, PlainAutoSizeTextarea, PlainBackdrop, PlainBorderButton, PlainButton, PlainCalendarDay, PlainCalendarDayButton, PlainCalendarDayNamesHeader, PlainCalendarDays, PlainCalendarMonth, PlainCalendarMonthNavigator, PlainCalendarMonthYearHeader, PlainCarousel, PlainCarouselSlideshow, PlainCarouselWithThumbnails, PlainCenteredStrip, PlainCenteredStripHighlight, PlainCenteredStripOpacity, PlainChoice, PlainComboBox, PlainCrossfadeStage, PlainDateComboBox, PlainDateInput, PlainDialog, PlainDrawer, PlainDrawerWithGrip, PlainDropdownList, PlainExpandablePanel, PlainExpandableSection, PlainExpandCollapseToggle, PlainExplorer, PlainFilterComboBox, PlainFilterListBox, PlainHamburgerMenuButton, PlainHidden, PlainInput, PlainListBox, PlainListComboBox, PlainListExplorer, PlainListWithSearch, PlainMenu, PlainMenuButton, PlainMenuItem, PlainMenuSeparator, PlainModalBackdrop, PlainModes, PlainMultiSelectListBox, PlainNumberSpinBox, PlainOptionList, PlainOverlay, PlainOverlayFrame, PlainPageDot, PlainPopup, PlainPopupButton, PlainPopupSource, PlainProgressSpinner, PlainPullToRefresh, PlainRepeatButton, PlainSelectableButton, PlainSlideshow, PlainSlideshowWithPlayControls, PlainSlidingPages, PlainSlidingStage, PlainSpinBox, PlainTabButton, PlainTabs, PlainTabStrip, PlainToast, Popup, PopupButton, PopupSource, ProgressSpinner, PullToRefresh, ReactiveElement, RepeatButton, SelectableButton, Slideshow, SlideshowWithPlayControls, SlidingPages, SlidingStage, SpinBox, TabButton, Tabs, TabStrip, Toast, TooltipButton, UpDownToggle, and WrappedStandardElement.
ids property
A convenient shortcut for looking up an element by ID in the component's Shadow DOM subtree.
Example: if component's template contains a shadow element <button id="foo">
, you can use the reference this[ids].foo
to obtain
the corresponding button in the component instance's shadow tree. The
ids
property is simply a shorthand for getElementById
, so
this[ids].foo
is the same as
this[shadowRoot].getElementById('foo')
.
Type: object