跳到主要内容

认识

一、认识


Web Components 标准非常重要的一个特性是,它使开发者能够将 HTML 页面的功能封装为 custom elements(自定义标签),而往常,开发者不得不写一大堆冗长、深层嵌套的标签来实现同样的页面功能。具体工作流如下:

一、创建自定义元素类: 继承原始类 HTMLElement 或者已有内置元素类 HTMLUListElement

二、创建 Shadow Root 影子 Root: 通过调用元素的 attachShadow 方法,可以为该元素创建一个 Shadow Root。这相当于在元素内部开辟一块独立的 DOM 区域。Shadow DOM(影子 DOMWeb Components 技术中的核心概念之一, 它允许你将一个 DOM 子树与主文档 DOM 隔离开来,从而实现组件级别的封装。Shadow DOM 可以将一个元素的内部结构、样式和行为与外部文档分离开。这样一来,组件内部的样式不会受到全局样式的干扰,也不会影响到外部的样式。通过 Shadow DOM, 你可以创建自包含的组件。组件内部的实现细节对外部是不可见的,仅暴露必要的接口或插槽(slot)给外部使用。其中, open 模式, 可以通过 element.shadowRoot 访问 Shadow DOM; closed 模式, 外部无法访问 Shadow DOM, 只能在内部使用。

三、封装与插槽: 在 Shadow DOM 内部,可以定义组件的结构和样式,同时可以利用 <slot> 标签来指定组件的插槽区域,将外部内容插入到 Shadow DOM 内部,实现灵活的内容分发和组件组合。Shadow DOM 内部的 CSS 仅对其内部元素生效,避免了全局样式的污染。同时,外部样式默认也不会影响 Shadow DOM 内部的元素。

四、注册自定义元素: customElementsWindow 对象上的一个只读属性,接口返回一个 CustomElementRegistry 对象的引用,可用于注册新的 custom element, 或者获取之前定义过的自定义元素的信息。customElements.define() 方法用于定义一个自定义元素。

二、语法


index.js 定义自定义元素

class CustomElement extends HTMLElement {
constructor() {
super();

const shadow = this.attachShadow({ mode: "open" });
const wrapper = document.createElement("div");

wrapper.innerHTML = "自定义元素";
shadow.appendChild(wrapper);
}

connectedCallback() {
console.log("当 custom element 首次被插入文档 DOM 时,被调用。");
}

disconnectedCallback() {
console.log("当 custom element 从文档 DOM 中删除时,被调用。");
}

adoptedCallback() {
console.log("当 custom element 被移动到新的文档时,被调用。");
}

attributeChangedCallback() {
console.log("当 custom element 增加、删除、修改自身属性时,被调用。");
}
}

customElements.define("custom-element", CustomElement);

index.html 使用自定义元素

<custom-element></custom-element>

三、用法


3.1 自主定制元素

// Create a class for the element
class PopUpInfo extends HTMLElement {
constructor() {
// Always call super first in constructor
super();

// Create a shadow root
var shadow = this.attachShadow({ mode: "open" });

// Create spans
var wrapper = document.createElement("span");
wrapper.setAttribute("class", "wrapper");
var icon = document.createElement("span");
icon.setAttribute("class", "icon");
icon.setAttribute("tabindex", 0);
var info = document.createElement("span");
info.setAttribute("class", "info");

// Take attribute content and put it inside the info span
var text = this.getAttribute("text");
info.textContent = text;

// Insert icon
var imgUrl;
if (this.hasAttribute("img")) {
imgUrl = this.getAttribute("img");
} else {
imgUrl = "img/default.png";
}
var img = document.createElement("img");
img.src = imgUrl;
icon.appendChild(img);

// Create some CSS to apply to the shadow dom
var style = document.createElement("style");

style.textContent =
".wrapper {" +
"position: relative;" +
"}" +
".info {" +
"font-size: 0.8rem;" +
"width: 200px;" +
"display: inline-block;" +
"border: 1px solid black;" +
"padding: 10px;" +
"background: white;" +
"border-radius: 10px;" +
"opacity: 0;" +
"transition: 0.6s all;" +
"position: absolute;" +
"bottom: 20px;" +
"left: 10px;" +
"z-index: 3;" +
"}" +
"img {" +
"width: 1.2rem" +
"}" +
".icon:hover + .info, .icon:focus + .info {" +
"opacity: 1;" +
"}";

// attach the created elements to the shadow dom

shadow.appendChild(style);
shadow.appendChild(wrapper);
wrapper.appendChild(icon);
wrapper.appendChild(info);
}
}

// Define the new element
customElements.define("popup-info", PopUpInfo);

3.2 自定义内置元素

// Create a class for the element
class WordCount extends HTMLParagraphElement {
constructor() {
// Always call super first in constructor
super();

// count words in element's parent element
var wcParent = this.parentNode;

function countWords(node) {
var text = node.innerText || node.textContent;
return text.split(/\s+/g).length;
}

var count = "Words: " + countWords(wcParent);

// Create a shadow root
var shadow = this.attachShadow({ mode: "open" });

// Create text node and add word count to it
var text = document.createElement("span");
text.textContent = count;

// Append it to the shadow root
shadow.appendChild(text);

// Update count when element content changes
setInterval(function () {
var count = "Words: " + countWords(wcParent);
text.textContent = count;
}, 200);
}
}

// Define the new element
customElements.define("word-count", WordCount, { extends: "p" });