Web 组件与 UI 元素开发指南
1. 反馈组件开发
当用户选择反馈选项时,可以监听自定义反馈事件以获得通知。以下是监听反馈事件的示例代码:
document.querySelector('feedback-rating').addEventListener('feedback', event => {
// Get the value of the feedback component's "helpful" property and send it to an
// endpoint with a POST request.
fetch('/api/analytics/feedback', {
method: 'POST',
body: JSON.stringify({ helpful: event.target.helpful }),
headers: {
'Content-Type': 'application/json'
}
});
});
反馈评分组件会显示一个提示和两个按钮,用户根据认为网站内容是否有用来点击其中一个按钮。点击事件监听器使用了事件委托,只需添加一个监听器来响应反馈提示内的任何点击。若点击的元素没有
data-helpful
属性,则用户未点击反馈按钮,不做处理;否则,将字符串值转换为布尔值并设置为自定义元素的属性,还会触发一个可在其他地方监听的事件。
为使事件能从阴影 DOM 传递到常规 DOM,必须设置
composed: true
选项,否则添加到自定义元素的事件监听器不会触发。事件触发后,可通过
event.target
属性检查反馈元素的
helpful
属性,以确定用户点击了哪个反馈按钮。
由于样式和标记包含在阴影 DOM 中,CSS 规则不会影响阴影 DOM 之外的任何元素。不过,传递给组件插槽的内容可由全局 CSS 规则设置样式,因为插槽内容留在标准或轻量级 DOM 中。
2. 个人资料卡片组件开发
若想创建一个可重复使用的卡片组件来展示用户资料,可使用 Web 组件中的插槽将内容传递到特定区域。
-
定义模板
:
const template = document.createElement('template');
template.innerHTML = `
<style>
:host {
display: grid;
border: 1px solid #ccc;
border-radius: 5px;
padding: 8px;
grid-template-columns: auto 1fr;
column-gap: 16px;
align-items: center;
margin: 1rem;
}
.photo {
border-radius: 50%;
grid-row: 1 / span 3;
}
.name {
font-size: 2rem;
font-weight: bold;
}
.title {
font-weight: bold;
}
</style>
<div class="photo"><slot name="photo"></slot></div>
<div class="name"><slot name="name"></slot></div>
<div class="title"><slot name="title"></slot></div>
<div class="bio"><slot></slot></div>
`;
此模板有三个命名插槽(
photo
、
name
和
title
)和一个用于传记的默认插槽。
-
组件实现
:
class ProfileCard extends HTMLElement {
constructor() {
super();
this.attachShadow({ mode: 'open' });
this.shadowRoot.appendChild(template.content.cloneNode(true));
}
}
customElements.define('profile-card', ProfileCard);
- 使用组件 :
<profile-card>
<img slot="photo" src="/api/portraits/chavez.jpg" />
<div slot="name">Phillip Chavez</div>
<div slot="title">CEO</div>
<p>Philip is a great CEO.</p>
</profile-card>
<profile-card>
<img slot="photo" src="/api/portraits/lynch.jpg" />
<div slot="name">Jamie Lynch</div>
<div slot="title">Vice President</div>
<p>Jamie is a great vice president.</p>
</profile-card>
在 CSS 样式中,
:host
选择器代表应用于自定义元素阴影宿主的样式,即阴影 DOM 所附着的元素。通过这个例子,可看到 Web 组件能创建可重复使用的内容和布局,插槽是插入内容的强大工具。
3. 懒加载图像组件开发
若需要一个包含图像的可重复使用组件,且该图像在滚动到视口时才加载,可使用
IntersectionObserver
等待元素滚动到视图中,然后设置包含图像的
src
属性。
-
组件实现
:
class LazyImage extends HTMLElement {
constructor() {
super();
const shadowRoot = this.attachShadow({ mode: 'open' });
this.image = document.createElement('img');
shadowRoot.appendChild(this.image);
}
connectedCallback() {
const observer = new IntersectionObserver(entries => {
if (entries[0].isIntersecting) {
console.log('Loading image');
this.image.src = this.getAttribute('src');
observer.disconnect();
}
});
observer.observe(this);
}
}
customElements.define('lazy-image', LazyImage);
- 使用组件 :
<lazy-image src="https://placekitten.com/200/138"></lazy-image>
元素滚动到视图中后,
IntersectionObserver
回调会获取
src
属性并设置为图像的
src
属性,触发图像加载。不过,较新的浏览器支持
img
标签的
loading="lazy"
属性,也能实现相同效果。
4. 展开/折叠组件开发
若想通过点击按钮显示或隐藏某些内容,可构建一个展开/折叠 Web 组件。该组件有两部分:切换内容的按钮和内容本身,每部分都有一个插槽,默认插槽用于内容,命名插槽用于按钮,还可通过更改
open
属性的值以编程方式展开或折叠组件。
-
定义模板
:
const template = document.createElement('template');
template.innerHTML = `
<div>
<button type="button" class="toggle-button">
<slot name="title"></slot>
</button>
<div class="content">
<slot></slot>
</div>
</div>
`;
- 组件实现 :
class Disclosure extends HTMLElement {
// Watch the 'open' attribute to react to changes.
static observedAttributes = ['open'];
constructor() {
super();
this.attachShadow({ mode: 'open' });
this.shadowRoot.appendChild(template.content.cloneNode(true));
this.content = this.shadowRoot.querySelector('.content');
}
connectedCallback() {
this.content.hidden = !this.hasAttribute('open');
this.shadowRoot.querySelector('.toggle-button')
.addEventListener('click', () => {
if (this.hasAttribute('open')) {
// Content is currently showing; remove the 'open'
// attribute and hide the content.
this.removeAttribute('open');
this.content.hidden = true;
} else {
// Content is currently hidden; add the 'open' attribute
// and show the content.
this.setAttribute('open', '');
this.content.hidden = false;
}
});
}
attributeChangedCallback(name, oldValue, newValue) {
// Update the content's hidden state based on the new attribute value.
if (newValue !== null) {
this.content.hidden = false;
} else {
this.content.hidden = true;
}
}
}
// The element name must be hyphenated.
customElements.define('x-disclosure', Disclosure);
为避免子内容闪烁,需添加一点 CSS:
x-disclosure:not(:defined) {
display: none;
}
- 使用组件 :
<x-disclosure>
<div slot="title">Details</div>
This is the detail child content that will be expanded or collapsed
when clicking the title button.
</x-disclosure>
展开/折叠组件使用
open
属性来确定是否显示子内容。点击切换按钮时,根据当前状态添加或删除该属性,然后有条件地应用
hidden
属性到子内容。也可通过添加或删除
open
属性以编程方式切换子内容。
5. 样式按钮组件开发
若想创建一个具有不同样式选项的可重复使用按钮组件,有三种按钮变体:默认变体(灰色背景)、“主要”变体(蓝色背景)和“危险”变体(红色背景)。
-
创建模板
:
const template = document.createElement('template');
template.innerHTML = `
<style>
button {
background: #333;
padding: 0.5em 1.25em;
font-size: 1rem;
border: none;
border-radius: 5px;
color: white;
}
button.primary {
background: #2563eb;
}
button.danger {
background: #dc2626;
}
</style>
<button>
<slot></slot>
</button>
`;
- 组件实现 :
class StyledButton extends HTMLElement {
static observedAttributes = ['variant', 'type'];
constructor() {
super();
this.attachShadow({ mode: 'open' });
this.shadowRoot.appendChild(template.content.cloneNode(true));
this.button = this.shadowRoot.querySelector('button');
}
attributeChangedCallback(name, oldValue, newValue) {
if (name === 'variant') {
this.button.className = newValue;
} else if (name === 'type') {
this.button.type = newValue;
}
}
}
customElements.define('styled-button', StyledButton);
- 使用组件 :
<styled-button id="default-button" type="button">Default</styled-button>
<styled-button id="primary-button" type="button" variant="primary">
Primary
</styled-button>
<styled-button id="danger-button" type="button" variant="danger">
Danger
</styled-button>
通过将按钮元素的类名设置为变体名称来应用样式,相应的 CSS 规则会应用所需的背景颜色。添加点击监听器时,可直接添加到
styled-button
元素,得益于事件委托,点击底层按钮时会触发该监听器。
以下是创建不同组件的流程总结:
graph LR
A[反馈组件] --> B[监听反馈事件]
B --> C[处理点击事件]
C --> D[传递事件到常规 DOM]
E[个人资料卡片组件] --> F[定义模板]
F --> G[实现组件]
G --> H[使用组件]
I[懒加载图像组件] --> J[使用 IntersectionObserver]
J --> K[设置图像 src]
L[展开/折叠组件] --> M[定义模板]
M --> N[实现组件]
N --> O[添加 CSS 避免闪烁]
O --> P[使用组件]
Q[样式按钮组件] --> R[创建模板]
R --> S[实现组件]
S --> T[使用组件]
6. 现代浏览器内置 UI 元素
现代浏览器有一些强大的内置 UI 元素,可用于应用程序。
| UI 元素 | 描述 |
| ---- | ---- |
| 对话框(Dialogs) | 弹出对话框是许多应用程序的主要组成部分,提供反馈并提示输入。现代浏览器的
<dialog>
元素自带背景覆盖页面其余部分,可通过 CSS 设置样式。需自行添加标题、按钮等内容,并处理按钮点击事件。关闭对话框可调用
close
方法,可传递可选参数作为“返回值”,通过
returnValue
属性检查。 |
| 详情(Details) |
<details>
元素的内容可折叠,有摘要内容显示在交互式元素中,点击可显示或隐藏详细内容,可通过 CSS 设置样式并使用 JavaScript 切换可见性。 |
| 弹出框(Popovers) | 类似于对话框的弹出元素。点击弹出框外部会关闭它;弹出框可见时仍可与页面其余部分交互;可将任何 HTML 元素转换为弹出框。 |
| 通知(Notifications) | 现代浏览器有用于显示原生操作系统通知的 API,由 JavaScript 触发。用户必须授予权限才能发送通知,这些通知在应用程序运行时按需在 JavaScript 代码中创建。 |
7. 创建警告对话框
若想显示一个带有简单消息和“确定”按钮的对话框,可使用
<dialog>
元素和“确定”按钮。
-
定义 HTML
:
<dialog id="alert">
<h2>Alert</h2>
<p>This is an alert dialog.</p>
<button type="button" id="ok-button">OK</button>
</dialog>
<button type="button" id="show-dialog">Show Dialog</button>
- 添加 JavaScript :
// Select the dialog, its OK button, and the trigger button elements.
const dialog = document.querySelector('#alert');
const okButton = document.querySelector('#ok-button');
const trigger = document.querySelector('#show-dialog');
// Close the dialog when the OK button is clicked.
okButton.addEventListener('click', () => {
dialog.close();
});
// Show the dialog when the trigger button is clicked.
trigger.addEventListener('click', () => {
dialog.showModal();
});
showModal
方法显示模态对话框,会阻止页面其余部分,焦点“被困”在对话框内;也可调用
show
方法显示无模态对话框,可在对话框打开时与页面其余部分交互。点击“确定”按钮或按
Escape
键可关闭对话框,可监听
cancel
事件捕获按
Escape
键的操作,关闭对话框会触发
close
事件。
对话框元素还有一些键盘可访问性功能,打开对话框时,第一个可聚焦元素自动获得焦点,可通过添加
autofocus
属性更改此行为。关闭对话框时,键盘焦点会返回触发按钮。可使用 CSS 设置对话框及其背景的样式,如使用
::backdrop
伪元素设置背景颜色。
8. 创建确认对话框
若想提示用户确认操作,提示应显示问题并带有“确认”和“取消”按钮。具体实现需根据需求进一步处理按钮点击事件和逻辑。
通过以上介绍,我们了解了多种 Web 组件和现代浏览器内置 UI 元素的开发和使用方法,这些技术能帮助我们创建更丰富、交互性更强的 Web 应用程序。
Web 组件与 UI 元素开发指南
9. 不同组件开发的对比分析
为了更清晰地了解各个组件的特点和适用场景,我们对前面介绍的组件进行对比分析。
| 组件类型 | 主要功能 | 关键技术 | 适用场景 |
| ---- | ---- | ---- | ---- |
| 反馈组件 | 监听用户反馈选项,可将反馈数据发送到分析端点 | 自定义反馈事件、Fetch API | 需要收集用户对内容反馈的页面,如文章、产品介绍页等 |
| 个人资料卡片组件 | 展示用户资料,可重复使用 | Web 组件插槽 | 社交平台、员工介绍页面等需要展示用户信息的地方 |
| 懒加载图像组件 | 图像在滚动到视口时才加载,节省资源 | IntersectionObserver | 图片较多的页面,如相册、电商商品列表页 |
| 展开/折叠组件 | 通过点击按钮显示或隐藏内容 | 自定义元素属性、事件监听 | 表单中的高级选项、文章的详细内容等 |
| 样式按钮组件 | 创建具有不同样式的可重复使用按钮 | 自定义元素属性、CSS 类名 | 各种需要按钮交互的页面,如登录页、操作提示页等 |
10. 组件开发的注意事项
在进行组件开发时,有一些注意事项需要我们关注:
-
阴影 DOM 的使用
:阴影 DOM 可以封装组件的样式和结构,避免对外部元素产生影响。但在传递事件和样式时,需要注意设置相关选项,如
composed: true
让事件跨越阴影 DOM。
-
兼容性问题
:不同浏览器对 Web 组件和内置 UI 元素的支持程度不同。在使用新特性时,要查看兼容性数据,如使用
<dialog>
元素时可参考 CanIUse 网站。
-
性能优化
:对于懒加载图像组件等涉及资源加载的组件,要合理使用技术,避免不必要的资源浪费。
11. 组件开发的扩展思路
在实际应用中,我们可以对这些组件进行扩展和组合,以满足更多的需求。以下是一些扩展思路:
-
组合组件
:将个人资料卡片组件和反馈组件组合,在展示用户资料的同时收集用户对该资料的反馈。
<profile-card>
<img slot="photo" src="/api/portraits/chavez.jpg" />
<div slot="name">Phillip Chavez</div>
<div slot="title">CEO</div>
<p>Philip is a great CEO.</p>
<feedback-rating></feedback-rating>
</profile-card>
- 扩展功能 :为展开/折叠组件添加动画效果,让内容的显示和隐藏更加平滑。可以使用 CSS 动画或 JavaScript 动画库来实现。
.content {
transition: height 0.3s ease-in-out;
overflow: hidden;
}
12. 内置 UI 元素的使用技巧
对于现代浏览器的内置 UI 元素,有一些使用技巧可以提升用户体验:
-
对话框的返回值处理
:在使用对话框时,合理利用
close
方法的可选参数和
returnValue
属性,将用户的选择传递回页面。
const dialog = document.querySelector('#confirmation');
const confirmButton = document.querySelector('#confirm');
const cancelButton = document.querySelector('#cancel');
confirmButton.addEventListener('click', () => {
dialog.close('confirmed');
});
cancelButton.addEventListener('click', () => {
dialog.close('cancelled');
});
// 在打开对话框的地方检查返回值
dialog.addEventListener('close', () => {
if (dialog.returnValue === 'confirmed') {
// 执行确认操作
} else if (dialog.returnValue === 'cancelled') {
// 执行取消操作
}
});
-
详情元素的样式定制
:通过 CSS 对
<details>元素进行样式定制,让其更加美观和符合页面风格。
details {
border: 1px solid #ccc;
border-radius: 5px;
padding: 10px;
}
details summary {
cursor: pointer;
font-weight: bold;
}
13. 总结与展望
通过本文的介绍,我们详细了解了多种 Web 组件和现代浏览器内置 UI 元素的开发和使用方法。这些技术为我们创建丰富、交互性强的 Web 应用程序提供了有力支持。
在未来的 Web 开发中,Web 组件和内置 UI 元素的应用将会更加广泛。随着浏览器技术的不断发展,我们可以期待更多强大的功能和更便捷的开发方式。同时,我们也可以进一步探索组件之间的组合和扩展,创造出更加独特和实用的用户界面。
以下是一个总结不同组件开发步骤的 mermaid 流程图:
graph LR
A[组件开发] --> B[选择组件类型]
B --> C{反馈组件}
B --> D{个人资料卡片组件}
B --> E{懒加载图像组件}
B --> F{展开/折叠组件}
B --> G{样式按钮组件}
C --> H[定义反馈事件监听]
C --> I[处理反馈数据]
D --> J[定义模板]
D --> K[实现组件逻辑]
E --> L[使用 IntersectionObserver]
E --> M[设置图像加载]
F --> N[定义模板和属性]
F --> O[处理展开/折叠逻辑]
G --> P[创建模板和样式]
G --> Q[处理按钮属性]
H --> R[完成开发]
I --> R
J --> R
K --> R
L --> R
M --> R
N --> R
O --> R
P --> R
Q --> R
通过以上的总结和流程图,我们可以更清晰地看到组件开发的整体流程和不同组件的关键步骤。希望这些内容能帮助你在 Web 开发中更加熟练地运用这些技术。
超级会员免费看
1412

被折叠的 条评论
为什么被折叠?



