Custom Elements 是一项 Web Component 技术,可以让开发者自定义 HTML 元素并在页面中使用。
在本文中,我们将介绍如何使用 Custom Elements 实现一个步骤条组件,并讨论一些遇到的坑点。
实现步骤
1. 定义组件
我们首先定义一个 step-bar
组件,其中包含若干个 step-item
。每个 step-item
表示一个步骤,包含一个标题和一个状态,状态可以是 active
、done
或 undone
之一。
// javascriptcn.com 代码示例 <!-- step-bar.html --> <template> <style> /* 样式省略 */ </style> <div class="step-bar"> <slot></slot> </div> </template> <script> class StepBar extends HTMLElement { connectedCallback() { // 初始化步骤条 } } class StepItem extends HTMLElement { connectedCallback() { // 初始化步骤 } get title() { return this.getAttribute('title'); } set title(value) { this.setAttribute('title', value); } get status() { return this.getAttribute('status'); } set status(value) { this.setAttribute('status', value); } } window.customElements.define('step-bar', StepBar); window.customElements.define('step-item', StepItem); </script>
2. 编写样式
为了美化步骤条组件,我们需要编写一些样式。这里我们采用了 Flex 布局和 CSS 伪元素等技巧。
// javascriptcn.com 代码示例 /* step-bar.css */ .step-bar { display: flex; justify-content: space-between; align-items: center; width: 100%; } .step-item { position: relative; flex: 1; margin-right: 10px; } .step-item::before { position: absolute; top: 50%; left: -5px; content: ''; width: 10px; height: 10px; border-radius: 50%; background-color: #ccc; z-index: -1; } .step-item:first-child::before { display: none; } .step-item.active::before, .step-item.done::before { background-color: #009688; } .step-item.active ~ .step-item::before, .step-item.done ~ .step-item::before { background-color: #ccc; } .step-item.active::after, .step-item.done::after { position: absolute; top: 50%; left: -3px; content: ''; width: 6px; height: 6px; border-radius: 50%; background-color: #fff; transform: translateY(-50%); z-index: 1; } .step-item.undone ~ .step-item::after { display: none; } .step-item.active ~ .step-item::after { background-color: #ccc; }
3. 初始化组件
我们需要在 connectedCallback
方法中初始化组件。对于 step-bar
组件,我们需要遍历所有子元素并添加 step-item
类名,以便它们受到样式控制。
对于 step-item
组件,我们需要根据属性值设置状态和标题。
// javascriptcn.com 代码示例 // step-bar.js class StepBar extends HTMLElement { connectedCallback() { const children = Array.from(this.children); children.forEach((child, index) => { if (child instanceof StepItem) { child.index = index; child.classList.add('step-item'); } }); } } class StepItem extends HTMLElement { connectedCallback() { const title = this.title; const status = this.status; this.titleEl = document.createElement('div'); this.titleEl.classList.add('step-title'); this.titleEl.innerText = title; this.appendChild(this.titleEl); this.updateStatus(status); } updateStatus(status) { this.classList.remove('active', 'done', 'undone'); this.classList.add(status); if (status === 'active') { this.titleEl.innerText = `Step ${this.index + 1}: ${this.title}`; } } static get observedAttributes() { return ['title', 'status']; } attributeChangedCallback(name, oldValue, newValue) { if (oldValue !== newValue) { this.updateStatus(newValue); } } get title() { return this.getAttribute('title') || ''; } set title(value) { this.setAttribute('title', value); } get status() { return this.getAttribute('status') || 'undone'; } set status(value) { this.setAttribute('status', value); } get index() { return parseInt(this.getAttribute('index'), 10) || 0; } set index(value) { this.setAttribute('index', value); } } window.customElements.define('step-bar', StepBar); window.customElements.define('step-item', StepItem);
4. 使用组件
现在我们可以在 HTML 中使用 step-bar
和 step-item
元素了。
// javascriptcn.com 代码示例 <!-- index.html --> <!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title>Step Bar Demo</title> <link rel="stylesheet" href="step-bar.css"> <script src="step-bar.js"></script> </head> <body> <step-bar> <step-item title="Step 1" status="active"></step-item> <step-item title="Step 2" status="undone"></step-item> <step-item title="Step 3" status="undone"></step-item> </step-bar> </body> </html>
遇到的坑点
在实现步骤条组件时,我们遇到了一些坑点。下面是一些解决方案及注意事项。
1. HTML 内容被移除
在使用组件时,注意不要通过 JS 动态修改组件内部的 HTML 内容,因为这会使浏览器将原来的 HTML 移除并重新插入新的 HTML,导致组件失效。
2. 动态属性修改不会自动触发 attributeChangedCallback
在组件中,我们可以通过 setAttribute
或 property
来修改自定义属性的值。但是需要注意的是,当我们通过 property
修改属性值时,不会自动触发 attributeChangedCallback
方法,我们需要额外调用 setAttribute
来触发。
set status(value) { this.setAttribute('status', value); } setIndex(index) { this.index = index; this.setAttribute('index', index); }
3. 内部元素绑定事件无法传递到外部
当我们将一个 click
事件绑定到组件的内部元素时,点击该元素时,该事件不会传递到组件本身或其父级元素,导致无法触发组件的相关回调。解决方法是使用事件委托,将事件绑定到组件本身或其父级元素。
// javascriptcn.com 代码示例 class StepBar extends HTMLElement { connectedCallback() { this.addEventListener('click', e => { const item = e.target.closest('.step-item'); if (item) { // 处理 StepItem 点击事件 } }, false); } }
总结
在本文中,我们使用 Custom Elements 实现了一个步骤条组件,并讨论了一些遇到的坑点。本文的内容可以帮助读者了解 Custom Elements 技术,并指导读者如何开发自定义组件。完整的示例代码可以在我的 GitHub 仓库中找到。
来源:JavaScript中文网 ,转载请注明来源 本文地址:https://www.javascriptcn.com/post/652bc9fe7d4982a6ebda662b