您的第一个自定义元素
那么,您有兴趣了解 Web 组件并创建自己的自定义 HTML 标签吗?在这篇文章中,我们将探讨基本语法和概念,让您开始涉足自定义元素和Shadow DOM。
我们将创造一个愚蠢的<我的标题>自定义元素,简单地标记出样式标题。不是很有用,但它有助于演示一些初始概念。
入门
首先,我们将创建一个单独的 JavaScript 文件,其中包含有关自定义元素的所有内容:其样式规则、其标记、ES6 类定义,最后注册自定义元素。在我们想要使用自定义元素的 HTML 文件中,我们所要做的就是包含该 JavaScript 文件,然后我们就可以开始在页面上使用新标签了。
为了达到更好的效果,我们将所有内容都包装在一个IIFE中:
(function() {
// the good stuff goes here
})();
现在让我们为自定义元素定义一个类,该类应该扩展HTML元素:
(function() {
class MyTitle extends HTMLElement {
connectedCallback() {
this.innerHTML = `
<style>
h1 {
font-size: 2.5rem;
color: hotpink;
font-family: monospace;
text-align: center;
text-decoration: pink solid underline;
text-decoration-skip: ink;
}
</style>
<h1>Hello Alligator!</h1>
`;
}
}
window.customElements.define('my-title', MyTitle);
})();
以下是一些注意事项:
- 在 ES6 类中,这指的是元素实例本身。
- 这已连接回调方法在元素添加到 DOM 后触发。还有一个断开连接回调从 DOM 中移除元素时调用的方法,对于清理事件处理程序之类的东西很有用。
- 我们注册(定义)自定义元素自定义元素.define并将标签名称作为第一个参数传入,然后将定义元素的类作为第二个参数传入。定义元素之后,我们便可以在 HTML 文档中使用它。请注意,标签名称应至少包含两个单词,并用连字符分隔。这是为了防止将来的任何 HTML 元素与自定义元素发生冲突。
使用这样一个简单的自定义元素,您还可以在对customElements.define的调用中直接将该类定义为匿名类:
(function() {
window.customElements.define(
'my-title',
class extends HTMLElement {
connectedCallback() {
this.innerHTML = `
<style>
h1 {
font-size: 2.5rem;
color: hotpink;
font-family: monospace;
text-align: center;
text-decoration: pink solid underline;
text-decoration-skip: ink;
}
</style>
<h1>Hello Alligator!</h1>
`;
}
}
);
})();
您会注意到,所有内容都包含在 JavaScript 中,并且我们没有任何独立的 HTML 标记。这是因为,也许不幸的是,HTML 导入似乎已经失败了,而 Web 组件的未来发展方向将是使用ES6 字符串文字在 JavaScript 中定义标记和样式。
影子 DOM
上面的例子很好,但有一个主要问题:我们的样式不局限于我们的自定义元素。这意味着现在所有的h1我们页面上的标签将是带下划线的粉红色。为了使此组件的样式不影响其外部世界,一个临时解决方案是将我们的标记包装在类似 div 的东西中,然后使用仅针对我们的包装器的选择器应用样式:
this.innerHTML = `
<style>
.wrap-my-title h1 {
font-size: 2.5rem;
color: hotpink;
font-family: monospace;
text-align: center;
text-decoration: pink solid underline;
text-decoration-skip: ink;
}
</style>
<div class="wrap-my-tile">
<h1>Hello Alligator!</h1>
</div>
`;
这不是很好,甚至不是万无一失的,如果有另一个元素包装我的标题标记中的某个位置。此外,如果我们可以使用简单的 CSS 选择器,例如h1. 这就是影子 DOM进来。Shadow DOM 允许我们将样式范围限定在自定义元素上,这样它们就不会消失。
要使用 Shadow DOM,您需要附加一个影子根到元素,然后在影子根内定义元素的标记:
(function() {
class MyTitle extends HTMLElement {
constructor() {
super();
this.attachShadow({ mode: 'open' });
this.shadowRoot.innerHTML = `
<style>
h1 {
font-size: 2.5rem;
color: hotpink;
font-family: monospace;
text-align: center;
text-decoration: pink solid underline;
text-decoration-skip: ink;
}
</style>
<h1>Hello Alligator!</h1>
`;
}
}
window.customElements.define('my-title', MyTitle);
})();
以下是一些注意事项:
- 类构造函数是附加影子根并定义其内部 html 的好地方。
- 当自定义元素的类有构造函数时,你应该始终调用极好的()在里面。
- 影子根的模式可以是打开或者关闭。您可能只会使用open,因为否则您将无法为其设置任何 innerHTML。
使用 Shadow DOM,标记在浏览器控制台中的外观如下:
替代语法
您还可以通过创建模板元素,设置它的innerHTML,然后将模板的内容作为新子元素克隆到我们的影子根:
(function() {
const template = document.createElement('template');
template.innerHTML = `
<style>
h1 {
font-size: 2.5rem;
color: hotpink;
font-family: monospace;
text-align: center;
text-decoration: pink solid underline;
text-decoration-skip: ink;
}
</style>
<h1>Hello Alligator!</h1>
`;
class MyTitle extends HTMLElement {
constructor() {
super();
this.attachShadow({ mode: 'open' });
this.shadowRoot.appendChild(template.content.cloneNode(true));
}
}
window.customElements.define('my-title', MyTitle);
})();
用法
使用我们的自定义元素非常简单,只需将脚本文件添加到页面,然后像使用任何其他常规 HTML 元素一样使用我们的元素即可。但请注意,自定义元素应始终有一个结束标记:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<script src="./my-counter.js"></script>
</head>
<body>
<my-title></my-title>
</body>
</html>
请记住,我们的元素尚未完全准备好投入生产。目前,该元素只能在少数现代浏览器中使用。您需要通过 Babel 之类的转译器运行代码,以便实现尚未全面支持的 JavaScript 功能,例如 ES6 类或字符串文字,并且您需要使用 polyfill 来处理自定义元素和影子 DOM。我们将在另一篇文章中介绍如何使用 Web Components polyfill。