认识
一、认识
Web Components
标准非常重要的一个特性是,它使开发者能够将 HTML
页面的功能封装为 custom elements
(自定义标签),而往常,开发者不得不写一大堆冗长、深层嵌套的标签来实现同样的页面功能。具体工作流如下:
一、创建自定义元素类: 继承原始类 HTMLElement
或者已有内置元素类 HTMLUListElement
二、创建 Shadow Root
影子 Root
: 通过调用元素的 attachShadow
方法,可以为该元素创建一个 Shadow Root
。这相当于在元素内部开辟一块独立的 DOM
区域。Shadow DOM
(影子 DOM
)是 Web 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
内部的元素。
四、注册自定义元素: customElements
是 Window
对象上的一个只读属性,接口返回一个 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" });