使用自定义标签和 Shadow DOM 增强 HTML

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 更加模块化和组织化。通过扩展现有元素或创建全新元素,我们可以将功能和展现分离,每个元素具有自己的状态和行为,使代码更易于维护和扩展。