Web Component - js-lib-com/wood GitHub Wiki

W3C has a suite of technologies allowing to create reusable custom elements, named Web Components. On this suite there is an API named Custom Elements that help in creating new tags with custom behavior, somewhat similar with WOOD component. See Using Custom Elements from MDN.

Custom Element

Usual way to declare a custom element is to use a self-executing anonymous function. By W3C convention, custom elements name should be a compound name and have at least one dash.

  • lib/nav-separator.js
(function () {
    class NavSeparator extends HTMLElement {
        constructor() {
            super();
        }
    }

    customElements.define("nav-separator", NavSeparator);
})();

From WOOD perspective custom elements are exactly the same as the built-in ones. A custom element can be used in any WOOD component: parent or child, template or content. Anyway, need to explicitly include the script into WOOD component descriptor.

  • page/index/index.xml
<page>
	<script src="lib/nav-separator.js"></script>
</page>
  • page/index/index.htm
<body>
    <nav>
        ...
        <nav-separator />
        ...
    </nav>
    ...
</div>

Shadow DOM

Shadow DOM is a standard W3C API that allows for style selectors, including elements ID, and events encapsulation. It is a way to define a new DOM tree whose root container, or host, is visible in the document, while the shadow root and its children are not.

Here are core concept definitions:

  • shadow host: The regular DOM node that the shadow DOM is attached to,
  • shadow tree: The DOM tree inside the shadow DOM,
  • shadow boundary: the place where the shadow DOM ends, and the regular DOM begins,
  • shadow root: The root node of the shadow tree.

For details see Using Shadow DOM.

Be it a page component page/index with a section. Please note shadow CSS class from section element and custom attribute data-style with style reference to page/index/section.css file.

  • page/index/index.htm
<body>
	<h1>Page Title</h1>
	<section class="shadow" data-style="@style/section">
		<h1>Section Title</h1>
	</section>
</body>

Page component descriptor declare a library script, lib/shadow.js.

  • page/index/index.xml
<page>
    <script src="lib/shadow.js" />
</page>

Page style has a global rule for all H1 elements.

  • page/index/index.css
h1 {
	color: var(--page-heading-color);
}

Into page component directory we also create style file for shadow section element, page/index/section.css.

  • page/index/section.css
h1 {
	color: var(--section-heading-color);
}

Library shadow script replaces section element with shadow CSS class with dynamic created shadow host element and shadow root. The dynamically include shadow section styles and move original section element to shadow root.

  • lib/shadow.js
const shadowElements = document.getElementsByClassName("shadow");

while (shadowElements.length) {
	const shadowElement = shadowElements.item(0);
	shadowElement.classList.remove("shadow");

	const shadowHost = document.createElement(shadowElement.tagName);
	shadowElement.parentElement.insertBefore(shadowHost, shadowElement);
	const shadowRoot = shadowHost.attachShadow({ mode: 'open' });

	const shadowStyle = shadowElement.getAttribute("data-style");
	if (shadowStyle) {
		shadowElement.removeAttribute("data-style");
		const linkElement = document.createElement("link");
		linkElement.setAttribute("rel", "stylesheet");
		linkElement.setAttribute("href", shadowStyle);
		shadowRoot.appendChild(linkElement);
	}

	shadowRoot.appendChild(shadowElement);
}

Here is resulting index page from generated site.

  • site/index.htm
<HTML>
	<HEAD>
		<LINK href="style/page_index.css" rel="stylesheet" type="text/css" />
		<SCRIPT defer="true" src="script/lib.shadow.js" type="text/javascript"></SCRIPT>
	</HEAD>
	<BODY>
		<H1>Page Title</H1>
		<SECTION class="shadow" data-style="style/page_section.css">
			<H1>Section Title</H1>
		</SECTION>
	</BODY>
</HTML>

After script/lib.shadow.js script execution all elements with shadow CSS class are replaced with shadow host element and related shadow document tree. Resulting layout from browser DOM resemble next HTML pseudo-code:

<HTML>
	<HEAD>
		<LINK href="style/page_index.css" rel="stylesheet" type="text/css" />
		<SCRIPT defer="true" src="script/lib.shadow.js" type="text/javascript"></SCRIPT>
	</HEAD>
	<BODY>
		<H1>Page Title</H1>
		<SECTION>
			<SHADOW-ROOT>
				<LINK href="style/page_section.css" rel="stylesheet" type="text/css" />
				<SECTION>
					<H1>Section Title</H1>
				</SECTION>
			</SHADOW-ROOT>
		</SECTION>
	</BODY>
</HTML>

Page heading style is controlled by global rules from site/style/page_index.css file. Because section is in a shadow DOM, global heading rules does not apply to section heading. Instead dynamically loaded style site/style/page_section.css is in effect.


Last updated 9/8/2022.

⚠️ **GitHub.com Fallback** ⚠️