1. 什么是自定义标签?
HTML5引入了一个新特性 - 自定义标签,也被称为 web 组件 或 自定义元素。它允许开发者创建自己的标签,并通过JavaScript呈现出不同的行为和样式。这些自定义标签可以被其他开发者使用和共享,提高代码的可重用性和组织性。
<my-button>Click me!</my-button>
2. 如何创建自定义标签?
2.1 定义自定义元素
使用JavaScript创建自定义元素,需要继承HTMLElement
类并定义自己的标签名
class MyButton extends HTMLElement {
// custom element implementation
}
window.customElements.define('my-button', MyButton);
2.2 用户界面:Shadow DOM
为了控制样式和布局,自定义元素可以使用 Shadow DOM。 Shadow DOM提供了一个与文档DOM分离的DOM子树,使开发人员可以将HTML、CSS和JS隐藏在自定义元素内部,避免与外部DOM混淆。
<my-button>
#shadow-root
<button>Click me!</button>
</my-button>
3. CSS样式化 Shadow DOM
使用CSS样式化Shadow DOM,需要使用::shadow
伪类,表示样式将被应用于影子根元素。然后,样式可以被嵌套在Shadow DOM的内部样式表中。
my-button {
display: inline-block;
padding: 12px 24px;
font-size: 24px;
color: white;
background-color: blue;
}
my-button::shadow button {
border: none;
background: none;
color: inherit;
}
4. JavaScript 行为
通过添加事件处理程序和自定义方法,自定义元素可以添加行为和交互性。在connectedCallback()
方法中添加事件监听器,以响应符合自定义元素的所有实例。
class MyButton extends HTMLElement {
connectedCallback() {
this.addEventListener('click', () => {
this.doSomething();
});
}
doSomething() {
alert('clicked!');
}
}
window.customElements.define('my-button', MyButton);
5. 扩展其他元素
除了创建全新的自定义元素外,还可以扩展现有元素并添加新的行为和样式。
class FancyButton extends HTMLButtonElement {
connectedCallback() {
this.classList.add('fancy');
}
}
window.customElements.define('fancy-button', FancyButton, { extends: 'button' });
如上代码中,使用了选项“extends: 'button'
”扩展<button>
元素,而不是创建全新的元素。
6. 自定义标签的优点
自定义标签提供了一种将功能、样式和模板打包在一起的方法。它们还可以通过组合其他自定义元素来创建更高层次的元素,这将使功能封装,并减少对其他JavaScript库和框架的依赖。
7. 综合应用
以下是一个使用自定义标签和 Shadow DOM 的示例应用程序,它基于以上提到的知识,实现了一个简单的评分组件。
class StarRating extends HTMLElement {
constructor() {
super();
const shadow = this.attachShadow({ mode: 'open' });
const wrapper = document.createElement('div');
wrapper.setAttribute('class', 'wrapper');
for (let i = 1; i <= 5; i++) {
const star = document.createElement('span');
star.setAttribute('class', 'star');
star.setAttribute('data-index', String(i));
star.addEventListener('mouseover', () => {
this.hoveredStar(star);
});
star.addEventListener('click', () => {
this.selectStar(star);
});
wrapper.appendChild(star);
}
shadow.appendChild(wrapper);
const style = document.createElement('style');
style.textContent = `
.wrapper {
font-size: 2rem;
display: inline-block;
color: gold;
}
.star {
cursor: pointer;
font-size: 1.5em;
display: inline-block;
transition: color 100ms ease-in-out;
}
.star:hover,
.star.selected {
color: orange;
}
`;
shadow.appendChild(style);
}
connectedCallback() {
if (!this.hasAttribute('value')) {
this.setAttribute('value', 0);
}
const value = Number(this.getAttribute('value'));
this.updateStars(value);
}
hoveredStar(star) {
const index = Number(star.getAttribute('data-index'));
this.updateStars(index);
}
selectStar(star) {
const index = Number(star.getAttribute('data-index'));
this.setAttribute('value', String(index));
this.updateStars(index);
}
updateStars(value) {
const stars = this.shadowRoot.querySelectorAll('.star');
stars.forEach((star, index) => {
if (index < value) {
star.setAttribute('class', 'star selected');
} else {
star.setAttribute('class', 'star');
}
});
}
}
customElements.define('star-rating', StarRating);
使用以下HTML标记使用<star-rating>
组件。
<star-rating value="3"></star-rating>
上述代码段将生成一个包含 5 个星形符号,其中前 3 个被填充的评级组件,稍后可使用 JavaScript 进行更新。以下是效果图:
8. 总结
自定义标签和 Shadow DOM 可以使 HTML 更加模块化和组织化。通过扩展现有元素或创建全新元素,我们可以将功能和展现分离,每个元素具有自己的状态和行为,使代码更易于维护和扩展。